Add deck POST/PUT
This commit is contained in:
parent
3f6e84317a
commit
a317dd31a8
3 changed files with 100 additions and 7 deletions
|
@ -361,11 +361,23 @@ async def store_deck_card(
|
|||
"""
|
||||
INSERT INTO "deck_list" ("deck_id", "oracle_id", "quantity")
|
||||
VALUES (%(deck_id)s, %(oracle_id)s, %(quantity)s)
|
||||
ON CONFLICT ("deck_id", "oracle_id") DO UPDATE
|
||||
SET "quantity" = "deck_list"."quantity" + EXCLUDED."quantity"
|
||||
""",
|
||||
{"deck_id": deck_id, "oracle_id": str(oracle_id), "quantity": quantity},
|
||||
)
|
||||
|
||||
|
||||
async def clear_deck(db: psycopg.AsyncCursor, deck_id: int) -> None:
|
||||
await db.execute(
|
||||
"""
|
||||
DELETE FROM "deck_list"
|
||||
WHERE "deck_id" = %(deck_id)s
|
||||
""",
|
||||
{"deck_id": deck_id},
|
||||
)
|
||||
|
||||
|
||||
async def get_decks(
|
||||
db: psycopg.AsyncCursor, limit: int = 10, offset: int = 0
|
||||
) -> typing.List[tutor.models.Deck]:
|
||||
|
@ -479,8 +491,8 @@ async def get_deck(
|
|||
'quantity', "deck_list"."quantity"
|
||||
))) AS "cards"
|
||||
FROM "decks"
|
||||
LEFT JOIN "deck_list" USING ("deck_id")
|
||||
LEFT JOIN "oracle_latest" USING ("oracle_id")
|
||||
JOIN "deck_list" USING ("deck_id")
|
||||
JOIN "oracle_latest" USING ("oracle_id")
|
||||
WHERE "decks"."deck_id" = %(deck_id)s
|
||||
GROUP BY "decks"."deck_id"
|
||||
, "decks"."name"
|
||||
|
|
|
@ -119,6 +119,7 @@ class CardConstraint:
|
|||
language: typing.Optional[str] = None
|
||||
foil: typing.Optional[str] = None
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class DeckCard:
|
||||
card: Card
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import asyncio
|
||||
import decimal
|
||||
import importlib.metadata
|
||||
import importlib.resources
|
||||
import io
|
||||
import itertools
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import typing
|
||||
import urllib.parse
|
||||
|
@ -13,12 +17,16 @@ import tornado.ioloop
|
|||
import tornado.web
|
||||
import tornado_openapi3.handler
|
||||
import yaml
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
import tutor.database
|
||||
import tutor.models
|
||||
import tutor.search
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def update_args(url: str, **qargs) -> str:
|
||||
parts = urllib.parse.urlsplit(url)
|
||||
return urllib.parse.urlunsplit(
|
||||
|
@ -212,7 +220,33 @@ class CollectionHandler(RequestHandler):
|
|||
)
|
||||
|
||||
|
||||
class DeckImporter:
|
||||
line_pattern = re.compile(r"^(?P<quantity>\d+)x? (?P<name>[^\]$]+)")
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.imported: int = 0
|
||||
self.last_chunk: bytes = b""
|
||||
|
||||
def process(
|
||||
self, chunk: bytes = b""
|
||||
) -> typing.Iterator[tutor.models.CardConstraint]:
|
||||
lines = (self.last_chunk + chunk).splitlines()
|
||||
self.last_chunk = lines.pop()
|
||||
for line in lines:
|
||||
if match := self.line_pattern.match(line.decode("utf8").strip()):
|
||||
for card in itertools.repeat(
|
||||
tutor.models.CardConstraint(name=match.group("name")),
|
||||
int(match.group("quantity")),
|
||||
):
|
||||
self.imported += 1
|
||||
yield card
|
||||
|
||||
|
||||
class DecksHandler(RequestHandler):
|
||||
def initialize(self) -> None:
|
||||
self.importer = DeckImporter()
|
||||
self.deck_id = None
|
||||
|
||||
async def get(self) -> None:
|
||||
page = max(1, int(self.get_argument("page", "1")))
|
||||
limit = int(self.get_argument("limit", "10"))
|
||||
|
@ -233,22 +267,68 @@ class DecksHandler(RequestHandler):
|
|||
self.write(json.dumps(decks, cls=JSONEncoder))
|
||||
|
||||
async def post(self) -> None:
|
||||
...
|
||||
self.set_header("Content-Type", "application/json")
|
||||
self.set_header("Access-Control-Allow-Origin", "*")
|
||||
self.write(json.dumps({}))
|
||||
|
||||
|
||||
@tornado.web.stream_request_body
|
||||
class DeckHandler(RequestHandler):
|
||||
async def prepare(self) -> None:
|
||||
self.connection = await self.pool.getconn()
|
||||
self.deck_id = self.path_args[0]
|
||||
content_type = self.request.headers.get("Content-Type", "text/plain")
|
||||
if content_type == "text/plain":
|
||||
self.importer = DeckImporter()
|
||||
if self.request.method == "PUT":
|
||||
async with self.connection.cursor() as cursor:
|
||||
await tutor.database.clear_deck(cursor, self.deck_id)
|
||||
|
||||
async def data_received(self, chunk) -> None:
|
||||
for card in self.importer.process(chunk):
|
||||
await self.add_card(card)
|
||||
|
||||
async def add_card(self, card: tutor.models.CardConstraint) -> None:
|
||||
async with self.connection.cursor() as cursor:
|
||||
oracle_id = await tutor.database.oracle_id_by_name(cursor, card.name)
|
||||
if oracle_id:
|
||||
logger.debug("Adding card %s to deck %s", card.name, self.deck_id)
|
||||
await tutor.database.store_deck_card(cursor, self.deck_id, oracle_id, 1)
|
||||
|
||||
def on_finish(self) -> None:
|
||||
IOLoop.current().add_callback(self.close_connection)
|
||||
|
||||
async def close_connection(self) -> None:
|
||||
await self.pool.putconn(self.connection)
|
||||
|
||||
async def get(self, deck_id) -> 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:
|
||||
deck = await tutor.database.get_deck(cursor, deck_id)
|
||||
async with self.connection.cursor() as cursor:
|
||||
deck = await tutor.database.get_deck(cursor, deck_id)
|
||||
if not deck:
|
||||
raise tornado.web.HTTPError(404)
|
||||
self.write(json.dumps(deck, cls=JSONEncoder))
|
||||
|
||||
async def post(self, deck_id) -> None:
|
||||
for card in self.importer.process():
|
||||
await self.add_card(card)
|
||||
async with self.connection.cursor() as cursor:
|
||||
deck = await tutor.database.get_deck(cursor, deck_id)
|
||||
await self.connection.commit()
|
||||
self.set_header("Content-Type", "application/json")
|
||||
self.set_header("Access-Control-Allow-Origin", "*")
|
||||
self.write(json.dumps(deck, cls=JSONEncoder))
|
||||
|
||||
async def put(self, deck_id) -> None:
|
||||
...
|
||||
for card in self.importer.process():
|
||||
await self.add_card(card)
|
||||
async with self.connection.cursor() as cursor:
|
||||
deck = await tutor.database.get_deck(cursor, deck_id)
|
||||
await self.connection.commit()
|
||||
self.set_header("Content-Type", "application/json")
|
||||
self.set_header("Access-Control-Allow-Origin", "*")
|
||||
self.write(json.dumps(deck, cls=JSONEncoder))
|
||||
|
||||
|
||||
class TemplateHandler(RequestHandler):
|
||||
|
|
Loading…
Reference in a new issue