diff --git a/fftcgtool/__init__.py b/fftcgtool/__init__.py index 4117f37..2054474 100644 --- a/fftcgtool/__init__.py +++ b/fftcgtool/__init__.py @@ -1,7 +1,8 @@ from .book import Book from .carddb import CardDB, RWCardDB +from .ffdecks import FFDecks from .language import Language from .opus import Opus from .ttsdeck import TTSDeck -__all__ = ["Book", "CardDB", "RWCardDB", "Language", "Opus", "TTSDeck"] +__all__ = ["Book", "CardDB", "RWCardDB", "FFDecks", "Language", "Opus", "TTSDeck"] diff --git a/fftcgtool/ffdecks.py b/fftcgtool/ffdecks.py new file mode 100644 index 0000000..4654df1 --- /dev/null +++ b/fftcgtool/ffdecks.py @@ -0,0 +1,106 @@ +from __future__ import annotations + +import logging +import re +from typing import Iterable, Iterator, Optional + +import requests + +from .code import Code +from .ttsdeck import TTSDeck + + +class FFDecks(list[TTSDeck]): + __FFDECKS_API_URL = "https://ffdecks.com/api/deck" + __RE_FFDECKS_ID = re.compile(r"((https?://)?ffdecks\.com(/+api)?/+deck/+)?([0-9]+).*", flags=re.UNICODE) + + @classmethod + def sanitized_ids(cls, deck_ids: Iterable) -> Iterator[Optional[str]]: + matches = ( + cls.__RE_FFDECKS_ID.match(str(deck_id)) + for deck_id in deck_ids + ) + + return ( + match.groups()[3] + if match is not None + else None + for match in matches + ) + + def __init__(self, deck_ids: Iterable): + super().__init__() + logger = logging.getLogger(__name__) + + def by_type(data: dict[str, str | int]) -> int: + key_prios = { + "Forward": 1, + "Summon": 2, + "Monster": 3, + "Backup": 5, + } + + try: + return key_prios[data["type"]] + except KeyError: + return 4 + + def by_cost(data: dict[str, str | int]) -> int: + return data["cost"] + + for deck_id in self.sanitized_ids(deck_ids): + if deck_id is None: + logger.error("Malformed Deck ID for FFDecks API!") + + else: + # api request + res = requests.get(FFDecks.__FFDECKS_API_URL, params={"deck_id": deck_id}) + + if not res.ok: + logger.error(f"Invalid Deck ID '{deck_id}' for FFDecks API!") + + else: + # general metadata + name = f"{res.json()['name']}" + description = deck_id + + logger.info(f"Importing Deck {name!r}") + + # pre-extract the used data + deck_cards = [{ + "code": card["card"]["serial_number"], + "type": card["card"]["type"], + "cost": int(card["card"]["cost"]), + "count": int(card["quantity"]), + } for card in res.json()["cards"]] + + # sort cards by type, then by cost + deck_cards.sort(key=by_cost) + deck_cards.sort(key=by_type) + + # ffdecks quirk: some full-art promos in database + replace_full_arts = { + # line format: + # full-art-id: normal id, + "PR-051": "11-083", + "PR-055": "11-062", + } + + # replace with normal-art cards + for card in deck_cards: + try: + card["code"] = replace_full_arts[card["code"]] + except KeyError: + pass + + codes = [ + # create list of code objects + Code(card["code"]) + # for each card + for card in deck_cards + # repeat to meet count + for _ in range(card["count"]) + ] + + # create deck object + self.append(TTSDeck(codes, name, description, True)) diff --git a/fftcgtool/scripts/fftcgtool.py b/fftcgtool/scripts/fftcgtool.py index e94ec8d..8cf527b 100755 --- a/fftcgtool/scripts/fftcgtool.py +++ b/fftcgtool/scripts/fftcgtool.py @@ -168,7 +168,7 @@ def ffdecks(deck_ids: list[str]) -> list[fftcgtool.TTSDeck]: DECK_ID: each of the Decks to import """ - return list(fftcgtool.TTSDeck.from_ffdecks_decks(deck_ids)) + return fftcgtool.FFDecks(deck_ids) @main.result_callback() diff --git a/fftcgtool/ttsdeck.py b/fftcgtool/ttsdeck.py index f864b30..e6b881f 100644 --- a/fftcgtool/ttsdeck.py +++ b/fftcgtool/ttsdeck.py @@ -3,10 +3,6 @@ from __future__ import annotations import json import logging import os -import re -from typing import Optional, Iterable, Iterator - -import requests from .carddb import CardDB from .cards import Cards @@ -48,100 +44,6 @@ class TTSDeck(Cards): def file_name(self) -> str: return f"{super().file_name}.json" - __FFDECKS_API_URL = "https://ffdecks.com/api/deck" - __RE_FFDECKS_ID = re.compile(r"((https?://)?ffdecks\.com(/+api)?/+deck/+)?([0-9]+).*", flags=re.UNICODE) - - @classmethod - def sanitized_ids(cls, deck_ids: Iterable) -> Iterator[Optional[str]]: - matches = ( - cls.__RE_FFDECKS_ID.match(str(deck_id)) - for deck_id in deck_ids - ) - - return ( - match.groups()[3] - if match is not None - else None - for match in matches - ) - - @classmethod - def from_ffdecks_decks(cls, deck_ids: Iterable) -> Iterator[TTSDeck]: - logger = logging.getLogger(__name__) - - def by_type(data: dict[str, str | int]) -> int: - key_prios = { - "Forward": 1, - "Summon": 2, - "Monster": 3, - "Backup": 5, - } - - try: - return key_prios[data["type"]] - except KeyError: - return 4 - - def by_cost(data: dict[str, str | int]) -> int: - return data["cost"] - - for deck_id in cls.sanitized_ids(deck_ids): - if deck_id is None: - logger.error("Malformed Deck ID for FFDecks API!") - - else: - # api request - res = requests.get(TTSDeck.__FFDECKS_API_URL, params={"deck_id": deck_id}) - - if not res.ok: - logger.error(f"Invalid Deck ID '{deck_id}' for FFDecks API!") - - else: - # general metadata - name = f"{res.json()['name']}" - description = deck_id - - logger.info(f"Importing Deck {name!r}") - - # pre-extract the used data - deck_cards = [{ - "code": card["card"]["serial_number"], - "type": card["card"]["type"], - "cost": int(card["card"]["cost"]), - "count": int(card["quantity"]), - } for card in res.json()["cards"]] - - # sort cards by type, then by cost - deck_cards.sort(key=by_cost) - deck_cards.sort(key=by_type) - - # ffdecks quirk: some full-art promos in database - replace_full_arts = { - # line format: - # full-art-id: normal id, - "PR-051": "11-083", - "PR-055": "11-062", - } - - # replace with normal-art cards - for card in deck_cards: - try: - card["code"] = replace_full_arts[card["code"]] - except KeyError: - pass - - codes = [ - # create list of code objects - Code(card["code"]) - # for each card - for card in deck_cards - # repeat to meet count - for _ in range(card["count"]) - ] - - # create deck object - yield cls(codes, name, description, True) - def get_tts_object(self, language: Language) -> dict[str, any]: carddb = CardDB()