From 439a904c5445dd9f1847c13daaa1058de2815756 Mon Sep 17 00:00:00 2001 From: AJ Slater Date: Wed, 21 Feb 2024 18:08:50 -0800 Subject: [PATCH] full dates in serialization. remainder in square brackets. tests for serializer --- comicfn2dict/regex.py | 8 +++++-- comicfn2dict/unparse.py | 48 +++++++++++++++++++++++++++++++------- tests/comic_filenames.py | 35 +++++++++++++++++++++++++-- tests/test_comicfn2dict.py | 8 ++----- tests/test_dict2comicfn.py | 13 +++++++++++ 5 files changed, 94 insertions(+), 18 deletions(-) create mode 100644 tests/test_dict2comicfn.py diff --git a/comicfn2dict/regex.py b/comicfn2dict/regex.py index c17ecd8..dc2e987 100644 --- a/comicfn2dict/regex.py +++ b/comicfn2dict/regex.py @@ -105,10 +105,14 @@ _MONTH_NUMERIC_RE_EXP = r"(?P0?\d|1[0-2]?)" _MONTH_RE_EXP = r"(" + _MONTH_ALPHA_RE_EXP + r"|" + _MONTH_NUMERIC_RE_EXP + r")" _ALPHA_MONTH_RANGE = ( r"\b" - + r"(" + r"|".join(MONTHS) + r")" + + r"(" + + r"|".join(MONTHS) + + r")" + r"(" + r"\.?-" - + r"(" + r"|".join(MONTHS) + r")" + + r"(" + + r"|".join(MONTHS) + + r")" + r")\b" ) print(_ALPHA_MONTH_RANGE) diff --git a/comicfn2dict/unparse.py b/comicfn2dict/unparse.py index a04380c..abe5fab 100644 --- a/comicfn2dict/unparse.py +++ b/comicfn2dict/unparse.py @@ -1,5 +1,8 @@ """Unparse comic filenames.""" -from collections.abc import Callable, Mapping +from collections.abc import Callable, Mapping, Sequence +from contextlib import suppress +from calendar import month_abbr +from types import MappingProxyType def issue_formatter(issue: str) -> str: @@ -18,18 +21,39 @@ _PAREN_FMT: str = "({})" _FILENAME_FORMAT_TAGS: tuple[tuple[str, str | Callable], ...] = ( ("series", "{}"), ("volume", "v{}"), + ("volume_count", "(of {:03})"), ("issue", issue_formatter), ("issue_count", "(of {:03})"), - ("year", _PAREN_FMT), + ("date", _PAREN_FMT), ("title", "{}"), + ("publisher", _PAREN_FMT), ("original_format", _PAREN_FMT), ("scan_info", _PAREN_FMT), ) _EMPTY_VALUES: tuple[None, str] = (None, "") _DEFAULT_EXT = "cbz" +_DATE_KEYS = ("year", "month", "day") class ComicFilenameSerializer: + def _add_date(self) -> None: + if "date" in self.metadata: + return + parts = [] + for key in _DATE_KEYS: + if part := self.metadata.get(key): + if key == "month" and not parts: + with suppress(TypeError): + part = month_abbr[int(part)] + + parts.append(part) + if key == "month" and not parts: + # noop if only day. + break + if parts: + date = "-".join(parts) + self.metadata = MappingProxyType({**self.metadata, "date": date}) + def _tokenize_tag(self, tag: str, fmt: str | Callable) -> str: val = self.metadata.get(tag) if val in _EMPTY_VALUES: @@ -38,22 +62,30 @@ class ComicFilenameSerializer: token = final_fmt.format(val).strip() return token + def _add_remainder(self) -> str: + if remainders := self.metadata.get("remainders"): + if isinstance(remainders, Sequence): + remainder = " ".join(remainders) + else: + remainder = str(remainders) + return f"[{remainder}]" + return "" + def serialize(self) -> str: """Get our preferred basename from a metadata dict.""" + self._add_date() + tokens = [] for tag, fmt in _FILENAME_FORMAT_TAGS: if token := self._tokenize_tag(tag, fmt): tokens.append(token) fn = " ".join(tokens) - if remainders := self.metadata.get("remainders"): - # TODO make token and add before join? - remainder = " ".join(remainders) - # TODO oh this is the - delineated remainder :( - fn += f" - {remainder}" + fn += self._add_remainder() if self._ext: - fn += "." + self.metadata.get("ext", _DEFAULT_EXT) + ext = self.metadata.get("ext", _DEFAULT_EXT) + fn += f".{ext}" return fn diff --git a/tests/comic_filenames.py b/tests/comic_filenames.py index e85a18e..71823f6 100644 --- a/tests/comic_filenames.py +++ b/tests/comic_filenames.py @@ -1,5 +1,8 @@ """Test filenames with human parsed correct results.""" +from types import MappingProxyType + + TEST_COMIC_FIELDS = { "series": "Long Series Name", "issue": "001", @@ -30,7 +33,7 @@ TEST_COMIC_VOL_ONLY = { "ext": "cbr", } -# Working with 0.1.0 +# Tests for 0.1.0 FNS = { "Night of 1000 Wolves 001 (2013).cbz": { "series": "Night of 1000 Wolves", @@ -239,7 +242,7 @@ FNS = { }, } -# Fixed with 0.2.0 +# Tests for 0.2.0 FNS.update( { # Philosopy change regarding dashes. @@ -442,6 +445,34 @@ FNS.update( }, } ) +PARSE_FNS = MappingProxyType(FNS) + +SERIALIZE_FNS = MappingProxyType( + { + "Long Series Name #001 (2000) Title (TPB) (Releaser).cbz": TEST_COMIC_FIELDS, + "Long Series Name v1 #001 " + "(2000) Title (TPB) (Releaser & Releaser-Releaser).cbr": TEST_COMIC_VOL_ONLY, + "Series Name (2000-12-31).cbz": { + "series": "Series Name", + "year": "2000", + "month": "12", + "day": "31", + "ext": "cbz", + }, + "Series Name (2000-12).cbz": { + "series": "Series Name", + "year": "2000", + "month": "12", + "ext": "cbz", + }, + "Series Name (Dec-31).cbz": { + "series": "Series Name", + "month": "12", + "day": "31", + "ext": "cbz", + }, + } +) # first_key, first_val = NEW.popitem() # FNS[first_key] = first_val diff --git a/tests/test_comicfn2dict.py b/tests/test_comicfn2dict.py index 33f4d82..768c5c2 100644 --- a/tests/test_comicfn2dict.py +++ b/tests/test_comicfn2dict.py @@ -1,18 +1,14 @@ """Tests for filename parsing.""" from pprint import pprint -from types import MappingProxyType import pytest from deepdiff.diff import DeepDiff from comicfn2dict import ComicFilenameParser -from tests.comic_filenames import FNS - -ALL_FIELDS = frozenset({"series", "volume", "issue", "issue_count", "year", "ext"}) -FIELD_SCHEMA = MappingProxyType({key: None for key in ALL_FIELDS}) +from tests.comic_filenames import PARSE_FNS -@pytest.mark.parametrize("item", FNS.items()) +@pytest.mark.parametrize("item", PARSE_FNS.items()) def test_parse_filename(item): """Test filename parsing.""" fn, defined_fields = item diff --git a/tests/test_dict2comicfn.py b/tests/test_dict2comicfn.py new file mode 100644 index 0000000..787183f --- /dev/null +++ b/tests/test_dict2comicfn.py @@ -0,0 +1,13 @@ +"""Tests for filename parsing.""" +import pytest + +from comicfn2dict import ComicFilenameSerializer +from tests.comic_filenames import SERIALIZE_FNS + + +@pytest.mark.parametrize("item", SERIALIZE_FNS.items()) +def test_serialize_dict(item): + """Test metadata serialization.""" + test_fn, md = item + fn = ComicFilenameSerializer(md).serialize() + assert test_fn == fn