diff --git a/src/palace/manager/api/odl2.py b/src/palace/manager/api/odl2.py index 20631e2a1..5f446c380 100644 --- a/src/palace/manager/api/odl2.py +++ b/src/palace/manager/api/odl2.py @@ -218,13 +218,6 @@ def _extract_publication_metadata( feed, publication, data_source_name ) - if ( - metadata.circulation.licenses_owned == 0 - and metadata.circulation.licenses_available == 0 - ): - # This title is not available, so we don't need to process it. - return metadata - if not publication.licenses: # This title is an open-access title, no need to process licenses. return metadata @@ -234,6 +227,9 @@ def _extract_publication_metadata( medium = None skipped_license_formats = set(self.settings.skipped_license_formats) + publication_availability = self._extract_availability( + publication.metadata.availability + ) for odl_license in publication.licenses: identifier = odl_license.metadata.identifier @@ -263,13 +259,16 @@ def _extract_publication_metadata( if not license_info_document_link: parsed_license = None - elif not self._extract_availability(odl_license.metadata.availability): + elif ( + not self._extract_availability(odl_license.metadata.availability) + or not publication_availability + ): # No need to fetch the license document, we already know that this title is not available. parsed_license = LicenseData( identifier=identifier, checkout_url=None, status_url=license_info_document_link, - status=LicenseStatus.get(odl_license.metadata.availability.state), + status=LicenseStatus.unavailable, checkouts_available=0, ) else: diff --git a/tests/manager/api/test_odl2.py b/tests/manager/api/test_odl2.py index 578311ca8..ed601215f 100644 --- a/tests/manager/api/test_odl2.py +++ b/tests/manager/api/test_odl2.py @@ -1,5 +1,8 @@ +import copy import datetime +import functools import json +import uuid import pytest from freezegun import freeze_time @@ -398,74 +401,134 @@ def test_import_availability( moby_dick_license_dict = feed_json["publications"][0]["licenses"][0] test_book_license_dict = feed_json["publications"][2]["licenses"][0] + huck_finn_publication_dict = feed_json["publications"][1] + huck_finn_publication_dict["licenses"] = copy.deepcopy( + feed_json["publications"][0]["licenses"] + ) + huck_finn_publication_dict["images"] = copy.deepcopy( + feed_json["publications"][0]["images"] + ) + huck_finn_license_dict = huck_finn_publication_dict["licenses"][0] + MOBY_DICK_LICENSE_ID = "urn:uuid:f7847120-fc6f-11e3-8158-56847afe9799" TEST_BOOK_LICENSE_ID = "urn:uuid:f7847120-fc6f-11e3-8158-56847afe9798" + HUCK_FINN_LICENSE_ID = f"urn:uuid:{uuid.uuid4()}" - moby_dick_license_dict["metadata"]["availability"] = { - "state": "unavailable", - } test_book_license_dict["metadata"]["availability"] = { "state": "unavailable", "reason": "https://registry.opds.io/reason#preordered", "until": "2016-01-20T00:00:00Z", } + huck_finn_license_dict["metadata"]["identifier"] = HUCK_FINN_LICENSE_ID + huck_finn_publication_dict["metadata"][ + "title" + ] = "Adventures of Huckleberry Finn" + + # Mock responses from license status server + def license_status_reply( + license_id: str, + concurrency: int = 10, + checkouts: int | None = 30, + expires: str | None = "2016-04-25T12:25:21+02:00", + ) -> LicenseInfoHelper: + return LicenseInfoHelper( + license=LicenseHelper( + identifier=license_id, + concurrency=concurrency, + checkouts=checkouts, + expires=expires, + ), + left=checkouts, + available=concurrency, + ) + + odl_mock_get.add(license_status_reply(MOBY_DICK_LICENSE_ID)) + odl_mock_get.add(license_status_reply(HUCK_FINN_LICENSE_ID)) imported_editions, pools, works, failures = odl2_importer.import_from_feed( json.dumps(feed_json) ) assert isinstance(pools, list) - assert 2 == len(pools) - - [moby_dick_pool, test_book_pool] = pools - - assert moby_dick_pool.identifier.identifier == "978-3-16-148410-0" - assert moby_dick_pool.identifier.type == "ISBN" - assert moby_dick_pool.licenses_owned == 0 - assert moby_dick_pool.licenses_available == 0 - assert len(moby_dick_pool.licenses) == 1 - [moby_dick_license] = moby_dick_pool.licenses - assert moby_dick_license.identifier == MOBY_DICK_LICENSE_ID - assert moby_dick_license.is_available_for_borrowing is False - assert moby_dick_license.status == LicenseStatus.unavailable - - assert test_book_pool.identifier.identifier == "http://example.org/test-book" - assert test_book_pool.identifier.type == "URI" - assert test_book_pool.licenses_owned == 0 - assert test_book_pool.licenses_available == 0 - assert len(test_book_pool.licenses) == 1 - [test_book_license] = test_book_pool.licenses - assert test_book_license.identifier == TEST_BOOK_LICENSE_ID - assert test_book_license.is_available_for_borrowing is False - assert test_book_license.status == LicenseStatus.unavailable + assert 3 == len(pools) + + [moby_dick_pool, huck_finn_pool, test_book_pool] = pools + + def assert_pool( + pool: LicensePool, + identifier: str, + identifier_type: str, + licenses_owned: int, + licenses_available: int, + license_id: str, + available_for_borrowing: bool, + license_status: LicenseStatus, + ) -> None: + assert pool.identifier.identifier == identifier + assert pool.identifier.type == identifier_type + assert pool.licenses_owned == licenses_owned + assert pool.licenses_available == licenses_available + assert len(pool.licenses) == 1 + [license_info] = pool.licenses + assert license_info.identifier == license_id + assert license_info.is_available_for_borrowing is available_for_borrowing + assert license_info.status == license_status + + assert_moby_dick_pool = functools.partial( + assert_pool, + identifier="978-3-16-148410-0", + identifier_type="ISBN", + license_id=MOBY_DICK_LICENSE_ID, + ) + assert_test_book_pool = functools.partial( + assert_pool, + identifier="http://example.org/test-book", + identifier_type="URI", + license_id=TEST_BOOK_LICENSE_ID, + ) + assert_huck_finn_pool = functools.partial( + assert_pool, + identifier="9781234567897", + identifier_type="ISBN", + license_id=HUCK_FINN_LICENSE_ID, + ) + + assert_moby_dick_pool( + moby_dick_pool, + licenses_owned=30, + licenses_available=10, + available_for_borrowing=True, + license_status=LicenseStatus.available, + ) + + assert_test_book_pool( + test_book_pool, + licenses_owned=0, + licenses_available=0, + available_for_borrowing=False, + license_status=LicenseStatus.unavailable, + ) + + assert_huck_finn_pool( + huck_finn_pool, + licenses_owned=30, + licenses_available=10, + available_for_borrowing=True, + license_status=LicenseStatus.available, + ) # Harvest the feed again, but this time the status has changed moby_dick_license_dict["metadata"]["availability"] = { - "state": "available", + "state": "unavailable", } del test_book_license_dict["metadata"]["availability"] + huck_finn_publication_dict["metadata"]["availability"] = { + "state": "unavailable", + } # Mock responses from license status server odl_mock_get.add( - LicenseInfoHelper( - license=LicenseHelper( - identifier=MOBY_DICK_LICENSE_ID, - concurrency=10, - checkouts=30, - expires="2016-04-25T12:25:21+02:00", - ), - left=30, - available=10, - ) - ) - odl_mock_get.add( - LicenseInfoHelper( - license=LicenseHelper( - identifier=TEST_BOOK_LICENSE_ID, - concurrency=10, - ), - available=10, - ) + license_status_reply(TEST_BOOK_LICENSE_ID, checkouts=None, expires=None) ) # Harvest the feed again @@ -474,29 +537,33 @@ def test_import_availability( ) assert isinstance(pools, list) - assert 2 == len(pools) - - [moby_dick_pool, test_book_pool] = pools - - assert moby_dick_pool.identifier.identifier == "978-3-16-148410-0" - assert moby_dick_pool.identifier.type == "ISBN" - assert moby_dick_pool.licenses_owned == 30 - assert moby_dick_pool.licenses_available == 10 - assert len(moby_dick_pool.licenses) == 1 - [moby_dick_license] = moby_dick_pool.licenses - assert moby_dick_license.identifier == MOBY_DICK_LICENSE_ID - assert moby_dick_license.is_available_for_borrowing is True - assert moby_dick_license.status == LicenseStatus.available - - assert test_book_pool.identifier.identifier == "http://example.org/test-book" - assert test_book_pool.identifier.type == "URI" - assert test_book_pool.licenses_owned == 10 - assert test_book_pool.licenses_available == 10 - assert len(test_book_pool.licenses) == 1 - [test_book_license] = test_book_pool.licenses - assert test_book_license.identifier == TEST_BOOK_LICENSE_ID - assert test_book_license.is_available_for_borrowing is True - assert test_book_license.status == LicenseStatus.available + assert 3 == len(pools) + + [moby_dick_pool, huck_finn_pool, test_book_pool] = pools + + assert_moby_dick_pool( + moby_dick_pool, + licenses_owned=0, + licenses_available=0, + available_for_borrowing=False, + license_status=LicenseStatus.unavailable, + ) + + assert_test_book_pool( + test_book_pool, + licenses_owned=10, + licenses_available=10, + available_for_borrowing=True, + license_status=LicenseStatus.available, + ) + + assert_huck_finn_pool( + huck_finn_pool, + licenses_owned=0, + licenses_available=0, + available_for_borrowing=False, + license_status=LicenseStatus.unavailable, + ) class TestODL2API: