diff --git a/comicapi/genericmetadata.py b/comicapi/genericmetadata.py index 5cc1ed3..23f92ca 100644 --- a/comicapi/genericmetadata.py +++ b/comicapi/genericmetadata.py @@ -21,6 +21,7 @@ possible, however lossy it might be from __future__ import annotations import logging +from dataclasses import dataclass, field from typing import Any, TypedDict from comicapi import utils @@ -64,6 +65,7 @@ class CreditMetadata(TypedDict): primary: bool +@dataclass class GenericMetadata: writer_synonyms = ["writer", "plotter", "scripter"] penciller_synonyms = ["artist", "penciller", "penciler", "breakdowns"] @@ -73,59 +75,63 @@ class GenericMetadata: cover_synonyms = ["cover", "covers", "coverartist", "cover artist"] editor_synonyms = ["editor"] - def __init__(self) -> None: + is_empty: bool = True + tag_origin: str | None = None - self.is_empty: bool = True - self.tag_origin: str | None = None + series: str | None = None + issue: str | None = None + title: str | None = None + publisher: str | None = None + month: int | None = None + year: int | None = None + day: int | None = None + issue_count: int | None = None + volume: int | None = None + genre: str | None = None + language: str | None = None # 2 letter iso code + comments: str | None = None # use same way as Summary in CIX - self.series: str | None = None - self.issue: str | None = None - self.title: str | None = None - self.publisher: str | None = None - self.month: int | None = None - self.year: int | None = None - self.day: int | None = None - self.issue_count: int | None = None - self.volume: int | None = None - self.genre: str | None = None - self.language: str | None = None # 2 letter iso code - self.comments: str | None = None # use same way as Summary in CIX + volume_count: int | None = None + critical_rating: float | None = None # rating in cbl; CommunityRating in CIX + country: str | None = None - self.volume_count: int | None = None - self.critical_rating: float | None = None # rating in cbl; CommunityRating in CIX - self.country: str | None = None + alternate_series: str | None = None + alternate_number: str | None = None + alternate_count: int | None = None + imprint: str | None = None + notes: str | None = None + web_link: str | None = None + format: str | None = None + manga: str | None = None + black_and_white: bool | None = None + page_count: int | None = None + maturity_rating: str | None = None - self.alternate_series: str | None = None - self.alternate_number: str | None = None - self.alternate_count: int | None = None - self.imprint: str | None = None - self.notes: str | None = None - self.web_link: str | None = None - self.format: str | None = None - self.manga: str | None = None - self.black_and_white: bool | None = None - self.page_count: int | None = None - self.maturity_rating: str | None = None + story_arc: str | None = None + series_group: str | None = None + scan_info: str | None = None - self.story_arc: str | None = None - self.series_group: str | None = None - self.scan_info: str | None = None + characters: str | None = None + teams: str | None = None + locations: str | None = None - self.characters: str | None = None - self.teams: str | None = None - self.locations: str | None = None + credits: list[CreditMetadata] = field(default_factory=list) + tags: list[str] = field(default_factory=list) + pages: list[ImageMetadata] = field(default_factory=list) - self.credits: list[CreditMetadata] = [] - self.tags: list[str] = [] - self.pages: list[ImageMetadata] = [] + # Some CoMet-only items + price: str | None = None + is_version_of: str | None = None + rights: str | None = None + identifier: str | None = None + last_mark: str | None = None + cover_image: str | None = None - # Some CoMet-only items - self.price: str | None = None - self.is_version_of: str | None = None - self.rights: str | None = None - self.identifier: str | None = None - self.last_mark: str | None = None - self.cover_image: str | None = None + def __post_init__(self): + for key, value in self.__dict__.items(): + if value and key != "is_empty": + self.is_empty = False + break def overlay(self, new_md: GenericMetadata) -> None: """Overlay a metadata object on this one diff --git a/tests/comicarchive_test.py b/tests/comicarchive_test.py index 93501a0..25618a8 100644 --- a/tests/comicarchive_test.py +++ b/tests/comicarchive_test.py @@ -48,9 +48,7 @@ def test_metadata_read(): thisdir / "data" / "Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game (2007).cbz" ) md = c.read_cix() - md_dict = md.__dict__ - md_test_dict = comicapi.genericmetadata.md_test.__dict__ - assert md_dict == md_test_dict + assert md == comicapi.genericmetadata.md_test def test_save_cix(tmp_path): @@ -118,9 +116,7 @@ def test_copy_to_archive(archiver, tmp_path): assert set(cbz.archiver.get_filename_list()) == set(comic_archive.archiver.get_filename_list()) md = comic_archive.read_cix() - md_dict = md.__dict__ - md_test_dict = comicapi.genericmetadata.md_test.__dict__ - assert md_dict == md_test_dict + assert md == comicapi.genericmetadata.md_test md = comicapi.genericmetadata.GenericMetadata() md.overlay(comicapi.genericmetadata.md_test) @@ -129,6 +125,4 @@ def test_copy_to_archive(archiver, tmp_path): assert comic_archive.write_cix(md) test_md = comic_archive.read_cix() - md_dict = md.__dict__ - test_md_dict = test_md.__dict__ - assert md_dict == test_md_dict + assert md == test_md diff --git a/tests/genericmetadata_test.py b/tests/genericmetadata_test.py new file mode 100644 index 0000000..16683fa --- /dev/null +++ b/tests/genericmetadata_test.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +import dataclasses + +import pytest + +import comicapi.genericmetadata + + +@pytest.fixture +def md(): + yield dataclasses.replace(comicapi.genericmetadata.md_test) + + +stuff = [ + ( + {"series": "test", "issue": "2", "title": "never"}, + dataclasses.replace(comicapi.genericmetadata.md_test, series="test", issue="2", title="never"), + ), + ( + {"series": "", "issue": "2", "title": "never"}, + dataclasses.replace(comicapi.genericmetadata.md_test, series=None, issue="2", title="never"), + ), + ( + {}, + dataclasses.replace(comicapi.genericmetadata.md_test), + ), +] + + +@pytest.mark.parametrize("replaced, expected", stuff) +def test_metadata_overlay(md: comicapi.genericmetadata.GenericMetadata, replaced, expected): + md_overlay = comicapi.genericmetadata.GenericMetadata(**replaced) + md.overlay(md_overlay) + + assert md == expected + + +def test_add_credit(): + md = comicapi.genericmetadata.GenericMetadata() + + md.add_credit(person="test", role="writer", primary=False) + md.credits == [{"person": "test", "role": "writer", "primary": False}] + + +def test_add_credit_primary(): + md = comicapi.genericmetadata.GenericMetadata() + + md.add_credit(person="test", role="writer", primary=False) + md.add_credit(person="test", role="writer", primary=True) + md.credits == [{"person": "test", "role": "writer", "primary": True}] + + +credits = [ + ("writer", "Dara Naraghi"), + ("writeR", "Dara Naraghi"), +] + + +@pytest.mark.parametrize("role, expected", credits) +def test_get_primary_credit(md, role, expected): + assert md.get_primary_credit(role) == expected diff --git a/tests/issuestring_test.py b/tests/issuestring_test.py index 49b671e..3597686 100644 --- a/tests/issuestring_test.py +++ b/tests/issuestring_test.py @@ -13,6 +13,7 @@ issues = [ ("22.BEY", 22.0, "022.BEY"), ("22A", 22.0, "022A"), ("22-A", 22.0, "022-A"), + ("", None, ""), ] diff --git a/tests/utils_test.py b/tests/utils_test.py new file mode 100644 index 0000000..b767e1c --- /dev/null +++ b/tests/utils_test.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +import pytest + +import comicapi.utils + + +def test_recursive_list_with_file(tmp_path) -> None: + foo_png = tmp_path / "foo.png" + foo_png.write_text("not a png") + + temp_folder = tmp_path / "bar" + temp_folder.mkdir() + temp_file = temp_folder / "test.cbz" + temp_file.write_text("not a zip") + + temp_folder2 = tmp_path / "bar" / "baz" / "something else" + temp_folder2.mkdir(parents=True) + temp_cbr = temp_folder2 / "bar.cbr" + temp_cbr.write_text("not a rar") + + temp_txt = tmp_path / "info.txt" + temp_txt.write_text("this is here") + + expected_result = {str(foo_png), str(temp_cbr), str(temp_file), str(temp_txt)} + result = set(comicapi.utils.get_recursive_filelist([tmp_path])) + + assert result == expected_result + + +values = [ + ({"data": "", "is_int": False, "is_float": False}, None), + ({"data": None, "is_int": False, "is_float": False}, None), + ({"data": None, "is_int": True, "is_float": False}, None), + ({"data": " ", "is_int": True, "is_float": False}, None), + ({"data": "", "is_int": True, "is_float": False}, None), + ({"data": "9", "is_int": False, "is_float": False}, "9"), + ({"data": 9, "is_int": False, "is_float": False}, "9"), + ({"data": 9, "is_int": True, "is_float": False}, 9), + ({"data": "9", "is_int": True, "is_float": False}, 9), + ({"data": 9.3, "is_int": True, "is_float": False}, 9), + ({"data": "9.3", "is_int": True, "is_float": False}, 9), + ({"data": "9.", "is_int": True, "is_float": False}, 9), + ({"data": " 9 . 3 l", "is_int": True, "is_float": False}, 9), + ({"data": 9, "is_int": False, "is_float": True}, 9.0), + ({"data": "9", "is_int": False, "is_float": True}, 9.0), + ({"data": 9.3, "is_int": False, "is_float": True}, 9.3), + ({"data": "9.3", "is_int": False, "is_float": True}, 9.3), + ({"data": "9.", "is_int": False, "is_float": True}, 9.0), + ({"data": " 9 . 3 l", "is_int": False, "is_float": True}, 9.3), +] + + +@pytest.mark.parametrize("value, result", values) +def test_xlate(value, result): + assert comicapi.utils.xlate(**value) == result