diff --git a/mokkari/schemas/imprint.py b/mokkari/schemas/imprint.py new file mode 100644 index 0000000..b1d80d6 --- /dev/null +++ b/mokkari/schemas/imprint.py @@ -0,0 +1,20 @@ +"""Imprint module. + +This module provides the following classes: + +- Imprint +""" + +from mokkari.schemas.generic import GenericItem +from mokkari.schemas.publisher import Publisher + + +class Imprint(Publisher): + """A data model representing an imprint that extends Publisher. + + Attributes: + publisher (GenericItem): The generic item representing the publisher. + + """ + + publisher: GenericItem diff --git a/mokkari/schemas/issue.py b/mokkari/schemas/issue.py index 8b56b2e..26323da 100644 --- a/mokkari/schemas/issue.py +++ b/mokkari/schemas/issue.py @@ -111,6 +111,7 @@ class Issue(CommonIssue): Attributes: publisher (GenericItem): The publisher of the issue. + imprint (GenericItem, optional): The imprint of the issue or None. series (IssueSeries): The series to which the issue belongs. collection_title (str): The title of the issue collection. story_titles (list[str]): The titles of the stories in the issue. @@ -133,6 +134,7 @@ class Issue(CommonIssue): """ publisher: GenericItem + imprint: GenericItem | None = None series: IssueSeries collection_title: str = Field(alias="title") story_titles: list[str] = Field(alias="name") diff --git a/mokkari/schemas/series.py b/mokkari/schemas/series.py index 05a0f5c..7d85ccc 100644 --- a/mokkari/schemas/series.py +++ b/mokkari/schemas/series.py @@ -66,6 +66,7 @@ class Series(CommonSeries): series_type (GenericItem): The type of the series. status (str): The status of the series. publisher (GenericItem): The publisher of the series. + imprint (GenericItem, optional): The imprint of the series or None. year_end (int, optional): The year the series ended. desc (str): The description of the series. genres (list[GenericItem], optional): The genres associated with the series. @@ -79,6 +80,7 @@ class Series(CommonSeries): series_type: GenericItem status: str publisher: GenericItem + imprint: GenericItem | None = None year_end: int | None = None desc: str genres: list[GenericItem] = [] diff --git a/mokkari/session.py b/mokkari/session.py index 79d7ae8..0b67ee8 100644 --- a/mokkari/session.py +++ b/mokkari/session.py @@ -26,6 +26,7 @@ from mokkari.schemas.character import Character from mokkari.schemas.creator import Creator from mokkari.schemas.generic import GenericItem +from mokkari.schemas.imprint import Imprint from mokkari.schemas.issue import BaseIssue, Issue from mokkari.schemas.publisher import Publisher from mokkari.schemas.series import BaseSeries, Series @@ -547,6 +548,48 @@ def universes_list( raise exceptions.ApiError(err) from err return result + def imprint(self: Session, _id: int) -> Imprint: + """Retrieves an imprint by ID. + + Args: + _id: The ID of the imprint to retrieve. + + Returns: + Imprint: The retrieved imprint object. + + Raises: + ApiError: If there is an error during the API call or validation. + """ + resp = self._call(["imprint", _id]) + adaptor = TypeAdapter(Imprint) + try: + result = adaptor.validate_python(resp) + except ValidationError as error: + raise exceptions.ApiError(error) from error + return result + + def imprints_list( + self: Session, params: dict[str, str | int] | None = None + ) -> list[BaseResource]: + """Retrieves a list of imprints based on the provided parameters. + + Args: + params: A dictionary containing parameters for filtering imprints (optional). + + Returns: + list[BaseResource]: A list of BaseResource objects representing the retrieved imprints. + + Raises: + ApiError: If there is an error during the API call or validation. + """ + resp = self._get_results(["imprint"], params) + adapter = TypeAdapter(list[BaseResource]) + try: + result = adapter.validate_python(resp["results"]) + except ValidationError as err: + raise exceptions.ApiError(err) from err + return result + def _get_results( self: Session, endpoint: list[str | int], diff --git a/tests/test_arcs.py b/tests/test_arcs.py index 6c2f193..0de1cd7 100644 --- a/tests/test_arcs.py +++ b/tests/test_arcs.py @@ -44,7 +44,7 @@ def test_arcs_list(talker: Session) -> None: assert next(arc_iter).name == "(She) Drunk History" assert next(arc_iter).name == "1+2 = Fantastic Three" assert next(arc_iter).name == "1602" - assert len(arcs) == 1813 + assert len(arcs) == 1817 assert arcs[5].name == "1602" diff --git a/tests/test_characters.py b/tests/test_characters.py index 0dac9d0..90166ec 100644 --- a/tests/test_characters.py +++ b/tests/test_characters.py @@ -60,14 +60,14 @@ def test_character_list(talker: Session) -> None: assert next(character_iter).name == "'Mazing Man" assert next(character_iter).name == "3-D Man (Chandler)" assert next(character_iter).name == "3-D Man (Garrett)" - assert len(chars) == 975 + assert len(chars) == 981 assert chars[2].name == "3-D Man (Garrett)" def test_character_issue_list(talker: Session) -> None: """Test for getting an issue list for an arc.""" issues = talker.character_issues_list(1) - assert len(issues) == 412 + assert len(issues) == 415 assert issues[0].id == 258 assert issues[0].issue_name == "Fantastic Four (1961) #45" assert issues[0].cover_date == date(1965, 12, 1) diff --git a/tests/test_creator.py b/tests/test_creator.py index cf7221c..0a611c4 100644 --- a/tests/test_creator.py +++ b/tests/test_creator.py @@ -44,7 +44,7 @@ def test_creator_list(talker: Session) -> None: assert next(creator_iter).name == "Abel Laxamana" assert next(creator_iter).name == "Adam Freeman" assert next(creator_iter).name == "Adam Schlagman" - assert len(creators) == 377 + assert len(creators) == 379 assert creators[3].name == "Adam Schlagman" diff --git a/tests/test_imprints.py b/tests/test_imprints.py new file mode 100644 index 0000000..c6b6765 --- /dev/null +++ b/tests/test_imprints.py @@ -0,0 +1,71 @@ +"""Test Imprints module. + +This module contains tests for Imprint objects. +""" + +import json + +import pytest +import requests_mock + +from mokkari import exceptions +from mokkari.session import Session + + +def test_known_imprints(talker: Session) -> None: + """Test for a known publisher.""" + vertigo = talker.imprint(1) + assert vertigo.name == "Vertigo Comics" + assert ( + vertigo.image.__str__() + == "https://static.metron.cloud/media/imprint/2024/08/12/vertigo.jpg" + ) + assert vertigo.founded == 1993 + assert vertigo.publisher.name == "DC Comics" + assert ( + vertigo.resource_url.__str__() == "https://metron.cloud/imprint/vertigo-comics/" + ) + + +def test_imprint_list(talker: Session) -> None: + """Test the ImprintList.""" + imprints = talker.imprints_list() + imprints_iter = iter(imprints) + assert next(imprints_iter).name == "DC Black Label" + assert next(imprints_iter).name == "Icon Comics" + assert next(imprints_iter).name == "Vertigo Comics" + assert len(imprints) == 3 + assert imprints[2].name == "Vertigo Comics" + + +def test_bad_imprint(talker: Session) -> None: + """Test for a non-existent imprint.""" + with requests_mock.Mocker() as r: + r.get( + "https://metron.cloud/api/imprint/-1/", + text='{"response_code": 404, "detail": "Not found."}', + ) + with pytest.raises(exceptions.ApiError): + talker.imprint(-1) + + +def test_bad_imprint_validate(talker: Session) -> None: + """Test data with invalid data.""" + # Change the 'name' field to an int, when it should be a string. + data = { + "id": 15, + "name": 150, + "founded": 1993, + "desc": "Foo Bar", + "image": "https://static.metron.cloud/media/imprint/2018/12/02/bongo.png", + "modified": "2019-06-23T15:13:23.581612-04:00", + } + + with requests_mock.Mocker() as r: + r.get( + "https://metron.cloud/api/imprint/15/", + text=json.dumps(data), + ) + + with pytest.raises(exceptions.ApiError): + talker.imprint(15) diff --git a/tests/test_issues.py b/tests/test_issues.py index be4e724..40a9e7a 100644 --- a/tests/test_issues.py +++ b/tests/test_issues.py @@ -14,9 +14,19 @@ from mokkari.session import Session +def test_imprint_issue(talker: Session) -> None: + """Test issue from an imprint.""" + sandman = talker.issue(46182) + assert sandman.imprint.id == 1 + assert sandman.imprint.name == "Vertigo Comics" + + def test_issue_with_rating(talker: Session) -> None: """Test issue with a rating.""" ff = talker.issue(51658) + assert ff.publisher.id == 1 + assert ff.publisher.name == "Marvel" + assert ff.imprint is None assert ff.series.name == "Fantastic Four" assert ff.series.volume == 7 assert ff.rating.id == 3 diff --git a/tests/test_publishers.py b/tests/test_publishers.py index 202ed40..5508ca6 100644 --- a/tests/test_publishers.py +++ b/tests/test_publishers.py @@ -42,7 +42,7 @@ def test_publisher_list(talker: Session) -> None: assert next(publisher_iter).name == "12-Gauge Comics" assert next(publisher_iter).name == "AAA Pop Comics" assert next(publisher_iter).name == "AWA Studios" - assert len(publishers) == 110 + assert len(publishers) == 107 assert publishers[2].name == "AWA Studios" diff --git a/tests/test_series.py b/tests/test_series.py index 652599a..262847a 100644 --- a/tests/test_series.py +++ b/tests/test_series.py @@ -13,6 +13,13 @@ from mokkari.session import Session +def test_series_with_imprint(talker: Session) -> None: + """Test series from an imprint.""" + sandman = talker.series(3315) + assert sandman.imprint.id == 1 + assert sandman.imprint.name == "Vertigo Comics" + + def test_known_series(talker: Session) -> None: """Test for a known series.""" death = talker.series(1) @@ -66,7 +73,7 @@ def test_series_list(talker: Session) -> None: assert next(series_iter).id == 7972 assert next(series_iter).id == 2481 assert next(series_iter).id == 763 - assert len(series) == 245 + assert len(series) == 247 assert series[3].id == 2481 assert series[3].volume == 1 assert series[3].issue_count == 715 diff --git a/tests/test_series_type.py b/tests/test_series_type.py index 367dd71..119b595 100644 --- a/tests/test_series_type.py +++ b/tests/test_series_type.py @@ -11,6 +11,6 @@ def test_series_type_list(talker: Session) -> None: series_types = talker.series_type_list() st_iter = iter(series_types) assert next(st_iter).name == "Annual" - assert next(st_iter).name == "Digital Chapters" - assert series_types[3].name == "Hard Cover" + assert next(st_iter).name == "Digital Chapter" + assert series_types[3].name == "Hardcover" assert len(series_types) == 9 diff --git a/tests/test_teams.py b/tests/test_teams.py index eae2b56..c748066 100644 --- a/tests/test_teams.py +++ b/tests/test_teams.py @@ -35,14 +35,14 @@ def test_team_list(talker: Session) -> None: assert next(team_iter).name == "A-Force" assert next(team_iter).name == "A-Next" assert next(team_iter).name == "A.I.M." - assert len(teams) == 1534 + assert len(teams) == 1545 assert teams[4].name == "A.I.M." def test_team_issue_list(talker: Session) -> None: """Test for getting an issue list for an arc.""" issues = talker.team_issues_list(1) - assert len(issues) == 618 + assert len(issues) == 621 assert issues[0].id == 258 assert issues[0].issue_name == "Fantastic Four (1961) #45" assert issues[0].cover_date == date(1965, 12, 1) diff --git a/tests/testing_mock.sqlite b/tests/testing_mock.sqlite index 8d47264..ecfb375 100644 Binary files a/tests/testing_mock.sqlite and b/tests/testing_mock.sqlite differ