From 9bd4a80ba182c574c27580a70495338adfe7b8b4 Mon Sep 17 00:00:00 2001 From: Michael Benowitz Date: Tue, 7 Nov 2023 09:06:20 -0500 Subject: [PATCH 1/3] SMA-523 Add since/until params for open sources materials In order to allow libraries to set an expected expiration date for open source materials this adds `opds:since` and `opds:until` to the availability object for these materials. This leverages functionality that is already used for loans to create a "loan-like" behavior for these materials (when desired). This should have no side effects since it's leveraging existing attributes but this should be tested to be true. --- api/opds.py | 4 +++- core/opds.py | 14 ++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/api/opds.py b/api/opds.py index eb7c6fb4f..0f893e510 100644 --- a/api/opds.py +++ b/api/opds.py @@ -1,4 +1,5 @@ import urllib.request, urllib.parse, urllib.error +from datetime import timedelta, datetime import copy import logging from flask import url_for @@ -425,6 +426,7 @@ def open_access_link(self, pool, lpdm): always_available = OPDSFeed.makeelement( "{%s}availability" % OPDSFeed.OPDS_NS, status="available" ) + link_tag.append(always_available) return link_tag @@ -1130,7 +1132,7 @@ def fulfill_link(self, license_pool, active_loan, delivery_mechanism, active_loan=active_loan ) - children = AcquisitionFeed.license_tags(license_pool, active_loan, None) + children = AcquisitionFeed.license_tags(license_pool, active_loan, None, rel=rel, library=self.library) link_tag.extend(children) children = self.drm_device_registration_tags( diff --git a/core/opds.py b/core/opds.py index 9dec1c0eb..d842d2ae8 100644 --- a/core/opds.py +++ b/core/opds.py @@ -1650,7 +1650,7 @@ def indirect_acquisition(cls, indirect_types): return top_level_parent @classmethod - def license_tags(cls, license_pool, loan, hold): + def license_tags(cls, license_pool, loan, hold, rel=None, library=None): # Generate a list of licensing tags. These should be inserted # into a tag. tags = [] @@ -1688,10 +1688,16 @@ def license_tags(cls, license_pool, loan, hold): else: status = 'reserved' since = hold.start - elif (license_pool.open_access or license_pool.unlimited_access or license_pool.self_hosted or ( + elif (license_pool.open_access or rel == OPDSFeed.OPEN_ACCESS_REL): + status = 'available' + + default_loan_period = collection.default_loan_period(library) if library else collection.STANDARD_DEFAULT_LOAN_PERIOD + + since = license_pool.availability_time + until = datetime.datetime.utcnow() + datetime.timedelta(days=default_loan_period) + elif (license_pool.unlimited_access or license_pool.self_hosted or ( license_pool.licenses_available > 0 and - license_pool.licenses_owned > 0) - ): + license_pool.licenses_owned > 0)): status = 'available' else: status='unavailable' From cea05a4328da5f836aa5c55986e3e94cfaf68fa5 Mon Sep 17 00:00:00 2001 From: Michael Benowitz Date: Mon, 13 Nov 2023 17:50:16 -0500 Subject: [PATCH 2/3] SMA-523 Add unit test --- core/tests/test_opds.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/core/tests/test_opds.py b/core/tests/test_opds.py index 5cbaa3be2..4c5455eec 100644 --- a/core/tests/test_opds.py +++ b/core/tests/test_opds.py @@ -1625,6 +1625,33 @@ def test_license_tags_show_self_hosted_books(self): assert 'status' in tags[0].attrib assert 'available' == tags[0].attrib['status'] + def test_license_tags_open_access(self): + # Arrange + edition, pool = self._edition(with_license_pool=True) + pool.open_access = True + pool.self_hosted = False + pool.unlimited_access = False + creation_time = datetime.datetime.utcnow() + + # Act + tags = AcquisitionFeed.license_tags( + pool, None, None + ) + + # Assert + assert 1 == len(tags) + + [tag] = tags + + assert ('status' in tag.attrib) == True + assert 'available' == tag.attrib['status'] + assert 'since' in tag.attrib + assert tag.attrib['since'] == pool.availability_time.strftime('%Y-%m-%dT%H:%M:%S+00:00') + assert 'until' in tag.attrib + assert tag.attrib['until'] == (creation_time + datetime.timedelta(days=21)).strftime('%Y-%m-%dT%H:%M:%SZ') + assert ('holds' in tag.attrib) == False + assert ('copies' in tag.attrib) == False + def test_single_entry(self): # Here's a Work with two LicensePools. From b5796925a0c47533b55687935a3d30fecfdecd57 Mon Sep 17 00:00:00 2001 From: Michael Benowitz Date: Thu, 16 Nov 2023 16:52:35 -0500 Subject: [PATCH 3/3] SMA-523 Remove unused import --- api/opds.py | 1 - 1 file changed, 1 deletion(-) diff --git a/api/opds.py b/api/opds.py index 0f893e510..9261217a6 100644 --- a/api/opds.py +++ b/api/opds.py @@ -1,5 +1,4 @@ import urllib.request, urllib.parse, urllib.error -from datetime import timedelta, datetime import copy import logging from flask import url_for