Skip to content
This repository has been archived by the owner on Nov 21, 2024. It is now read-only.

Commit

Permalink
Update OPDS2 to handle both time tracking and availability
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathangreen committed Jun 13, 2024
1 parent 7b6877b commit 6970ea8
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 41 deletions.
10 changes: 0 additions & 10 deletions src/webpub_manifest_parser/odl/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
Collection,
CollectionList,
Node,
PresentationMetadata,
)
from webpub_manifest_parser.core.properties import (
ArrayOfStringsProperty,
Expand Down Expand Up @@ -114,18 +113,9 @@ def __hash__(self):
return hash((self.metadata, self.links))


class ODLPublicationMetadata(PresentationMetadata):
availability = TypeProperty(
"availability", required=False, nested_type=OPDS2AvailabilityInformation
)


class ODLPublication(OPDS2Publication):
"""ODL publication."""

metadata = TypeProperty(
key="metadata", required=True, nested_type=ODLPublicationMetadata
)
links = ArrayOfLinksProperty(key="links", required=False)
licenses = ArrayOfCollectionsProperty(
"licenses",
Expand Down
20 changes: 18 additions & 2 deletions src/webpub_manifest_parser/opds2/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
)
from webpub_manifest_parser.core.properties import (
ArrayProperty,
BooleanProperty,
DateOrTimeProperty,
DateTimeProperty,
EnumProperty,
Expand Down Expand Up @@ -347,21 +348,36 @@ def __init__(
self.number_of_items = number_of_items


class OPDS2PublicationMetadata(PresentationMetadata):
# OPDS2 Removal proposed property. See here for more detail:
# https://github.com/opds-community/drafts/discussions/63
availability = TypeProperty(
"availability", required=False, nested_type=OPDS2AvailabilityInformation
)
# Palace OPDS extension to indicate that the publication supports time tracking
time_tracking = BooleanProperty(
"http://palaceproject.io/terms/timeTracking", False, default_value=False
)


class OPDS2Publication(Collection):
"""OPDS 2.0 publication."""

images = CompactCollectionProperty(
"images", required=True, role=OPDS2CollectionRolesRegistry.IMAGES
)
metadata = TypeProperty(
key="metadata", required=True, nested_type=OPDS2PublicationMetadata
)

def __init__(self, metadata=None, links=None, images=None):
"""Initialize a new instance of OPDS2Publication class."""
super().__init__()

if metadata and not isinstance(metadata, PresentationMetadata):
if metadata and not isinstance(metadata, OPDS2PublicationMetadata):
raise ValueError(
"Argument 'metadata' must be an instance of {}".format(
PresentationMetadata
OPDS2PublicationMetadata
)
)
if links and not isinstance(links, LinkList):
Expand Down
7 changes: 7 additions & 0 deletions tests/files/opds2/feed.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
"urn:isbn:978-3-16-148410-0"
],
"language": "en",
"availability": {
"state": "unavailable",
"reason": "https://example.com/subscription/removal",
"detail": "This publication is no longer available in your subscription",
"since": "2019-09-29T01:03:02Z"
},
"http://palaceproject.io/terms/timeTracking": true,
"published": "2015-09-29T00:00:00Z",
"modified": "2015-09-29T17:00:00Z",
"subject": [
Expand Down
9 changes: 5 additions & 4 deletions tests/webpub_manifest_parser/core/test_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
OPDS2Group,
OPDS2Navigation,
OPDS2Publication,
OPDS2PublicationMetadata,
)
from webpub_manifest_parser.rwpm import (
RWPMCollectionRolesRegistry,
Expand Down Expand Up @@ -136,7 +137,7 @@ def check_analyzer_errors(
publications=CollectionList(
[
OPDS2Publication(
metadata=PresentationMetadata(title="Publication 1"),
metadata=OPDS2PublicationMetadata(title="Publication 1"),
links=LinkList(
[
Link(
Expand Down Expand Up @@ -167,7 +168,7 @@ def check_analyzer_errors(
publications=CollectionList(
[
OPDS2Publication(
metadata=PresentationMetadata(title="Publication 1.1"),
metadata=OPDS2PublicationMetadata(title="Publication 1.1"),
links=LinkList(
[
Link(
Expand Down Expand Up @@ -199,7 +200,7 @@ def check_analyzer_errors(
publications=CollectionList(
[
ODLPublication(
metadata=PresentationMetadata(title="Publication 1"),
metadata=OPDS2PublicationMetadata(title="Publication 1"),
links=LinkList([Link(href="http://example.com")]),
licenses=CollectionList(
[
Expand All @@ -215,7 +216,7 @@ def check_analyzer_errors(
),
),
ODLPublication(
metadata=PresentationMetadata(title="Publication 1"),
metadata=OPDS2PublicationMetadata(title="Publication 1"),
links=LinkList([Link(href="http://example.com")]),
licenses=CollectionList(
[
Expand Down
4 changes: 2 additions & 2 deletions tests/webpub_manifest_parser/odl/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

from webpub_manifest_parser.core import ManifestParserResult
from webpub_manifest_parser.odl import ODLFeedParserFactory
from webpub_manifest_parser.odl.ast import ODLPublicationMetadata
from webpub_manifest_parser.opds2.ast import (
OPDS2AvailabilityInformation,
OPDS2AvailabilityType,
OPDS2FeedMetadata,
OPDS2PublicationMetadata,
)
from webpub_manifest_parser.opds2.registry import OPDS2LinkRelationsRegistry
from webpub_manifest_parser.utils import first_or_default
Expand Down Expand Up @@ -39,7 +39,7 @@ def test(self):
self.assertEqual(1, len(feed.publications))
[publication] = feed.publications

self.assertIsInstance(publication.metadata, ODLPublicationMetadata)
self.assertIsInstance(publication.metadata, OPDS2PublicationMetadata)
self.assertIsInstance(
publication.metadata.availability, OPDS2AvailabilityInformation
)
Expand Down
28 changes: 16 additions & 12 deletions tests/webpub_manifest_parser/odl/test_semantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@
from parameterized import parameterized

from tests.webpub_manifest_parser.core.test_analyzer import AnalyzerTest
from webpub_manifest_parser.core.ast import (
CollectionList,
Link,
LinkList,
PresentationMetadata,
)
from webpub_manifest_parser.core.ast import CollectionList, Link, LinkList
from webpub_manifest_parser.core.registry import LinkRelationsRegistry
from webpub_manifest_parser.core.semantic import (
MANIFEST_MISSING_SELF_LINK_ERROR,
Expand Down Expand Up @@ -40,6 +35,7 @@
OPDS2FeedMetadata,
OPDS2Group,
OPDS2Navigation,
OPDS2PublicationMetadata,
)


Expand Down Expand Up @@ -141,7 +137,9 @@ class ODLSemanticAnalyzerTest(AnalyzerTest):
publications=CollectionList(
[
ODLPublication(
metadata=PresentationMetadata(title="Publication 1"),
metadata=OPDS2PublicationMetadata(
title="Publication 1"
),
licenses=CollectionList(),
)
]
Expand All @@ -150,7 +148,7 @@ class ODLSemanticAnalyzerTest(AnalyzerTest):
[
ODL_PUBLICATION_MUST_CONTAIN_EITHER_LICENSES_OR_OA_ACQUISITION_LINK_ERROR(
node=ODLPublication(
metadata=PresentationMetadata(title="Publication 1")
metadata=OPDS2PublicationMetadata(title="Publication 1")
),
node_property=None,
)
Expand All @@ -171,7 +169,9 @@ class ODLSemanticAnalyzerTest(AnalyzerTest):
publications=CollectionList(
[
ODLPublication(
metadata=PresentationMetadata(title="Publication 1"),
metadata=OPDS2PublicationMetadata(
title="Publication 1"
),
links=LinkList([Link(href="http://example.com")]),
)
]
Expand All @@ -180,7 +180,7 @@ class ODLSemanticAnalyzerTest(AnalyzerTest):
[
ODL_PUBLICATION_MUST_CONTAIN_EITHER_LICENSES_OR_OA_ACQUISITION_LINK_ERROR(
node=ODLPublication(
metadata=PresentationMetadata(title="Publication 1")
metadata=OPDS2PublicationMetadata(title="Publication 1")
),
node_property=None,
)
Expand All @@ -201,7 +201,9 @@ class ODLSemanticAnalyzerTest(AnalyzerTest):
publications=CollectionList(
[
ODLPublication(
metadata=PresentationMetadata(title="Publication 1"),
metadata=OPDS2PublicationMetadata(
title="Publication 1"
),
links=LinkList(
[
Link(
Expand Down Expand Up @@ -233,7 +235,9 @@ class ODLSemanticAnalyzerTest(AnalyzerTest):
publications=CollectionList(
[
ODLPublication(
metadata=PresentationMetadata(title="Publication 1"),
metadata=OPDS2PublicationMetadata(
title="Publication 1"
),
links=LinkList([Link(href="http://example.com")]),
licenses=CollectionList(
[
Expand Down
26 changes: 25 additions & 1 deletion tests/webpub_manifest_parser/opds2/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
OPDS2Feed,
OPDS2FeedMetadata,
OPDS2LinkProperties,
OPDS2PublicationMetadata,
)
from webpub_manifest_parser.opds2.registry import (
OPDS2LinkRelationsRegistry,
Expand Down Expand Up @@ -61,7 +62,7 @@ def test(self):
self.assertEqual(2, len(feed.publications))
publication = feed.publications[0]

self.assertIsInstance(publication.metadata, PresentationMetadata)
self.assertIsInstance(publication.metadata, OPDS2PublicationMetadata)
self.assertEqual("http://schema.org/Book", publication.metadata.type)
self.assertEqual("Moby-Dick", publication.metadata.title)
self.assertEqual(
Expand All @@ -78,6 +79,26 @@ def test(self):
datetime.datetime(2015, 9, 29, 17, 0, tzinfo=tzutc()),
publication.metadata.modified,
)
self.assertIsInstance(
publication.metadata.availability, OPDS2AvailabilityInformation
)
self.assertEqual(
publication.metadata.availability.state,
OPDS2AvailabilityType.UNAVAILABLE.value,
)
self.assertEqual(
publication.metadata.availability.since,
datetime.datetime(2019, 9, 29, 1, 3, 2, tzinfo=tzutc()),
)
self.assertEqual(
publication.metadata.availability.detail,
"This publication is no longer available in your subscription",
)
self.assertEqual(
publication.metadata.availability.reason,
"https://example.com/subscription/removal",
)
self.assertIs(publication.metadata.time_tracking, True)

self.assertIsInstance(publication.links, list)
self.assertEqual(len(publication.links), 2)
Expand Down Expand Up @@ -163,6 +184,9 @@ def test(self):
publication.metadata.modified,
)

self.assertIs(publication.metadata.availability, None)
self.assertIs(publication.metadata.time_tracking, False)

self.assertIsInstance(publication.links, list)

publication_acquisition_link = first_or_default(
Expand Down
18 changes: 8 additions & 10 deletions tests/webpub_manifest_parser/opds2/test_semantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,15 @@
from parameterized import parameterized

from tests.webpub_manifest_parser.core.test_analyzer import AnalyzerTest
from webpub_manifest_parser.core.ast import (
CollectionList,
Link,
LinkList,
PresentationMetadata,
)
from webpub_manifest_parser.core.ast import CollectionList, Link, LinkList
from webpub_manifest_parser.core.semantic import SemanticAnalyzerError
from webpub_manifest_parser.opds2.ast import (
OPDS2Feed,
OPDS2FeedMetadata,
OPDS2Group,
OPDS2Navigation,
OPDS2Publication,
OPDS2PublicationMetadata,
)
from webpub_manifest_parser.opds2.registry import (
OPDS2CollectionRolesRegistry,
Expand Down Expand Up @@ -91,7 +87,9 @@ class OPDS2SemanticAnalyzerTest(AnalyzerTest):
publications=CollectionList(
[
OPDS2Publication(
metadata=PresentationMetadata(title="Publication 1"),
metadata=OPDS2PublicationMetadata(
title="Publication 1"
),
links=LinkList([Link(href="http://example.com")]),
)
]
Expand Down Expand Up @@ -127,7 +125,7 @@ class OPDS2SemanticAnalyzerTest(AnalyzerTest):
publications=CollectionList(
[
OPDS2Publication(
metadata=PresentationMetadata(
metadata=OPDS2PublicationMetadata(
title="Publication 1"
),
links=LinkList(
Expand Down Expand Up @@ -192,7 +190,7 @@ def test_semantic_analyzer_does_correctly_processes_valid_ast(self):
publications=CollectionList(
[
OPDS2Publication(
metadata=PresentationMetadata(title="Publication 1"),
metadata=OPDS2PublicationMetadata(title="Publication 1"),
links=LinkList(
[
Link(
Expand Down Expand Up @@ -223,7 +221,7 @@ def test_semantic_analyzer_does_correctly_processes_valid_ast(self):
publications=CollectionList(
[
OPDS2Publication(
metadata=PresentationMetadata(
metadata=OPDS2PublicationMetadata(
title="Publication 1.1"
),
links=LinkList(
Expand Down

0 comments on commit 6970ea8

Please sign in to comment.