diff --git a/fftcg/book.py b/fftcg/book.py index 0578a41..85c736f 100644 --- a/fftcg/book.py +++ b/fftcg/book.py @@ -4,6 +4,7 @@ import yaml from PIL import Image from .cards import Cards +from .code import Code from .grid import Grid from .imageloader import ImageLoader @@ -55,24 +56,39 @@ class Book: return self.__pages[index]["image"] def save(self, filename: str) -> None: - pages: dict[str, dict[str, any]] + book: dict[str, dict[str, any]] - # load pages.yml file + # load book.yml file try: - with open("pages.yml", "r") as file: - pages = yaml.load(file, Loader=yaml.Loader) + with open("book.yml", "r") as file: + book = yaml.load(file, Loader=yaml.Loader) except FileNotFoundError: - pages = {} + book = {} # save book for i, page in enumerate(self.__pages): fn = f"{filename}_{i}.jpg" # save page image page["image"].save(fn) - # add contents of image - pages[fn] = {} - pages[fn]["cards"] = page["cards"] + # add contents of that image + book[fn] = {"cards": page["cards"]} - # update pages.yml file - with open("pages.yml", "w") as file: - yaml.dump(pages, file, Dumper=yaml.Dumper) + # update book.yml file + with open("book.yml", "w") as file: + yaml.dump(book, file, Dumper=yaml.Dumper) + + # invert book + inverse_book: dict[Code, dict[str, any]] = {} + + for fn, content in book.items(): + inverse_book |= { + str(card.code): { + "card": card, + "file": fn, + "index": i + } for i, card in enumerate(content["cards"]) + } + + # write inverse_book.yml file + with open("inverse_book.yml", "w") as file: + yaml.dump(inverse_book, file, Dumper=yaml.Dumper) diff --git a/fftcg/card.py b/fftcg/card.py index a02598d..cf4bd54 100644 --- a/fftcg/card.py +++ b/fftcg/card.py @@ -3,6 +3,32 @@ import re import yaml +from .code import Code + + +def encircle_symbol(symbol: str, negative: bool): + symbol = symbol[0].upper() + + base_symbols: tuple[str, str] = "", "" + if symbol.isalpha(): + if negative: + base_symbols = "A", "๐Ÿ…" + else: + base_symbols = "A", "โ’ถ" + elif symbol == "0": + if negative: + base_symbols = "0", "๐Ÿ„Œ" + else: + base_symbols = "0", "โ“ช" + elif symbol.isnumeric(): + if negative: + base_symbols = "1", "โžŠ" + else: + base_symbols = "1", "โ‘ " + + symbol_num = ord(symbol) - ord(base_symbols[0]) + return chr(ord(base_symbols[1]) + symbol_num) + class Card(yaml.YAMLObject): yaml_tag = u'!Card' @@ -18,10 +44,10 @@ class Card(yaml.YAMLObject): "้—‡": "Darkness" } - def __init__(self, opus, serial, rarity, elements, name, text): - self.__opus = opus - self.__serial = serial - self.__rarity = rarity + __ELEMENTS = "".join(__ELEMENTS_MAP.keys()) + + def __init__(self, code, elements, name, text): + self.__code = code self.__elements = elements self.__name = name self.__text = text @@ -30,44 +56,41 @@ class Card(yaml.YAMLObject): def from_data(cls, data: dict[str, any], language: str): if not data: return cls( - opus="0", - serial="000", - rarity="X", + code=Code(""), elements=[], name=None, text=None, ) else: - if str(data["Code"])[0].isnumeric(): - # card code starts with a number - opus, serial, rarity = \ - re.match(r"([0-9]+)-([0-9]+)([CRHLS])", data["Code"]).groups() + def sub_encircle(match: re.Match): + return encircle_symbol(match.group(1), True) - elif str(data["Code"]).startswith("PR"): - # card code starts with "PR" - opus, serial = \ - re.match(r"(PR)-([0-9]+)", data["Code"]).groups() - rarity = "" + def sub_elements(match: re.Match): + return encircle_symbol(Card.__ELEMENTS_MAP[match.group(1)], True) - elif str(data["Code"]).startswith("B"): - # card code starts with "B" - opus, serial = \ - re.match(r"(B)-([0-9]+)", data["Code"]).groups() - rarity = "" - - else: - # card code not recognized - opus, serial, rarity = \ - "?", "???", "?" + # load text + text = str(data[f"Text_{language}"]) + # place "S" symbols + text = text.replace("ใ€ŠSใ€‹", encircle_symbol("S", False)) + # place other letter and numerical cost symbols + text = re.sub(r"ใ€Š([a-z0-9])ใ€‹", sub_encircle, text, flags=re.IGNORECASE) + # place elemental cost symbols + text = re.sub(rf"ใ€Š([{Card.__ELEMENTS}])ใ€‹", sub_elements, text, flags=re.IGNORECASE) + # place dull symbols + text = text.replace("ใ€Šใƒ€ใƒซใ€‹", "[โคต]") + # replace formatting hints with brackets + text = re.sub(r"\[\[[a-z]\]\]([^\[]*?)\s*\[\[/\]\]\s*", r"[\1] ", text, flags=re.IGNORECASE) + # place EX-BURST markers + text = re.sub(r"\[\[ex\]\]\s*EX BURST\s*\[\[/\]\]\s*", r"[EX BURST] ", text, flags=re.IGNORECASE) + # place line breaks + text = re.sub(r"\s*\[\[br\]\]\s*", "\n\n", text, flags=re.IGNORECASE) return cls( - opus=opus, - serial=serial, - rarity=rarity, + code=Code(data["Code"]), elements=[Card.__ELEMENTS_MAP[element] for element in data["Element"].split("/")], name=data[f"Name_{language}"], - text=data[f"Text_{language}"], + text=text, ) def __str__(self) -> str: @@ -78,20 +101,8 @@ class Card(yaml.YAMLObject): # 6-048C @property - def code(self) -> str: - return f"{self.__opus}-{self.__serial}{self.__rarity}" - - @property - def opus(self) -> str: - return self.__opus - - @property - def serial(self) -> int: - return int(self.__serial) - - @property - def rarity(self) -> str: - return self.__rarity + def code(self) -> Code: + return self.__code @property def name(self) -> str: diff --git a/fftcg/cards.py b/fftcg/cards.py index 889165b..3cc1162 100644 --- a/fftcg/cards.py +++ b/fftcg/cards.py @@ -6,7 +6,7 @@ from .card import Card class Cards(list[Card]): __API_URL = "https://fftcg.square-enix-games.com/de/get-cards" - def __init__(self, params: dict[str, any]): + def _load(self, params: dict[str, any]): # required params: # text # supported params: @@ -18,7 +18,8 @@ class Cards(list[Card]): params["text"] = "" req = requests.post(Cards.__API_URL, json=params) - super().__init__([Card.from_data(card_data, "EN") for card_data in req.json()["cards"]]) + self.clear() + self.extend([Card.from_data(card_data, "EN") for card_data in req.json()["cards"]]) def __str__(self) -> str: return "\n".join(str(card) for card in self) diff --git a/fftcg/code.py b/fftcg/code.py new file mode 100644 index 0000000..0b7a5b8 --- /dev/null +++ b/fftcg/code.py @@ -0,0 +1,49 @@ +import re + +import yaml + + +class Code(yaml.YAMLObject): + yaml_tag = u'!Code' + + __RE_NUM = re.compile(r"([0-9]+)-([0-9]+)([CRHLS])") + __RE_PROMO = re.compile(r"(PR)-([0-9]+)") + __RE_BOSS = re.compile(r"(B)-([0-9]+)") + + def __init__(self, code: str): + if code[0].isnumeric(): + # card code starts with a number + self.__opus, self.__serial, self.__rarity = \ + Code.__RE_NUM.match(code).groups() + + elif code.startswith("PR"): + # card code starts with "PR" + self.__opus, self.__serial = \ + Code.__RE_PROMO.match(code).groups() + self.__rarity = "" + + elif code.startswith("B"): + # card code starts with "B" + self.__opus, self.__serial = \ + Code.__RE_BOSS.match(code).groups() + self.__rarity = "" + + else: + # card code not recognized + self.__opus, self.__serial, self.__rarity = \ + "?", "???", "?" + + def __str__(self) -> str: + return f"{self.__opus}-{self.__serial}{self.__rarity}" + + @property + def opus(self) -> str: + return self.__opus + + @property + def serial(self) -> int: + return int(self.__serial) + + @property + def rarity(self) -> str: + return self.__rarity diff --git a/fftcg/opus.py b/fftcg/opus.py index 3bd4f81..2104f61 100644 --- a/fftcg/opus.py +++ b/fftcg/opus.py @@ -7,6 +7,8 @@ from .cards import Cards class Opus(Cards): def __init__(self, opus_id: str): + super().__init__() + logger = logging.getLogger(__name__) if opus_id.isnumeric(): @@ -33,16 +35,16 @@ class Opus(Cards): self.__filename = "?" params = {"set": "?"} - super().__init__(params) + self._load(params) # remove reprints for card in self: - if not card.code.startswith(self.__number + "-"): + if not card.code.opus == self.__number: self.remove(card) # sort cards by opus, then serial - self.sort(key=lambda x: x.serial) - self.sort(key=lambda x: x.opus) + self.sort(key=lambda x: x.code.serial) + self.sort(key=lambda x: x.code.opus) for card in self: logger.info(f"imported card {card}")