Add deck list tracking
This commit is contained in:
parent
0f0abcd66e
commit
f326dd3ecd
5 changed files with 115 additions and 32 deletions
64
tables.sql
64
tables.sql
|
@ -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
|
||||
|
|
50
tutor/cli.py
50
tutor/cli.py
|
@ -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"]
|
||||
|
|
|
@ -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`)"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"],
|
||||
|
|
Loading…
Reference in a new issue