diff --git a/fftcg/book.py b/fftcg/book.py index 8b70d39..4168205 100644 --- a/fftcg/book.py +++ b/fftcg/book.py @@ -6,7 +6,7 @@ from PIL import Image from .cards import Cards from .imageloader import ImageLoader from .language import Language -from .utils import GRID, RESOLUTION, CARD_BACK_URL, IMAGES_DIR_NAME +from .utils import GRID, RESOLUTION, CARD_BACK_URL, IMAGES_DIR_NAME, chunks, grid_paste class Book: @@ -19,7 +19,7 @@ class Book: # all card face URLs urls = [ - ("https://fftcg.cdn.sewest.net/images/cards/full/{}_{}.jpg", str(card.code), language.image_suffix) + ("https://fftcg.cdn.sewest.net/images/cards/full/{}_{}.jpg", card.code.long, language.image_suffix) for card in cards ] # card back URL @@ -32,17 +32,19 @@ class Book: self.__pages = [] - for page_num, (page_images, page_cards) in enumerate(zip(GRID.chunks(images), GRID.chunks(cards))): + for page_num, (page_images, page_cards) in enumerate(zip( + chunks(GRID.capacity, images), chunks(GRID.capacity, cards) + )): # create book page Image 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) # set card indices for i, card in enumerate(page_cards): diff --git a/fftcg/code.py b/fftcg/code.py index 08265ee..91f5bd7 100644 --- a/fftcg/code.py +++ b/fftcg/code.py @@ -1,60 +1,39 @@ from __future__ import annotations import re +from dataclasses import dataclass, InitVar, field +@dataclass(frozen=True) class 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]+)") + opus: str = field(init=False) + serial: int = field(init=False) + rarity: str = field(init=False, compare=False) + code_init: InitVar[str] = field(default="") - 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() + __RE = re.compile(r"([1-9][0-9]*|PR|B)-([0-9]+)([CRHLS]?)", flags=re.UNICODE) - elif code.startswith("PR"): - # card code starts with "PR" - self.__opus, self.__serial = \ - Code.__RE_PROMO.match(code).groups() - self.__rarity = "" + def __post_init__(self, code_init: str): + match = Code.__RE.match(code_init) - elif code.startswith("B"): - # card code starts with "B" - self.__opus, self.__serial = \ - Code.__RE_BOSS.match(code).groups() - self.__rarity = "" + if match is not None: + groups = match.groups() + opus, serial, rarity = \ + groups[0], int(groups[1]), groups[2] else: # card code not recognized - self.__opus, self.__serial, self.__rarity = \ - "?", "???", "?" + opus, serial, rarity = \ + "?", 0, "?" - def __str__(self) -> str: - return f"{self.__opus}-{self.__serial}{self.__rarity}" - - def __repr__(self) -> str: - return f"Code({str(self)!r})" - - def __hash__(self) -> hash: - return hash(self.short) - - def __eq__(self, other: Code): - return self.short == other.short + object.__setattr__(self, "opus", opus) + object.__setattr__(self, "serial", serial) + object.__setattr__(self, "rarity", rarity) @property def short(self) -> str: - return f"{self.__opus}-{self.__serial}" + return f"{self.opus}-{self.serial:03}" @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 long(self) -> str: + return f"{self.short}{self.rarity}" diff --git a/fftcg/grid.py b/fftcg/grid.py index af1a1cf..10d7ac9 100644 --- a/fftcg/grid.py +++ b/fftcg/grid.py @@ -1,9 +1,5 @@ from __future__ import annotations -from PIL import Image - -_Point = tuple[int, int] - class Grid(tuple[int, int]): def __mul__(self, other: Grid) -> Grid: @@ -21,16 +17,3 @@ class Grid(tuple[int, int]): def capacity(self): # capacity of grid (reserve last space for card back) return self.x * self.y - 1 - - def chunks(self, whole: list) -> list: - # while there are elements - while whole: - # get a chunk - yield whole[:self.capacity] - # remove that chunk - whole = whole[self.capacity:] - - def paste(self, page: Image.Image, index: int, card: Image.Image) -> None: - w, h = card.size - position = (index % self.x) * w, (index // self.x) * h - page.paste(card, position) diff --git a/fftcg/language.py b/fftcg/language.py index 548fbe4..7e303ff 100644 --- a/fftcg/language.py +++ b/fftcg/language.py @@ -1,43 +1,37 @@ from __future__ import annotations +from dataclasses import dataclass, InitVar, field + +@dataclass(frozen=True) class Language: - def __init__(self, language: str): - language = language.lower() + short: str = field(init=False) + short_init: InitVar[str] = field(default="") + + def __post_init__(self, short_init: str): + short_init = short_init.lower() # supported languages - if language in ["de", "es", "fr", "ja", "it"]: - self.__short = language + if short_init in ["de", "es", "fr", "ja", "it"]: + object.__setattr__(self, "short", short_init) else: # everything else is english - self.__short = "en" - - def __repr__(self): - return f"Language({self.__short!r})" - - def __str__(self): - return self.__short - - def __hash__(self) -> hash: - return hash(str(self)) - - def __eq__(self, other: Language): - return str(self) == str(other) + object.__setattr__(self, "short", "en") @property def image_suffix(self): # supported languages for face URLs - if self.__short in ["de", "es", "fr", "it"]: - return self.__short + if self.short in ["de", "es", "fr", "it"]: + return self.short else: return "eg" @property def key_suffix(self): # supported languages for Square API - if self.__short in ["de", "es", "fr", "it"]: - return f"_{self.__short.upper()}" - elif self.__short == "ja": + if self.short in ["de", "es", "fr", "it"]: + return f"_{self.short.upper()}" + elif self.short == "ja": return "" else: return "_EN" diff --git a/fftcg/opus.py b/fftcg/opus.py index b768423..9a4c50b 100644 --- a/fftcg/opus.py +++ b/fftcg/opus.py @@ -17,17 +17,17 @@ class Opus(Cards): params: dict[str, any] if opus_id.isnumeric(): - name = f"Opus {opus_id} ({language})" + name = f"Opus {opus_id} ({language.short})" self.__number = opus_id params = {"set": [f"Opus {roman.toRoman(int(opus_id)).upper()}"]} elif opus_id == "chaos": - name = f"Boss Deck Chaos ({language})" + name = f"Boss Deck Chaos ({language.short})" self.__number = "B" params = {"set": ["Boss Deck Chaos"]} elif opus_id == "promo": - name = f"Promo ({language})" + name = f"Promo ({language.short})" self.__number = "PR" params = {"rarity": ["pr"]} diff --git a/fftcg/utils.py b/fftcg/utils.py index eacb846..c0a2ef6 100644 --- a/fftcg/utils.py +++ b/fftcg/utils.py @@ -1,3 +1,5 @@ +from PIL import Image + from .grid import Grid # constants @@ -33,3 +35,18 @@ def encircle_symbol(symbol: str, negative: bool): symbol_num = ord(symbol) - ord(base_symbols[0]) return chr(ord(base_symbols[1]) + symbol_num) + + +def chunks(chunk_size: int, whole: list): + # while there are elements + while whole: + # get a chunk + yield whole[:chunk_size] + # remove that chunk + whole = whole[chunk_size:] + + +def grid_paste(page: Image.Image, index: int, card: Image.Image) -> None: + w, h = card.size + position = (index % GRID.x) * w, (index // GRID.x) * h + page.paste(card, position)