Add first-class FacetData type for OPDS feed facet groups (PP-3675)#3109
Add first-class FacetData type for OPDS feed facet groups (PP-3675)#3109jonathangreen merged 5 commits intomainfrom
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3109 +/- ##
=======================================
Coverage 93.23% 93.24%
=======================================
Files 492 492
Lines 45429 45404 -25
Branches 6250 6245 -5
=======================================
- Hits 42357 42335 -22
+ Misses 1984 1983 -1
+ Partials 1088 1086 -2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
1f55f84 to
c5155bb
Compare
Introduce a FacetData dataclass that groups facet links by their facet group, moving group-level metadata (group name, type) out of individual Link objects. This eliminates redundant per-link fields (facet_group, facet_group_type, active_sort) and removes the need to reconstruct groups at serialization time. The OPDS facet rel and active-sort attribute are now applied by the OPDS1 serializer where they belong.
3c0c247 to
67ea9a6
Compare
- Use dataclasses.replace() in _serialize_facet_link to preserve all Link fields instead of manually copying a subset - Remove unused group_name parameter from _entrypoint_link - Move active_sort into V2_ATTRIBUTE_MAPPING for consistency - Fold _serialize_sort_links into _serialize_facet_links, removing the separate abstract method since sort is just another facet group
Detect the sort facet group by its constant key rather than by display title string comparison. Remove unused rel fields from facet link test data since serializers inject the correct rel.
Replace the href fallback for missing facet link titles with an error log identifying it as a programming error, since all facet links should always have a title set. Move the minimum-links check after link filtering so title-less links are excluded from the count.
V1 historically serialized sort facets as regular facets without a facetGroupType attribute. The FacetData refactor inadvertently added this attribute to V1 sort links. Clear the type for sort groups in V1's _serialize_facet_links to retain the old behavior.
| feed.add_link(start_url, rel="start", title=top_level_title) | ||
|
|
||
| # Add facet links for visibility filtering | ||
| facet_group_data = FacetData(group=SuppressedFacets.VISIBILITY_FACET_GROUP_NAME) |
There was a problem hiding this comment.
This works, but there is some duplication here, that I want to resolve. I'm going to handle this by doing a bit of a refactor in a follow up PR. So I kept the changes here minimal for now.
Edit: PR here: #3119
| ) | ||
| element = self._serialize_link(sort_link) | ||
| if link.active_facet: | ||
| element.set(self._attr_name("active_sort"), "true") |
There was a problem hiding this comment.
Minor - The PR description mentioned that active_sort was extracted into an ACTIVE_SORT_ATTR constant. Although, looking through at least the changes on this PR, it looks like this is the only occurrence left.
There was a problem hiding this comment.
Yah, I changed this up after I opened the PR and forgot to update the description. I think whats here is more consistent with the existing code.
) (#3119) ## Description Refactors `SuppressedFacets` to extend `BaseFacets` and use the standard `FacetGroup` NamedTuple, replacing the custom `FacetGroup` dataclass that was specific to the suppressed feed. This brings the suppressed feed's faceting into alignment with the rest of the system's facet infrastructure. Key changes: - `SuppressedFacets` now extends `BaseFacets` and implements its `facet_groups` / `items` protocol - Removed the custom `FacetGroup` dataclass in favor of the standard `FacetGroup` from `feed.facets.base` - Added `AdminSuppressedFeed.facet_links` override to handle the suppressed feed's custom title scheme (via `VisibilityFilter.display_title`) while using the standard facet link machinery - Added `AdminSuppressedAnnotator.facet_url` to bridge the standard `facet_url(facets)` interface with the existing `suppressed_url(**kwargs)` method - Used `Self` return types and `self.__class__()` for subclass-friendly construction ## Motivation and Context The suppressed feed previously had its own parallel `FacetGroup` type with different field names (`group_name` / `filter_value` vs the standard `group` / `value`), making the codebase harder to reason about. This PR eliminates that duplication so there is one `FacetGroup` type across the system. Stacked on #3109. ## How Has This Been Tested? - All existing tests pass - Added focused unit tests for the new `facet_links` and `facet_url` methods, including edge case coverage for empty URLs - mypy passes cleanly ## Checklist - [x] I have updated the documentation accordingly. - [x] All new and existing tests passed.
Description
Introduce a
FacetDatadataclass that groups facet links by their facet group, moving group-level metadata (group name, type) out of individualLinkobjects and into a dedicated container. This eliminates redundant per-link fields (facet_group,facet_group_type,active_sort) and removes the need to reconstruct groups at serialization time (e.g. viadefaultdictin the OPDS2 serializer).Key changes:
FacetDatatype infeed/types.pywithgroup,type, andlinksfieldsFeedData.facet_links: list[Link]replaced byFeedData.facets: list[FacetData]acquisition.pyat construction time, not at serialization timehttp://opds-spec.org/facet) moved from data layer into OPDS1 serializer where it belongsactive-sortattribute extracted into anACTIVE_SORT_ATTRconstantis_sort_facet(),palace_active_sortproperty,active_sortlink fieldfacetsarray (with@type) instead of as top-level feed linksOPDS2 Breaking Changes
Sort facets moved from top-level
links[]tofacets[]Previously, sort options were serialized as individual top-level links with
rel: "http://palaceproject.io/terms/rel/sort"and apalace_active_sortproperty. They now appear as a facet group inside thefacets[]array, identified by"@type": "http://palaceproject.io/terms/rel/sort"in the facet metadata. The active sort option is indicated by"rel": "self"on its link (consistent with how all other active facets are marked).Removed
palace_active_sortlink propertyThe
http://palaceproject.io/terms/properties/active-sortproperty has been removed from OPDS2 link properties. Active sort/facet state is now uniformly communicated via"rel": "self"on the active link within a facet group.Impact
These changes only affect the OPDS2 feed format. The only current OPDS2 consumer is Aspen, which does not use sort or entrypoint facet links, so these changes should have no practical impact. OPDS1 v1 and v2 feeds are unaffected by the structural changes (sort links and facet attributes are serialized as before).
Motivation and Context
The previous design stored group-level metadata (group name, group type) redundantly on every individual facet
Link. This meant serializers had to reconstruct the grouping by inspecting link attributes, and format-specific concerns (like the OPDS1 facet rel) leaked into the shared data layer. The newFacetDatatype models the one-to-many relationship between a facet group and its links directly, making the code cleaner and less error-prone.How Has This Been Tested?
FacetDatastructureChecklist