app.services.record_relationships¶
Relationship part of the record detail page (read-only).
Builds a RelationshipIndex for one
detail request and converts the record’s view into API-ready values
(outbound/inbound links and errata with titles, display status and
read-time relationship issues).
Design decisions worth recording for later phases:
Candidate participation in relationships. The index is built from the published rows (
list_relationship_summaries()) plus the identity siblings of the record whose detail is requested (list_identity_siblings(), which always includes the queried record itself, published or candidate). Siblings are marked published or candidate, so the domain treats rows of one identity as revisions (never asduplicate_uuid, never as an ambiguous link target). A candidate’s outbound links therefore resolve against published rows and its own identity’s other rows only; links authored by other candidates (of a different identity) are never resolved against anything. This also meansduplicate_uuiddetection for a candidate’s detail view does not see duplicates held only by unrelated candidates. Revisit if a later phase needs candidates to participate more broadly.Cost of the detail endpoint. Phase 09 benchmarking at 20,000 published records measured the naive “fetch every published row” index build at ~780 ms per detail request, of which ~630 ms was spent fetching and converting all 20,000 rows to
RecordSummaryin Python – the Postgres scan itself took ~80 ms (seedocs/developer/performance.rst). Since this one request only ever looks at one record’s view of the index,list_relationship_candidates()fetches only the published rows that can possibly affect that view (sharing the record’s UUID, matching one of its own link targets, or linking back to it) instead of every published row; see its docstring for why the result is exactly the summary set the full fetch would have narrowed the index to for this one record. Looking up the title of every related record is one query per related id (never per catalog row), so it stays bounded by the small number of links a record has, not by catalog size.Which errata belong to a record. Errata are fetched by the record’s UUID (
list_errata_for(), published and candidate rows from every registration) and added to the index with only theircorrectslink. An erratum belongs to the record when that link resolves – with the usual same-project, same-registration, global tiers – to a row of the record’s own identity (the published row, or the newest candidate when nothing is published). So an erratum in another project is not shown when a decision with the same UUID exists closer to it, an ambiguouserratum-forbelongs to no record, and the errata of a decision show on its published row and on its candidate revisions alike. As for other links, candidates of other identities are not link targets.The erratum’s own target. On an erratum’s page the
correctslink is reported asErratumForInfoand dropped from the outbound links.Errata of errata. An erratum never lists errata of its own (its page has no Errata section and a zero summary): errata correct decisions. An erratum whose
erratum-forresolves to another erratum keepserratum_for.resolvedbut gets the read-time warningerratum_target_is_erratum.
Public API: OutboundLinkInfo, InboundLinkInfo,
ErratumForInfo, RecordRelationships,
build_relationships().
- class app.services.record_relationships.ErratumForInfo(*, uuid, id, title, resolved)¶
Bases:
objectThe decision an erratum corrects (its
erratum-for).- Parameters:
- class app.services.record_relationships.InboundLinkInfo(*, link_type, source_id, source_title)¶
Bases:
objectA link from another record to the one being viewed.
- Parameters:
link_type (
InboundType) –updated_byorobsoleted_by.source_id (
UUID) – Id of the linking record.source_title (
str) – Title of the linking record.
- link_type: InboundType¶
- class app.services.record_relationships.OutboundLinkInfo(*, link_type, target_uuid, target_id, target_title, resolution, ambiguous, candidate_ids)¶
Bases:
objectOne outbound link and how it resolved.
- Parameters:
link_type (
LinkType) – The authored relationship.target_uuid (
UUID) – The authored target UUID.target_id (
UUID|None) – Resolved target record id, orNoneif unresolved.resolution (
Resolution|None) – Tier used, orNonewhen unresolved.ambiguous (
bool) – Whether several targets matched equally well.candidate_ids (
tuple[UUID,...]) – Ids of the equally good targets when ambiguous.
- resolution: Resolution | None¶
- class app.services.record_relationships.RecordRelationships(*, outbound_links, inbound_links, errata, erratum_for, status, issues)¶
Bases:
objectRelationship facts of one record, ready for the detail page.
- Parameters:
outbound_links (
tuple[OutboundLinkInfo,...]) – Resolved outbound links, authored order; without an erratum’s owncorrectslink.inbound_links (
tuple[InboundLinkInfo,...]) – Inboundupdated_by/obsoleted_bylinks.errata (
tuple[ErratumRow,...]) – Erratum rows (published first, then candidates) that belong to the record; see the module docstring.erratum_for (
ErratumForInfo|None) – For an erratum, the decision it corrects.status (
DisplayStatus) – Derived display status.issues (
tuple[Issue,...]) – Read-time relationship issues (missing_target,ambiguous_target,duplicate_uuid,erratum_target_is_erratum).
- outbound_links: tuple[OutboundLinkInfo, ...]¶
- inbound_links: tuple[InboundLinkInfo, ...]¶
- errata: tuple[ErratumRow, ...]¶
- erratum_for: ErratumForInfo | None¶
- status: DisplayStatus¶
- app.services.record_relationships.build_relationships(store, record, siblings)¶
Resolve the relationships of one record for its detail page.
- Parameters:
store (
CatalogStore) – Store of the caller’s unit of work.record (
StoredRecord) – The viewed record.siblings (
Sequence[StoredRecord]) – Every row of its identity (including itself).
- Return type:
- Returns:
Links, errata, status and read-time issues.