Compare commits

...

2 commits

Author SHA1 Message Date
7b78473f24 Add missing deck list OpenAPI spec 2023-08-12 15:54:19 -04:00
7f1735f200 Add endpoint for deck candidates 2023-08-12 15:54:03 -04:00
3 changed files with 92 additions and 0 deletions

View file

@ -594,3 +594,57 @@ async def collection_stats(db: psycopg.AsyncCursor) -> typing.Optional[dict]:
"""
)
return await db.fetchone()
async def deck_candidates(
db: psycopg.AsyncCursor, deck_id: int
) -> typing.List[tutor.models.CardCopy]:
db.row_factory = psycopg.rows.dict_row
await db.execute(
"""
SELECT cards.*, copies.*
FROM deck_list
JOIN cards USING(oracle_id)
JOIN copies USING (scryfall_id)
JOIN sets USING(set_code)
WHERE deck_id = %(deck_id)s AND type_line not like 'Basic Land%%'
ORDER BY CASE WHEN rarity >= 'rare'::rarity THEN 0 ELSE 1 END -- Is it in a binder?
, CASE WHEN LENGTH(color_identity) > 1 THEN 0 ELSE 1 END -- Sort gold cards before mono-colored
, color_identity
, CASE WHEN rarity > 'rare'::rarity THEN 0 ELSE 1 END -- Mythics are separated from rares in binders
, release_date ASC, set_code -- Older cards are better organized in boxes
, cards.name
""",
{"deck_id": deck_id},
)
rows = await db.fetchall()
return [
tutor.models.CardCopy(
card=tutor.models.Card(
scryfall_id=row["scryfall_id"],
oracle_id=row["oracle_id"],
name=row["name"],
set_code=row["set_code"],
set_name=row["set_name"],
collector_number=row["collector_number"],
rarity=tutor.models.Rarity.from_string(row["rarity"]),
color_identity=tutor.models.Color.from_string(row["color_identity"]),
cmc=row["cmc"],
mana_cost=row["mana_cost"],
type_line=row["type_line"],
release_date=datetime.date.fromisoformat(row["release_date"]),
games=set(),
legalities={},
edhrec_rank=row["edhrec_rank"],
oracle_text=row["oracle_text"],
price_usd=convert_price(row["price_usd"]),
price_usd_foil=convert_price(row["price_usd_foil"]),
price_eur=convert_price(row["price_eur"]),
price_eur_foil=convert_price(row["price_eur_foil"]),
price_tix=convert_price(row["price_tix"]),
),
foil=row["isFoil"] if row["isFoil"] is not None else False,
collection=row["collection"] or "Default",
)
for row in rows
]

View file

@ -371,6 +371,16 @@ class DeckHandler(RequestHandler):
self.set_status(204)
class DeckCandidatesHandler(OpenAPIRequestHandler, RequestHandler):
async def get(self, deck_id: int) -> None:
self.set_header("Content-Type", "application/json")
self.set_header("Access-Control-Allow-Origin", "*")
async with self.pool.connection() as conn:
async with conn.cursor() as cursor:
candidates = await tutor.database.deck_candidates(cursor, deck_id)
self.write(json.dumps(candidates, cls=JSONEncoder))
class TemplateHandler(RequestHandler):
def initialize(
self,
@ -429,6 +439,7 @@ class Application(tornado.web.Application):
(r"/api/collection", CollectionHandler),
(r"/api/decks", DecksHandler),
(r"/api/decks/([1-9][0-9]*)", DeckHandler),
(r"/api/decks/([1-9][0-9]*)/candidates", DeckCandidatesHandler),
]
if static_path := settings.get("static"):
paths.extend(

View file

@ -229,6 +229,19 @@ paths:
schema:
$ref: '#/components/schemas/collection_statistics'
/api/decks:
get:
summary: List decks
tags:
- Decks
responses:
'200':
description: Deck list
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/deck'
post:
summary: Create deck
tags:
@ -285,6 +298,20 @@ paths:
responses:
'204':
description: Deck deleted
/api/decks/{deck_id}/candidates:
get:
summary: Find cards in collection
tags:
- Decks
responses:
'200':
description: Cards
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/copy'
components:
schemas:
uuid: &uuid