From 8360eb69b2d739ae891854d01592a3aa573a8ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= Date: Tue, 17 Aug 2021 17:37:28 +0200 Subject: [PATCH] major cleanup --- fftcg/book.py | 27 ++++++++------------ fftcg/card.py | 29 +++------------------- fftcg/carddb.py | 59 +++++++++++++++++++++++++++----------------- fftcg/grid.py | 9 ++++--- fftcg/imageloader.py | 6 +++-- fftcg/opus.py | 30 ++++++++++++++++++++++ fftcg/ttsdeck.py | 5 ++-- fftcg/utils.py | 31 +++++++++++++++++++++++ main.py | 42 +++++-------------------------- 9 files changed, 130 insertions(+), 108 deletions(-) create mode 100644 fftcg/utils.py diff --git a/fftcg/book.py b/fftcg/book.py index 000bbe7..583bf78 100644 --- a/fftcg/book.py +++ b/fftcg/book.py @@ -4,18 +4,14 @@ import yaml from PIL import Image from .cards import Cards -from .grid import Grid from .imageloader import ImageLoader +from .utils import GRID, RESOLUTION, BOOK_YML_NAME class Book: - def __init__(self, cards: Cards, grid: tuple[int, int], resolution: tuple[int, int], language: str, - num_threads: int): + def __init__(self, cards: Cards, language: str, num_threads: int): logger = logging.getLogger(__name__) - # transform grid into Grid - grid = Grid(grid) - # sort cards by element, then alphabetically cards.sort(key=lambda x: x.name) cards.sort(key=lambda x: "Multi" if len(x.elements) > 1 else x.elements[0]) @@ -28,22 +24,22 @@ class Book: ) # multi-threaded download - images = ImageLoader.load(urls, resolution, language, num_threads) + images = ImageLoader.load(urls, language, num_threads) # card back Image back_image = images.pop(-1) self.__pages = [] - for page_images, page_cards in zip(grid.chunks(images), grid.chunks(cards)): + for page_images, page_cards in zip(GRID.chunks(images), GRID.chunks(cards)): # create book page Image - page_image = Image.new("RGB", grid * resolution) + page_image = Image.new("RGB", GRID * RESOLUTION) logger.info(f"New image: {page_image.size[0]}x{page_image.size[1]}") # paste card faces onto page for i, image in enumerate(page_images): - grid.paste(page_image, i, image) + GRID.paste(page_image, i, image) # paste card back in last position - grid.paste(page_image, grid.capacity, back_image) + GRID.paste(page_image, GRID.capacity, back_image) # save page self.__pages.append({ @@ -51,15 +47,12 @@ class Book: "cards": page_cards, }) - def __getitem__(self, index: int) -> Image.Image: - return self.__pages[index]["image"] - - def save(self, file_name: str, book_yml_name: str) -> None: + def save(self, file_name: str) -> None: book: dict[str, dict[str, any]] # load book.yml file try: - with open(book_yml_name, "r") as file: + with open(BOOK_YML_NAME, "r") as file: book = yaml.load(file, Loader=yaml.Loader) except FileNotFoundError: book = {} @@ -73,5 +66,5 @@ class Book: book[fn] = {"cards": page["cards"]} # update book.yml file - with open(book_yml_name, "w") as file: + with open(BOOK_YML_NAME, "w") as file: yaml.dump(book, file, Dumper=yaml.Dumper) diff --git a/fftcg/card.py b/fftcg/card.py index 7287cb6..ea8b014 100644 --- a/fftcg/card.py +++ b/fftcg/card.py @@ -1,33 +1,12 @@ +from __future__ import annotations + import json 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) +from .utils import encircle_symbol class Card(yaml.YAMLObject): @@ -53,7 +32,7 @@ class Card(yaml.YAMLObject): self.__text = text @classmethod - def from_data(cls, data: dict[str, any], language: str): + def from_data(cls, data: dict[str, any], language: str) -> Card: if not data: return cls( code=Code(""), diff --git a/fftcg/carddb.py b/fftcg/carddb.py index 77f9fda..4f2411a 100644 --- a/fftcg/carddb.py +++ b/fftcg/carddb.py @@ -1,45 +1,60 @@ +from __future__ import annotations + import yaml -_DictOfDicts = dict[str, dict[str, any]] +from fftcg.code import Code +from fftcg.utils import BOOK_YML_NAME -class CardDB(_DictOfDicts): - def __init__(self, book_yml_name: str): - book: _DictOfDicts +class CardDB: + __instance: CardDB = None + @classmethod + def get(cls) -> CardDB: + if not CardDB.__instance: + CardDB.__instance = CardDB() + + return CardDB.__instance + + def __init__(self): + self.__content: dict[Code, dict[str, any]] = {} + + def __getitem__(self, code: Code) -> dict[str, any]: + return self.__content[str(code)] + + def load(self): # load book.yml file + book: dict try: - with open(book_yml_name, "r") as file: + with open(BOOK_YML_NAME, "r") as file: book = yaml.load(file, Loader=yaml.Loader) except FileNotFoundError: book = {} # "invert" book into card database: # every card is indexable by its code - carddb: _DictOfDicts = {} + self.__content.clear() - for fn, content in book.items(): - carddb |= { + for file_name, content in book.items(): + self.__content |= { str(card.code): { "card": card, - "file": fn, + "file": file_name, "index": i, } for i, card in enumerate(content["cards"]) } - super().__init__(carddb) - # write carddb.yml file with open("carddb.yml", "w") as file: - yaml.dump(self, file, Dumper=yaml.Dumper) + yaml.dump(self.__content, file, Dumper=yaml.Dumper) - def make_deck(self, filters): - # filter codes by card criteria - codes = [ - content["card"].code - for content in self.values() - if all([f(content["card"]) for f in filters]) - ] - - from .ttsdeck import TTSDeck - return TTSDeck(codes, self) + # def make_deck(self, filters): + # # filter codes by card criteria + # codes = [ + # content["card"].code + # for content in self.__content.values() + # if all([f(content["card"]) for f in filters]) + # ] + # + # from .ttsdeck import TTSDeck + # return TTSDeck(codes) diff --git a/fftcg/grid.py b/fftcg/grid.py index e8cc178..af1a1cf 100644 --- a/fftcg/grid.py +++ b/fftcg/grid.py @@ -1,12 +1,13 @@ +from __future__ import annotations + from PIL import Image _Point = tuple[int, int] -class Grid(_Point): - def __mul__(self, other: _Point) -> _Point: - other = Grid(other) - return self.x * other.x, self.y * other.y +class Grid(tuple[int, int]): + def __mul__(self, other: Grid) -> Grid: + return Grid((self.x * other.x, self.y * other.y)) @property def x(self): diff --git a/fftcg/imageloader.py b/fftcg/imageloader.py index a836152..50eb495 100644 --- a/fftcg/imageloader.py +++ b/fftcg/imageloader.py @@ -6,6 +6,8 @@ import threading import requests from PIL import Image +from fftcg.utils import RESOLUTION + class ImageLoader(threading.Thread): def __init__(self, url_queue: queue.Queue, resolution: tuple[int, int], language: str): @@ -44,14 +46,14 @@ class ImageLoader(threading.Thread): self.__queue.task_done() @classmethod - def load(cls, urls: list[str], resolution: tuple[int, int], language: str, num_threads: int) -> list[Image.Image]: + def load(cls, urls: list[str], language: str, num_threads: int) -> list[Image.Image]: url_queue = queue.Queue() for url in urls: url_queue.put(url) loaders = [] for _ in range(num_threads): - loader = cls(url_queue, resolution, language) + loader = cls(url_queue, RESOLUTION, language) loaders.append(loader) loader.start() diff --git a/fftcg/opus.py b/fftcg/opus.py index 2104f61..6f3cd76 100644 --- a/fftcg/opus.py +++ b/fftcg/opus.py @@ -3,6 +3,7 @@ import logging import roman from .cards import Cards +from .ttsdeck import TTSDeck class Opus(Cards): @@ -60,3 +61,32 @@ class Opus(Cards): @property def filename(self) -> str: return self.__filename + + @property + def elemental_decks(self) -> list[TTSDeck]: + if self.name in ["Promo", "Boss Deck Chaos"]: + return [TTSDeck([ + card.code + for card in self + ])] + + else: + def element_filter(element: str): + return lambda card: card.elements == [element] + + # simple cases: create lambdas for base elemental decks + base_elements = ["Fire", "Ice", "Wind", "Earth", "Lightning", "Water"] + filters = [element_filter(elem) for elem in base_elements] + + filters += [ + # light/darkness elemental deck + lambda card: card.elements == ["Light"] or card.elements == ["Darkness"], + # multi element deck + lambda card: len(card.elements) > 1, + ] + + return [TTSDeck([ + card.code + for card in self + if f(card) + ]) for f in filters] diff --git a/fftcg/ttsdeck.py b/fftcg/ttsdeck.py index c81fc0f..ba85350 100644 --- a/fftcg/ttsdeck.py +++ b/fftcg/ttsdeck.py @@ -5,8 +5,9 @@ from .code import Code class TTSDeck(list[dict[str, any]]): - def __init__(self, codes: list[Code], carddb: CardDB): - super().__init__([carddb[str(code)] for code in codes]) + def __init__(self, codes: list[Code]): + carddb = CardDB.get() + super().__init__([carddb[code] for code in codes]) def __str__(self) -> str: face_urls = list(set([entry["file"] for entry in self])) diff --git a/fftcg/utils.py b/fftcg/utils.py new file mode 100644 index 0000000..6024bf9 --- /dev/null +++ b/fftcg/utils.py @@ -0,0 +1,31 @@ +from .grid import Grid + +# constants +GRID = Grid((10, 7)) # default in TTsim: 10 columns, 7 rows +RESOLUTION = Grid((429, 600)) # default in TTsim: 480x670 pixels per card +BOOK_YML_NAME = "book.yml" + + +# functions +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) diff --git a/main.py b/main.py index ccccf84..1586f95 100755 --- a/main.py +++ b/main.py @@ -5,11 +5,6 @@ import os import fftcg -# constants -GRID = 10, 7 # default in TTsim: 10 columns, 7 rows -RESOLUTION = 429, 600 # default in TTsim: 480x670 pixels per card -BOOK_YML_NAME = "book.yml" - def main() -> None: # set up CLI @@ -45,40 +40,15 @@ def main() -> None: # main program opus = fftcg.Opus(args.opus_id) - book = fftcg.Book(opus, GRID, RESOLUTION, "eg", args.num_threads) - book.save(opus.filename, BOOK_YML_NAME) + book = fftcg.Book(opus, "eg", args.num_threads) + book.save(opus.filename) # create elemental decks for opus - carddb = fftcg.CardDB(BOOK_YML_NAME) + carddb = fftcg.CardDB.get() + carddb.load() - def opus_filter(card: fftcg.Card): - return card.code.opus == opus.number - - filters: list - if opus.number == "PR": - filters = [[opus_filter]] - - else: - def element_filter(element: str): - return lambda card: card.elements == [element] - - # simple cases: create lambdas for base elemental decks - base_elements = ["Fire", "Ice", "Wind", "Earth", "Lightning", "Water"] - element_filters = [element_filter(elem) for elem in base_elements] - - element_filters += [ - # light/darkness elemental deck - lambda card: card.elements == ["Light"] or card.elements == ["Darkness"], - # multi element deck - lambda card: len(card.elements) > 1, - ] - - # add in the opus_filter for all elemental decks - filters = list(zip([opus_filter] * len(element_filters), element_filters)) - - # make the decks - for f in filters: - print(carddb.make_deck(f)) + for deck in opus.elemental_decks: + print(deck) # bye logging.info("Done. Put the generated JSON files in your 'Saved Objects' Folder.")