Add deck list tracking

This commit is contained in:
Correl Roush 2022-02-10 13:15:42 -05:00
parent 0f0abcd66e
commit f326dd3ecd
5 changed files with 115 additions and 32 deletions

View file

@ -1,5 +1,25 @@
CREATE TABLE IF NOT EXISTS `sets` (
`set_code` TEXT PRIMARY KEY,
`name` TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS `rarities` (
`rarity` TEXT PRIMARY KEY,
`rarity_ord` INTEGER NOT NULL
);
DELETE FROM `rarities`;
INSERT INTO `rarities` (`rarity`, `rarity_ord`) VALUES
('common', 1),
('uncommon', 2),
('rare', 3),
('special', 4),
('mythic', 5),
('bonus', 6);
CREATE TABLE IF NOT EXISTS `cards` (
`scryfall_id` TEXT PRIMARY KEY,
`oracle_id` TEXT NOT NULL,
`name` TEXT NOT NULL,
`set_code` TEXT NOT NULL,
`collector_number` TEXT NOT NULL,
@ -12,12 +32,16 @@ CREATE TABLE IF NOT EXISTS `cards` (
`nonfoil` INTEGER NOT NULL DEFAULT 1,
`variation` INTEGER NOT NULL DEFAULT 0,
`edhrec_rank` INTEGER,
`oracle_text` TEXT
`oracle_text` TEXT,
FOREIGN KEY (`set_code`) REFERENCES `sets` (`set_code`),
FOREIGN KEY (`rarity`) REFERENCES `rarities` (`rarity`)
);
CREATE INDEX `cards_name` ON `cards`(`name` COLLATE nocase);
CREATE INDEX `cards_rarity` ON `cards`(`rarity`);
CREATE INDEX `cards_color_identity` ON `cards`(`color_identity`);
CREATE INDEX IF NOT EXISTS `cards_name` ON `cards`(`name` COLLATE nocase);
CREATE INDEX IF NOT EXISTS `cards_rarity` ON `cards`(`rarity`);
CREATE INDEX IF NOT EXISTS `cards_color_identity` ON `cards`(`color_identity`);
CREATE INDEX IF NOT EXISTS `cards_oracle_id` ON `cards` (`oracle_id`);
CREATE INDEX IF NOT EXISTS `cards_name` ON `cards` (`name`);
CREATE TABLE IF NOT EXISTS `copies` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
@ -41,25 +65,6 @@ CREATE TABLE IF NOT EXISTS `card_prices` (
FOREIGN KEY (`scryfall_id`) REFERENCES `cards`(`scryfall_id`)
);
CREATE TABLE IF NOT EXISTS `sets` (
`set_code` TEXT PRIMARY KEY,
`name` TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS `rarities` (
`rarity` TEXT PRIMARY KEY,
`rarity_ord` INTEGER NOT NULL
);
DELETE FROM `rarities`;
INSERT INTO `rarities` (`rarity`, `rarity_ord`) VALUES
('common', 1),
('uncommon', 2),
('rare', 3),
('special', 4),
('mythic', 5),
('bonus', 6);
CREATE TABLE IF NOT EXISTS `legalities` (
`scryfall_id` TEXT NOT NULL,
`format` TEXT NOT NULL,
@ -75,6 +80,19 @@ CREATE TABLE IF NOT EXISTS `games` (
FOREIGN KEY (`scryfall_id`) REFERENCES `cards`(`scryfall_id`)
);
CREATE TABLE IF NOT EXISTS `decks` (
`deck_id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS `deck_cards` (
`deck_id` INTEGER NOT NULL,
`oracle_id` TEXT NOT NULL,
`quantity` INTEGER NOT NULL DEFAULT 1,
PRIMARY KEY (`deck_id`, `oracle_id`),
FOREIGN KEY (`deck_id`) REFERENCES `decks`(`deck_id`)
);
CREATE TABLE IF NOT EXISTS `vars` (
`key` TEXT PRIMARY KEY,
`value` TEXT

View file

@ -1,6 +1,8 @@
import datetime
import json
import logging
import os.path
import re
import aiosqlite
import click
@ -12,6 +14,7 @@ import tornado.web
import tutor.csvimport
import tutor.database
import tutor.scryfall
import tutor.search
import tutor.server
@ -63,7 +66,12 @@ def server(ctx, port, scheme, static, debug):
tornado.ioloop.IOLoop.current().start()
@main.command("import")
@main.group("import")
def import_group():
...
@import_group.command("cards")
@click.argument("filename", type=click.Path(dir_okay=False))
@click.pass_context
def import_cards(ctx, filename):
@ -72,6 +80,42 @@ def import_cards(ctx, filename):
)
@import_group.command("deck")
@click.argument("filename", type=click.Path(dir_okay=False))
@click.option("--name")
@click.pass_context
def import_deck(ctx, filename, name):
line_pattern = re.compile(r"^(?P<quantity>\d+)x? (?P<name>[^\]$]+)")
with open(filename) as f:
lines = f.readlines()
name = name or os.path.splitext(os.path.basename(filename))[0]
async def import_deck():
async with aiosqlite.connect(ctx.obj["database"]) as db:
deck_id = await tutor.database.store_deck(db, name)
with click.progressbar(
lines, label=f"Importing deck '{name}' [{deck_id}]"
) as bar:
for line in bar:
if match := line_pattern.match(line.strip()):
groups = match.groupdict()
cards = await tutor.database.advanced_search(
db,
tutor.search.Search(
[tutor.search.Name(text=groups["name"])]
),
limit=1,
)
if cards:
await tutor.database.store_deck_card(
db, deck_id, cards[0].oracle_id, int(groups["quantity"])
)
await db.commit()
tornado.ioloop.IOLoop.current().run_sync(import_deck)
@main.command("update_scryfall")
@click.option("--filename", type=click.Path(dir_okay=False))
@click.pass_context
@ -107,9 +151,7 @@ def update_scryfall(ctx, filename):
) as bar:
for card_object in bar:
card = tutor.scryfall.to_card(card_object)
await tutor.database.store_card(
db, card
)
await tutor.database.store_card(db, card)
await tutor.database.store_price(db, today, card)
await tutor.database.store_set(
db, card_object["set"].upper(), card_object["set_name"]

View file

@ -72,6 +72,7 @@ async def search(
return [
tutor.models.Card(
scryfall_id=uuid.UUID(row["scryfall_id"]),
oracle_id=uuid.UUID(row["oracle_id"]),
name=row["name"],
set_code=row["set_code"],
collector_number=row["collector_number"],
@ -201,6 +202,7 @@ async def advanced_search(
return [
tutor.models.Card(
scryfall_id=uuid.UUID(row["scryfall_id"]),
oracle_id=uuid.UUID(row["oracle_id"]),
name=row["name"],
set_code=row["set_code"],
collector_number=row["collector_number"],
@ -226,11 +228,11 @@ async def advanced_search(
async def store_card(db: aiosqlite.Connection, card: tutor.models.Card) -> None:
await db.execute(
"INSERT INTO cards "
"(`scryfall_id`, `name`, `set_code`, `collector_number`, `rarity`,"
" `color_identity`, `cmc`, `type_line`, `release_date`, `edhrec_rank`,"
" `oracle_text`) "
"VALUES (:scryfall_id, :name, :set_code, :collector_number, :rarity,"
" :color_identity, :cmc, :type_line, :release_date, :edhrec_rank,"
"(`scryfall_id`, `oracle_id`, `name`, `set_code`, `collector_number`,"
" `rarity`, `color_identity`, `cmc`, `type_line`, `release_date`,"
" `edhrec_rank`, `oracle_text`) "
"VALUES (:scryfall_id, :oracle_id, :name, :set_code, :collector_number,"
" :rarity, :color_identity, :cmc, :type_line, :release_date, :edhrec_rank,"
" :oracle_text) "
"ON CONFLICT (scryfall_id) DO UPDATE "
"SET `name` = :name"
@ -245,6 +247,7 @@ async def store_card(db: aiosqlite.Connection, card: tutor.models.Card) -> None:
" , `oracle_text` = :oracle_text",
{
"scryfall_id": str(card.scryfall_id),
"oracle_id": str(card.oracle_id),
"name": card.name,
"set_code": card.set_code,
"collector_number": card.collector_number,
@ -324,6 +327,24 @@ async def store_copy(db: aiosqlite.Connection, copy: tutor.models.CardCopy) -> N
)
async def store_deck(db: aiosqlite.Connection, name: str) -> None:
cursor = await db.execute(
"INSERT INTO `decks` (`name`) VALUES (:name)",
{"name": name},
)
return cursor.lastrowid
async def store_deck_card(
db: aiosqlite.Connection, deck_id: int, oracle_id: uuid.UUID, quantity: int = 1
) -> None:
await db.execute(
"INSERT INTO `deck_cards` (`deck_id`, `oracle_id`, `quantity`)"
"VALUES (:deck_id, :oracle_id, :quantity)",
{"deck_id": deck_id, "oracle_id": str(oracle_id), "quantity": quantity},
)
async def store_var(db: aiosqlite.Connection, key: str, value: str) -> None:
await db.execute(
"INSERT INTO `vars` (`key`, `value`)"

View file

@ -65,6 +65,7 @@ class Legality(enum.Enum):
@dataclasses.dataclass
class Card:
scryfall_id: uuid.UUID
oracle_id: uuid.UUID
name: str
set_code: str
collector_number: str

View file

@ -10,6 +10,7 @@ def to_card(data: dict) -> tutor.models.Card:
}
return tutor.models.Card(
scryfall_id=data["id"],
oracle_id=data["oracle_id"],
name=data["name"],
set_code=data["set"].upper(),
collector_number=data["collector_number"],