From 9034045e9ae692bb6b59b56a2d60c758f3ae6e85 Mon Sep 17 00:00:00 2001 From: kempersc Date: Fri, 16 Jan 2026 14:33:06 +0100 Subject: [PATCH] feat: update manifest.json timestamp and refactor Custodian and PersonObservation schemas with new slots for entity status and observation source documents --- .../schemas/20251121/linkml/manifest.json | 2 +- schemas/20251121/linkml/manifest.json | 2 +- .../linkml/modules/classes/CateringPlace.yaml | 12 +- .../modules/classes/ConservationLab.yaml | 10 +- .../linkml/modules/classes/Custodian.yaml | 1 - .../modules/classes/CustodianLegalStatus.yaml | 8 +- .../modules/classes/EducationCenter.yaml | 10 +- .../modules/classes/ExhibitionSpace.yaml | 10 +- .../linkml/modules/classes/GiftShop.yaml | 14 +- .../classes/IntangibleHeritageForm.yaml | 8 +- .../modules/classes/PersonObservation.yaml | 7 + .../modules/classes/ProvenanceEvent.yaml | 37 ++--- .../linkml/modules/classes/ReadingRoom.yaml | 10 +- .../modules/classes/ResearchCenter.yaml | 10 +- .../linkml/modules/classes/Storage.yaml | 13 +- .../linkml/modules/classes/StorageUnit.yaml | 13 +- .../modules/classes/TemporaryLocation.yaml | 12 +- .../linkml/modules/classes/TransferEvent.yaml | 10 +- .../modules/classes/TransferPolicy.yaml | 6 +- .../linkml/modules/classes/TypeClass.yaml | 61 +++++++ .../linkml/modules/classes/VideoSubtitle.yaml | 8 +- .../modules/classes/VideoTranscript.yaml | 15 +- .../linkml/modules/classes/Warehouse.yaml | 15 +- .../slots/has_or_had_entity_status.yaml | 64 ++++++++ .../modules/slots/has_or_had_hypernym.yaml | 8 +- ...as_or_had_observation_source_document.yaml | 67 ++++++++ .../has_or_had_structured_description.yaml | 62 +++++++ .../slots/has_or_had_subtitle_format.yaml | 64 ++++++++ .../slots/has_or_had_transcript_format.yaml | 64 ++++++++ .../linkml/modules/slots/has_or_had_type.yaml | 42 +++-- src/glam_extractor/api/entity_review.py | 152 +++++++++++++++++- 31 files changed, 677 insertions(+), 140 deletions(-) create mode 100644 schemas/20251121/linkml/modules/classes/TypeClass.yaml create mode 100644 schemas/20251121/linkml/modules/slots/has_or_had_entity_status.yaml create mode 100644 schemas/20251121/linkml/modules/slots/has_or_had_observation_source_document.yaml create mode 100644 schemas/20251121/linkml/modules/slots/has_or_had_structured_description.yaml create mode 100644 schemas/20251121/linkml/modules/slots/has_or_had_subtitle_format.yaml create mode 100644 schemas/20251121/linkml/modules/slots/has_or_had_transcript_format.yaml diff --git a/frontend/public/schemas/20251121/linkml/manifest.json b/frontend/public/schemas/20251121/linkml/manifest.json index 52df58f8d2..2dccfb07a1 100644 --- a/frontend/public/schemas/20251121/linkml/manifest.json +++ b/frontend/public/schemas/20251121/linkml/manifest.json @@ -1,5 +1,5 @@ { - "generated": "2026-01-16T12:27:43.019Z", + "generated": "2026-01-16T12:29:25.004Z", "schemaRoot": "/schemas/20251121/linkml", "totalFiles": 3007, "categoryCounts": { diff --git a/schemas/20251121/linkml/manifest.json b/schemas/20251121/linkml/manifest.json index 2dccfb07a1..6d8415b821 100644 --- a/schemas/20251121/linkml/manifest.json +++ b/schemas/20251121/linkml/manifest.json @@ -1,5 +1,5 @@ { - "generated": "2026-01-16T12:29:25.004Z", + "generated": "2026-01-16T13:33:06.596Z", "schemaRoot": "/schemas/20251121/linkml", "totalFiles": 3007, "categoryCounts": { diff --git a/schemas/20251121/linkml/modules/classes/CateringPlace.yaml b/schemas/20251121/linkml/modules/classes/CateringPlace.yaml index ff0ac8915a..6aec8ec831 100644 --- a/schemas/20251121/linkml/modules/classes/CateringPlace.yaml +++ b/schemas/20251121/linkml/modules/classes/CateringPlace.yaml @@ -11,7 +11,7 @@ imports: # Shared slots (replacing catering_place_* slots per Rule 53) - ../slots/has_or_had_identifier - ../slots/has_or_had_label - - ../slots/has_or_had_description + - ../slots/has_or_had_structured_description - ./Label - ./Description # Domain-specific slots (kept) @@ -82,7 +82,7 @@ classes: - wd:Q30022 slots: - has_or_had_accessibility_feature - - has_or_had_description # was: catering_place_description - migrated per Rule 53 + - has_or_had_structured_description # was: catering_place_description - migrated per Rule 53 - has_or_had_identifier # was: catering_place_id - migrated per Rule 53 - has_or_had_label # was: catering_place_name - migrated per Rule 53 - catering_price_range @@ -128,7 +128,7 @@ classes: - value: label_text: Van Gogh Museum Café description: Museum café - has_or_had_description: # was: catering_place_description - migrated per Rule 53 + has_or_had_structured_description: # was: catering_place_description - migrated per Rule 53 range: Description inlined: true description: A description of the catering place. @@ -261,7 +261,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/rijksmuseum-restaurant has_or_had_label: label_text: RIJKS Restaurant - has_or_had_description: + has_or_had_structured_description: description_text: Michelin-starred restaurant serving modern Dutch cuisine. Located in museum atrium with garden views. catering_type: RESTAURANT cuisine_type: Modern Dutch fine dining @@ -280,7 +280,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/na-cafe has_or_had_label: label_text: Nationaal Archief Café - has_or_had_description: + has_or_had_structured_description: description_text: Casual café for archive visitors. Light lunches, coffee, and pastries. catering_type: CAFE cuisine_type: Café fare, sandwiches, soups @@ -296,7 +296,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/cafe-americain has_or_had_label: label_text: Café Americain - has_or_had_description: + has_or_had_structured_description: description_text: Historic art deco café dating from 1902. Literary landmark and protected monument. catering_type: HISTORIC_CAFE heritage_type_classification: HISTORIC_RESTAURANT diff --git a/schemas/20251121/linkml/modules/classes/ConservationLab.yaml b/schemas/20251121/linkml/modules/classes/ConservationLab.yaml index 18c74ce448..a3fc5777ce 100644 --- a/schemas/20251121/linkml/modules/classes/ConservationLab.yaml +++ b/schemas/20251121/linkml/modules/classes/ConservationLab.yaml @@ -19,7 +19,7 @@ imports: # MIGRATED 2026-01-15: lab_* slots replaced with shared slots per Rule 53 - ../slots/has_or_had_identifier # was: lab_id - ../slots/has_or_had_label # was: lab_name - - ../slots/has_or_had_description # was: lab_description + - ../slots/has_or_had_structured_description # was: lab_description - ./Label - ./Description - ../slots/safety_certification @@ -90,7 +90,7 @@ classes: # MIGRATED 2026-01-15: lab_* slots replaced with shared slots per Rule 53 - has_or_had_identifier # was: lab_id - has_or_had_label # was: lab_name - - has_or_had_description # was: lab_description + - has_or_had_structured_description # was: lab_description - safety_certification - specificity_annotation - staff_count @@ -126,7 +126,7 @@ classes: - value: label_text: KB Preservation Laboratory description: Library preservation lab - has_or_had_description: # was: lab_description + has_or_had_structured_description: # was: lab_description range: Description inlined: true description: >- @@ -233,7 +233,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/rijksmuseum-conservation has_or_had_label: label_text: Rijksmuseum Conservation Studio - has_or_had_description: + has_or_had_structured_description: description_text: State-of-the-art conservation studio specializing in Dutch Golden Age paintings, works on paper, and decorative arts. conservation_specialization: - Paintings @@ -258,7 +258,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/na-restauratie has_or_had_label: label_text: Nationaal Archief Restauratie Atelier - has_or_had_description: + has_or_had_structured_description: description_text: Paper and parchment conservation workshop serving the national archives. Specializes in historical documents, maps, and seals. conservation_specialization: - Paper diff --git a/schemas/20251121/linkml/modules/classes/Custodian.yaml b/schemas/20251121/linkml/modules/classes/Custodian.yaml index bdf0674255..345d8c2c12 100644 --- a/schemas/20251121/linkml/modules/classes/Custodian.yaml +++ b/schemas/20251121/linkml/modules/classes/Custodian.yaml @@ -221,7 +221,6 @@ classes: range: string required: false legal_status: - range: uriorcurie required: false place_designation: range: CustodianPlace diff --git a/schemas/20251121/linkml/modules/classes/CustodianLegalStatus.yaml b/schemas/20251121/linkml/modules/classes/CustodianLegalStatus.yaml index 4914c06379..25f6d247b7 100644 --- a/schemas/20251121/linkml/modules/classes/CustodianLegalStatus.yaml +++ b/schemas/20251121/linkml/modules/classes/CustodianLegalStatus.yaml @@ -48,7 +48,7 @@ imports: - ../slots/dissolution_date - ../slots/temporal_extent - ../slots/is_or_was_suborganization_of # was: parent_custodian - migrated per Rule 53 (2025-01-15) - - ../slots/legal_status + - ../slots/has_or_had_entity_status - ../slots/governance_structure - ../slots/reconstruction_method - ../slots/is_or_was_derived_from # was: was_derived_from - migrated per Rule 53 @@ -117,7 +117,7 @@ classes: - legal_form - legal_jurisdiction - legal_name - - legal_status + - has_or_had_entity_status - is_or_was_suborganization_of # was: parent_custodian - migrated per Rule 53 (2025-01-15) - primary_register - reconstruction_method @@ -231,7 +231,7 @@ classes: description: | Parent organization in hierarchical structure. MIGRATED from parent_custodian slot per slot_fixes.yaml (Rule 53, 2025-01-15). - legal_status: + has_or_had_entity_status: range: LegalStatus required: true examples: @@ -353,7 +353,7 @@ classes: alpha_2: NL alpha_3: NLD legal_system_type: CIVIL_LAW - legal_status: + has_or_had_entity_status: status_code: ACTIVE status_name: Active is_or_was_derived_from: # was: was_derived_from - migrated per Rule 53 diff --git a/schemas/20251121/linkml/modules/classes/EducationCenter.yaml b/schemas/20251121/linkml/modules/classes/EducationCenter.yaml index a40f3e055c..b405a0da56 100644 --- a/schemas/20251121/linkml/modules/classes/EducationCenter.yaml +++ b/schemas/20251121/linkml/modules/classes/EducationCenter.yaml @@ -16,7 +16,7 @@ imports: # Shared slots (replacing education_center_* slots per Rule 53) - ../slots/has_or_had_identifier - ../slots/has_or_had_label - - ../slots/has_or_had_description + - ../slots/has_or_had_structured_description - ./Label - ./Description # Domain-specific slots (kept) @@ -95,7 +95,7 @@ classes: # MIGRATED 2026-01-15: education_center_* slots replaced with shared slots per Rule 53 - has_or_had_identifier # was: education_center_id - has_or_had_label # was: education_center_name - - has_or_had_description # was: education_center_description + - has_or_had_structured_description # was: education_center_description - education_contact_email - education_type_classification - has_av_equipment @@ -135,7 +135,7 @@ classes: - value: | label_text: KB Workshops & Trainingen description: Library education facility - has_or_had_description: + has_or_had_structured_description: range: Description inlined: true description: A description of the education center. @@ -274,7 +274,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/rijksmuseum-education has_or_had_label: label_text: Rijksmuseum Educatie Centrum - has_or_had_description: + has_or_had_structured_description: description_text: Dedicated education facility offering school programs, family workshops, and teacher training. education_type_classification: EDUCATION_CENTER serves_or_served: # was: target_audience - migrated per Rule 53 @@ -306,7 +306,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/na-leercentrum has_or_had_label: label_text: Nationaal Archief Leercentrum - has_or_had_description: + has_or_had_structured_description: description_text: Learning center focused on historical research skills and genealogy. education_type_classification: RESOURCE_CENTER serves_or_served: # was: target_audience - migrated per Rule 53 diff --git a/schemas/20251121/linkml/modules/classes/ExhibitionSpace.yaml b/schemas/20251121/linkml/modules/classes/ExhibitionSpace.yaml index d0087bed79..7d5b4eed06 100644 --- a/schemas/20251121/linkml/modules/classes/ExhibitionSpace.yaml +++ b/schemas/20251121/linkml/modules/classes/ExhibitionSpace.yaml @@ -12,7 +12,7 @@ imports: # Shared slots (replacing exhibition_space_* slots per Rule 53) - ../slots/has_or_had_identifier - ../slots/has_or_had_label - - ../slots/has_or_had_description + - ../slots/has_or_had_structured_description - ./Label - ./Description # Domain-specific slots (kept) @@ -89,7 +89,7 @@ classes: # MIGRATED 2026-01-15: exhibition_space_* slots replaced with shared slots per Rule 53 - has_or_had_identifier # was: exhibition_space_id - has_or_had_label # was: exhibition_space_name - - has_or_had_description # was: exhibition_space_description + - has_or_had_structured_description # was: exhibition_space_description - exhibition_type - gallery_type_classification - has_climate_control @@ -128,7 +128,7 @@ classes: - value: | label_text: Van Gogh Museum Mesdag Collection description: Partner venue exhibition - has_or_had_description: + has_or_had_structured_description: range: Description inlined: true description: A description of the exhibition space. @@ -239,7 +239,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/rijksmuseum-schiphol-gallery has_or_had_label: label_text: Rijksmuseum Schiphol - has_or_had_description: + has_or_had_structured_description: description_text: Free gallery at Schiphol Airport featuring rotating highlights from the Rijksmuseum collection. exhibition_type: SATELLITE_GALLERY museum_type_classification: ART_MUSEUM @@ -259,7 +259,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/stedelijk-project-space has_or_had_label: label_text: Stedelijk Museum Bureau Amsterdam - has_or_had_description: + has_or_had_structured_description: description_text: Project space for emerging contemporary artists and experimental exhibitions. exhibition_type: PROJECT_SPACE gallery_type_classification: PROJECT_SPACE diff --git a/schemas/20251121/linkml/modules/classes/GiftShop.yaml b/schemas/20251121/linkml/modules/classes/GiftShop.yaml index 8b0a71f8d7..4b3a346ed3 100644 --- a/schemas/20251121/linkml/modules/classes/GiftShop.yaml +++ b/schemas/20251121/linkml/modules/classes/GiftShop.yaml @@ -27,7 +27,7 @@ imports: # MIGRATED 2026-01-15: shop_* slots replaced with shared slots per Rule 53 - ../slots/has_or_had_identifier # was: shop_id - ../slots/has_or_had_label # was: shop_name - - ../slots/has_or_had_description # was: shop_description + - ../slots/has_or_had_structured_description # was: shop_description - ./Label - ./Description - ../slots/shop_type @@ -86,7 +86,7 @@ classes: \ - Temporary retail for special exhibition\n - Exhibition catalog, themed merchandise\n\n**Example - Rijksmuseum\ \ Gift Shop**:\n```yaml\nCustodian:\n hc_id: \"https://nde.nl/ontology/hc/nl-nh-ams-m-rm-q190804\"\n preferred_label:\ \ \"Rijksmuseum\"\n gift_shop:\n - has_or_had_identifier: \"https://nde.nl/ontology/hc/gift-shop/rijksmuseum-shop\" # was: shop_id\n has_or_had_label: # was: shop_name\n label_text:\ - \ \"Rijksmuseum Shop\"\n shop_type: MUSEUM_SHOP\n has_or_had_description: # was: shop_description\n description_text: |\n Award-winning museum shop offering\ + \ \"Rijksmuseum Shop\"\n shop_type: MUSEUM_SHOP\n has_or_had_structured_description: # was: shop_description\n description_text: |\n Award-winning museum shop offering\ \ reproductions, design objects,\n books, and exclusive Rijksmuseum merchandise.\n physical_location:\n\ \ - place_name: \"Rijksmuseum Shop - Main Hall\"\n auxiliary_place_type: RETAIL_SPACE\n street_address:\ \ \"Museumstraat 1, Amsterdam\"\n online_shop:\n - platform_name: \"Rijksmuseum Online Shop\"\n \ @@ -122,7 +122,7 @@ classes: # MIGRATED 2026-01-15: shop_* slots replaced with shared slots per Rule 53 - has_or_had_identifier # was: shop_id - has_or_had_label # was: shop_name - - has_or_had_description # was: shop_description + - has_or_had_structured_description # was: shop_description - shop_type - specificity_annotation - square_meters @@ -155,7 +155,7 @@ classes: - value: label_text: British Library Bookshop description: Library bookshop name - has_or_had_description: # was: shop_description + has_or_had_structured_description: # was: shop_description range: Description inlined: true description: A description of the gift shop. @@ -334,7 +334,7 @@ classes: has_or_had_label: # was: shop_name label_text: Rijksmuseum Shop shop_type: MUSEUM_SHOP - has_or_had_description: # was: shop_description + has_or_had_structured_description: # was: shop_description description_text: Award-winning museum shop offering reproductions, design objects, books, and exclusive Rijksmuseum merchandise. Located in the redesigned entrance hall. physical_location: - place_name: Rijksmuseum Shop - Main Hall @@ -377,7 +377,7 @@ classes: has_or_had_label: # was: shop_name label_text: British Library Shop shop_type: BOOKSHOP - has_or_had_description: # was: shop_description + has_or_had_structured_description: # was: shop_description description_text: Specialist bookshop focusing on rare book facsimiles, literary merchandise, and British Library publications. physical_location: - place_name: British Library Shop @@ -410,7 +410,7 @@ classes: has_or_had_label: # was: shop_name label_text: Vermeer Exhibition Pop-up Shop shop_type: POP_UP - has_or_had_description: # was: shop_description + has_or_had_structured_description: # was: shop_description description_text: Temporary retail for the 2023 Vermeer exhibition with exclusive exhibition merchandise and catalog. physical_location: - place_name: Vermeer Exhibition Shop diff --git a/schemas/20251121/linkml/modules/classes/IntangibleHeritageForm.yaml b/schemas/20251121/linkml/modules/classes/IntangibleHeritageForm.yaml index 9815b7a859..cb08ca5da8 100644 --- a/schemas/20251121/linkml/modules/classes/IntangibleHeritageForm.yaml +++ b/schemas/20251121/linkml/modules/classes/IntangibleHeritageForm.yaml @@ -21,7 +21,7 @@ imports: # MIGRATED 2026-01-15: heritage_form_* slots replaced with shared slots per Rule 53 - ../slots/has_or_had_identifier # was: heritage_form_id - ../slots/has_or_had_label # was: heritage_form_name - - ../slots/has_or_had_description # was: heritage_form_description + - ../slots/has_or_had_structured_description # was: heritage_form_description - ./Label - ./Description - ../slots/kien_registration_date @@ -139,7 +139,7 @@ classes: - external_link - geographic_scope # MIGRATED 2026-01-15: heritage_form_* slots replaced with shared slots per Rule 53 - - has_or_had_description # was: heritage_form_description + - has_or_had_structured_description # was: heritage_form_description - has_or_had_identifier # was: heritage_form_id - has_or_had_label # was: heritage_form_name - kien_registration_date @@ -197,7 +197,7 @@ classes: label_text: 1 aprilviering Brielle - value: label_text: Bloemencorso Bollenstreek - has_or_had_description: # was: heritage_form_description + has_or_had_structured_description: # was: heritage_form_description required: false range: Description inlined: true @@ -388,7 +388,7 @@ classes: label: "Pride Amsterdam" has_or_had_label: label_text: Pride Amsterdam - has_or_had_description: + has_or_had_structured_description: description_text: "Annual LGBTQ+ celebration featuring the Canal Parade through Amsterdam's historic canals. First held in 1996, it represents Dutch values of tolerance, equality, and freedom." # unesco_domain - MIGRATED to is_or_was_categorized_as (2026-01-14, Rule 53) is_or_was_categorized_as: diff --git a/schemas/20251121/linkml/modules/classes/PersonObservation.yaml b/schemas/20251121/linkml/modules/classes/PersonObservation.yaml index 6e3b3e3142..bfcd2a44cf 100644 --- a/schemas/20251121/linkml/modules/classes/PersonObservation.yaml +++ b/schemas/20251121/linkml/modules/classes/PersonObservation.yaml @@ -42,6 +42,7 @@ imports: - ../slots/role_start_date - ../slots/role_end_date - ../slots/observation_source + - ../slots/has_or_had_observation_source_document - ../slots/is_or_was_affected_by_event - ../slots/contact_email - ../slots/expertise_area @@ -126,6 +127,7 @@ classes: - linkedin_profile_url - modified - observation_source + - has_or_had_observation_source_document - occupation - person_name - pronoun @@ -246,8 +248,13 @@ classes: range: date required: false observation_source: + range: string + required: false + description: Simple text reference to source (use has_or_had_observation_source_document for structured data) + has_or_had_observation_source_document: range: SourceDocument required: false + inlined: true is_or_was_affected_by_event: range: OrganizationalChangeEvent required: false diff --git a/schemas/20251121/linkml/modules/classes/ProvenanceEvent.yaml b/schemas/20251121/linkml/modules/classes/ProvenanceEvent.yaml index 8a817022f0..5366c900af 100644 --- a/schemas/20251121/linkml/modules/classes/ProvenanceEvent.yaml +++ b/schemas/20251121/linkml/modules/classes/ProvenanceEvent.yaml @@ -36,7 +36,7 @@ imports: - ../slots/event_timespan - ../slots/footnote - ../slots/from_owner - # REMOVED 2026-01-15: from_owner_text - migrated to has_or_had_description (Rule 53, symmetry with to_owner_text) + # REMOVED 2026-01-15: from_owner_text - migrated to has_or_had_structured_description (Rule 53, symmetry with to_owner_text) - ../slots/lot_number - ../slots/nazi_era_flag - ../slots/price_text @@ -44,8 +44,7 @@ imports: - ../slots/specificity_annotation - ../slots/template_specificity - ../slots/to_owner - - ../slots/has_or_had_description # was: to_owner_text - migrated per Rule 53 (2026-01-15) - - ./Description # for has_or_had_description range + - ../slots/has_or_had_structured_description # was: to_owner_text - migrated per Rule 53 (2026-01-15) # REMOVED 2026-01-15: transfer_location, transfer_location_text - migrated to event_location (Rule 53) - ../slots/event_location - ./SpecificityAnnotation @@ -96,7 +95,7 @@ classes: - event_type - footnote - from_owner - # MIGRATED 2026-01-15: from_owner_text → has_or_had_description (Rule 53, symmetry with to_owner_text) + # MIGRATED 2026-01-15: from_owner_text → has_or_had_structured_description (Rule 53, symmetry with to_owner_text) - lot_number - nazi_era_flag - object_ref @@ -108,7 +107,7 @@ classes: - specificity_annotation - template_specificity - to_owner - - has_or_had_description # was: to_owner_text - migrated per Rule 53 (2026-01-15) + - has_or_had_structured_description # was: to_owner_text - migrated per Rule 53 (2026-01-15) # MIGRATED 2026-01-15: transfer_location, transfer_location_text → event_location (Rule 53) - event_location slot_usage: @@ -169,7 +168,7 @@ classes: inlined: false examples: - value: https://nde.nl/ontology/hc/custodian/nl/mauritshuis - has_or_had_description: # was: to_owner_text, from_owner_text - migrated per Rule 53 (2026-01-15) + has_or_had_structured_description: # was: to_owner_text, from_owner_text - migrated per Rule 53 (2026-01-15) description: | Owner (source or destination) described as text when no structured entity exists. MIGRATED from to_owner_text and from_owner_text per slot_fixes.yaml (Rule 53, 2026-01-15). @@ -178,9 +177,7 @@ classes: - "from_owner": Previous owner (source of transfer) - "to_owner": New owner (destination of transfer) required: false - range: Description multivalued: true - inlined: true examples: # from_owner examples (migrated from from_owner_text) - value: @@ -361,8 +358,8 @@ classes: event_timespan: begin_of_the_begin: '1664-01-01' end_of_the_end: '1667-12-31' - # MIGRATED 2026-01-15: to_owner_text, from_owner_text → has_or_had_description (Rule 53) - has_or_had_description: + # MIGRATED 2026-01-15: to_owner_text, from_owner_text → has_or_had_structured_description (Rule 53) + has_or_had_structured_description: - description_text: Johannes Vermeer, Delft description_type: to_owner # MIGRATED 2026-01-15: transfer_location_text → event_location (Rule 53) @@ -377,8 +374,8 @@ classes: object_ref: https://nde.nl/ontology/hc/object/mauritshuis-girl-pearl-earring event_type: PURCHASE event_date_text: c. 1665-1674 - # MIGRATED 2026-01-15: from_owner_text, to_owner_text → has_or_had_description (Rule 53) - has_or_had_description: + # MIGRATED 2026-01-15: from_owner_text, to_owner_text → has_or_had_structured_description (Rule 53) + has_or_had_structured_description: - description_text: Johannes Vermeer description_type: from_owner - description_text: Pieter van Ruijven, Delft (c. 1665-1674) @@ -395,8 +392,8 @@ classes: event_type: AUCTION event_date: '1696-05-16' event_date_text: May 16, 1696 - # MIGRATED 2026-01-15: from_owner_text, to_owner_text → has_or_had_description (Rule 53) - has_or_had_description: + # MIGRATED 2026-01-15: from_owner_text, to_owner_text → has_or_had_structured_description (Rule 53) + has_or_had_structured_description: - description_text: Estate of Jacob Dissius description_type: from_owner - description_text: Unknown buyer @@ -421,8 +418,8 @@ classes: event_type: PURCHASE event_date: '1881-01-01' event_date_text: '1881' - # MIGRATED 2026-01-15: from_owner_text, to_owner_text → has_or_had_description (Rule 53) - has_or_had_description: + # MIGRATED 2026-01-15: from_owner_text, to_owner_text → has_or_had_structured_description (Rule 53) + has_or_had_structured_description: - description_text: Unknown seller description_type: from_owner - description_text: A.A. des Tombe, The Hague @@ -442,8 +439,8 @@ classes: event_type: BEQUEST event_date: '1903-01-01' event_date_text: '1903' - # MIGRATED 2026-01-15: from_owner_text → has_or_had_description (Rule 53) - has_or_had_description: + # MIGRATED 2026-01-15: from_owner_text → has_or_had_structured_description (Rule 53) + has_or_had_structured_description: - description_text: A.A. des Tombe (d. 1903) description_type: from_owner - description_text: Mauritshuis, The Hague @@ -460,8 +457,8 @@ classes: object_ref: https://nde.nl/ontology/hc/object/example-painting event_type: CONFISCATION event_date_text: '1938' - # MIGRATED 2026-01-15: from_owner_text, to_owner_text → has_or_had_description (Rule 53) - has_or_had_description: + # MIGRATED 2026-01-15: from_owner_text, to_owner_text → has_or_had_structured_description (Rule 53) + has_or_had_structured_description: - description_text: Jewish collector, Vienna description_type: from_owner - description_text: Nazi authorities diff --git a/schemas/20251121/linkml/modules/classes/ReadingRoom.yaml b/schemas/20251121/linkml/modules/classes/ReadingRoom.yaml index d9277c9c5a..c4fc1927e2 100644 --- a/schemas/20251121/linkml/modules/classes/ReadingRoom.yaml +++ b/schemas/20251121/linkml/modules/classes/ReadingRoom.yaml @@ -10,7 +10,7 @@ imports: # MIGRATED 2026-01-15: reading_room_* slots replaced with shared slots per Rule 53 - ../slots/has_or_had_identifier # was: reading_room_id - ../slots/has_or_had_label # was: reading_room_name - - ../slots/has_or_had_description # was: reading_room_description + - ../slots/has_or_had_structured_description # was: reading_room_description - ./Label - ./Description - ../slots/reading_room_type @@ -88,7 +88,7 @@ classes: - has_wifi - opening_hour # MIGRATED 2026-01-15: reading_room_* slots replaced with shared slots per Rule 53 - - has_or_had_description # was: reading_room_description + - has_or_had_structured_description # was: reading_room_description - has_or_had_identifier # was: reading_room_id - has_or_had_label # was: reading_room_name - reading_room_type @@ -126,7 +126,7 @@ classes: - value: label_text: Stadsarchief Amsterdam Studiezaal description: City archive reading room - has_or_had_description: # was: reading_room_description + has_or_had_structured_description: # was: reading_room_description range: Description inlined: true description: A description of the reading room. @@ -253,7 +253,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/na-studiezaal has_or_had_label: label_text: Nationaal Archief Studiezaal - has_or_had_description: + has_or_had_structured_description: description_text: Main research room for consulting archival collections. Self-service retrieval from open stacks. Staff assistance available. reading_room_type: GENERAL seating_capacity: 80 @@ -277,7 +277,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/kb-bijzondere-collecties has_or_had_label: label_text: KB Bijzondere Collecties Leeszaal - has_or_had_description: + has_or_had_structured_description: description_text: Special collections reading room for rare books, manuscripts, and incunabula. Supervised handling required. reading_room_type: SPECIAL_COLLECTIONS seating_capacity: 20 diff --git a/schemas/20251121/linkml/modules/classes/ResearchCenter.yaml b/schemas/20251121/linkml/modules/classes/ResearchCenter.yaml index dd91717a60..56168b43a0 100644 --- a/schemas/20251121/linkml/modules/classes/ResearchCenter.yaml +++ b/schemas/20251121/linkml/modules/classes/ResearchCenter.yaml @@ -20,7 +20,7 @@ imports: # MIGRATED 2026-01-15: research_center_* slots replaced with shared slots per Rule 53 - ../slots/has_or_had_identifier # was: research_center_id - ../slots/has_or_had_label # was: research_center_name - - ../slots/has_or_had_description # was: research_center_description + - ../slots/has_or_had_structured_description # was: research_center_description - ./Label - ./Description - ../slots/research_center_type @@ -89,7 +89,7 @@ classes: - major_research_project - publication_series_name # MIGRATED 2026-01-15: research_center_* slots replaced with shared slots per Rule 53 - - has_or_had_description # was: research_center_description + - has_or_had_structured_description # was: research_center_description - has_or_had_identifier # was: research_center_id - has_or_had_label # was: research_center_name - research_center_type @@ -124,7 +124,7 @@ classes: - value: label_text: NIOD Institute for War, Holocaust and Genocide Studies description: Specialized research institute - has_or_had_description: # was: research_center_description + has_or_had_structured_description: # was: research_center_description range: Description inlined: true description: A description of the research center. @@ -236,7 +236,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/rijksmuseum-research has_or_had_label: label_text: Rijksmuseum Research Department - has_or_had_description: + has_or_had_structured_description: description_text: Scholarly research on Dutch art and history, with focus on Golden Age. Publishes Rijksmuseum Bulletin and monograph series. research_center_type: RESEARCH_DEPARTMENT research_focus_area: @@ -269,7 +269,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/kb-dh-lab has_or_had_label: label_text: KB Lab - Digital Humanities - has_or_had_description: + has_or_had_structured_description: description_text: Digital humanities research facility focusing on computational approaches to library collections. research_center_type: DIGITAL_HUMANITIES_CENTER research_focus_area: diff --git a/schemas/20251121/linkml/modules/classes/Storage.yaml b/schemas/20251121/linkml/modules/classes/Storage.yaml index e09eb0554a..a8c121ae56 100644 --- a/schemas/20251121/linkml/modules/classes/Storage.yaml +++ b/schemas/20251121/linkml/modules/classes/Storage.yaml @@ -50,8 +50,7 @@ imports: - ../slots/specificity_annotation - ../slots/standards_applied - ../slots/has_or_had_storage_condition - - ../slots/has_or_had_description # was: storage_description - migrated per Rule 53 - - ./Description + - ../slots/has_or_had_structured_description # was: storage_description - migrated per Rule 53 (uses Description class) - ../slots/has_or_had_type - ../slots/has_or_had_storage_unit - ../slots/has_or_had_stores_collection @@ -126,7 +125,7 @@ classes: - specificity_annotation - standards_applied - has_or_had_storage_condition - - has_or_had_description # was: storage_description - migrated per Rule 53 + - has_or_had_structured_description # was: storage_description - migrated per Rule 53 (uses Description class) - has_or_had_identifier # was: storage_id - migrated per Rule 53 - is_or_was_stored_at # was: storage_location - migrated per Rule 53 - has_or_had_label # was: storage_name - migrated per Rule 53 @@ -177,13 +176,11 @@ classes: description: Cold storage for film and photographic materials - value: ART_STORAGE description: Climate-controlled art storage - has_or_had_description: # was: storage_description - migrated per Rule 53 + has_or_had_structured_description: # was: storage_description - migrated per Rule 53 (uses Description class) description: | Description of this storage facility. MIGRATED from storage_description per slot_fixes.yaml (Rule 53). Uses Description class with text and language support. - range: Description - inlined: true multivalued: true examples: - value: | @@ -309,7 +306,7 @@ classes: label_text: Depot Amersfoort language: nl has_or_had_type: ART_STORAGE - has_or_had_description: + has_or_had_structured_description: - description_text: 'Off-site storage depot for Rijksmuseum overflow collections. Climate-controlled facility housing paintings, sculptures, and decorative arts not currently on display.' description_type: storage language: en @@ -331,7 +328,7 @@ classes: label_text: Depot B - Cold Storage language: en has_or_had_type: COLD_STORAGE - has_or_had_description: + has_or_had_structured_description: - description_text: 'Refrigerated vault for film negatives, photographic materials, and temperature-sensitive documents. Maintained at 4°C, 35% RH.' description_type: storage language: en diff --git a/schemas/20251121/linkml/modules/classes/StorageUnit.yaml b/schemas/20251121/linkml/modules/classes/StorageUnit.yaml index 3b23ddfcba..0b13190f53 100644 --- a/schemas/20251121/linkml/modules/classes/StorageUnit.yaml +++ b/schemas/20251121/linkml/modules/classes/StorageUnit.yaml @@ -40,8 +40,7 @@ imports: - ../slots/stores_or_stored # was: stores_object - migrated per Rule 53 (2026-01-15); range now HeritageObject - ./HeritageObject # Added 2026-01-15 for stores_or_stored range - ../slots/template_specificity - - ../slots/has_or_had_description # was: unit_description - migrated per Rule 53 - - ./Description + - ../slots/has_or_had_structured_description # was: unit_description - migrated per Rule 53 (uses Description class) # REMOVED - migrated to has_or_had_identifier (2026-01-14, Rule 53) # - ../slots/unit_id # - ../slots/unit_identifier @@ -104,7 +103,7 @@ classes: - specificity_annotation - stores_or_stored # was: stores_object - migrated per Rule 53 (2026-01-15) - template_specificity - - has_or_had_description # was: unit_description - migrated per Rule 53 + - has_or_had_structured_description # was: unit_description - migrated per Rule 53 (uses Description class) # REMOVED - migrated to has_or_had_identifier (2026-01-14, Rule 53) # - unit_id # - unit_identifier @@ -136,13 +135,11 @@ classes: unit_type: range: StorageUnitTypeEnum required: true - has_or_had_description: # was: unit_description - migrated per Rule 53 + has_or_had_structured_description: # was: unit_description - migrated per Rule 53 (uses Description class) description: | Description of this storage unit. MIGRATED from unit_description per slot_fixes.yaml (Rule 53). Uses Description class with text and language support. - range: Description - inlined: true multivalued: true row_number: range: string @@ -225,7 +222,7 @@ classes: unit_identifier: NA-2024-BOX-00145 unit_name: Archive Box 145 - WWII Ministry Records unit_type: ARCHIVE_BOX - has_or_had_description: + has_or_had_structured_description: - description_text: 'Acid-free archive box containing Ministry of Defense correspondence from 1940-1945. Handle with care.' description_type: unit language: en @@ -242,7 +239,7 @@ classes: unit_identifier: FF-MAPS-042 unit_name: Flat File Drawer 42 - Netherlands Maps unit_type: FLAT_FILE_DRAWER - has_or_had_description: + has_or_had_structured_description: - description_text: 'Flat file drawer containing oversized maps of the Netherlands, 1850-1920. Climate-controlled environment.' description_type: unit language: en diff --git a/schemas/20251121/linkml/modules/classes/TemporaryLocation.yaml b/schemas/20251121/linkml/modules/classes/TemporaryLocation.yaml index bffe82ae08..96c20cb01a 100644 --- a/schemas/20251121/linkml/modules/classes/TemporaryLocation.yaml +++ b/schemas/20251121/linkml/modules/classes/TemporaryLocation.yaml @@ -17,7 +17,7 @@ imports: # temp_location_reason → has_or_had_rationale + reason_type (enum) - ../slots/has_or_had_identifier - ../slots/has_or_had_label - - ../slots/has_or_had_description + - ../slots/has_or_had_structured_description - ../slots/has_or_had_rationale - ../slots/has_or_had_type - ../slots/is_active @@ -84,7 +84,7 @@ classes: # temp_location_* slots REMOVED - migrated to generic slots (Rule 53, 2026-01-15) - has_or_had_identifier - has_or_had_label - - has_or_had_description + - has_or_had_structured_description - has_or_had_rationale - has_or_had_type - is_active @@ -124,7 +124,7 @@ classes: description: Traveling exhibition - value: Emergency Collection Storage - Watersnood 2024 description: Emergency relocation - has_or_had_description: + has_or_had_structured_description: range: Description inlined: true description: Detailed description of the temporary location. @@ -229,7 +229,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/stedelijk-temp-2020 has_or_had_label: - Stedelijk Museum Temporary Entrance - has_or_had_description: + has_or_had_structured_description: - description_text: Temporary entrance during main entrance renovation. Access via garden entrance. description_type: location language: en @@ -252,7 +252,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/rijksmuseum-popup-groningen has_or_had_label: - Rijksmuseum Pop-up Groningen - has_or_had_description: + has_or_had_structured_description: - description_text: Summer pop-up exhibition in Groninger Forum featuring highlights from the Golden Age collection. description_type: location language: en @@ -272,7 +272,7 @@ classes: has_or_had_identifier: https://nde.nl/ontology/hc/aux/emergency-storage-2024 has_or_had_label: - Emergency Collection Storage - Watersnood 2024 - has_or_had_description: + has_or_had_structured_description: - description_text: Emergency relocation of collection materials following flooding at main depot. description_type: location language: en diff --git a/schemas/20251121/linkml/modules/classes/TransferEvent.yaml b/schemas/20251121/linkml/modules/classes/TransferEvent.yaml index c38606ed0c..b2473cdbaa 100644 --- a/schemas/20251121/linkml/modules/classes/TransferEvent.yaml +++ b/schemas/20251121/linkml/modules/classes/TransferEvent.yaml @@ -30,7 +30,7 @@ imports: - ../slots/temporal_extent - ../slots/starts_or_started_at_location - ../slots/ends_or_ended_at_location - - ../slots/has_or_had_description + - ../slots/has_or_had_structured_description - ../slots/has_or_had_policy - ../slots/specificity_annotation - ../slots/template_specificity @@ -64,7 +64,7 @@ classes: - `starts_or_started_at_location`: Origin location - `ends_or_ended_at_location`: Destination location - `has_or_had_policy`: Transfer policy governing the transfer - - `has_or_had_description`: Narrative description + - `has_or_had_structured_description`: Narrative description **Replaces** (per slot_fixes.yaml): - `transfer_to_collection_date` (simple date) @@ -86,7 +86,7 @@ classes: - temporal_extent - starts_or_started_at_location - ends_or_ended_at_location - - has_or_had_description + - has_or_had_structured_description - has_or_had_policy - specificity_annotation - template_specificity @@ -110,7 +110,7 @@ classes: range: Location required: false inlined: true - has_or_had_description: + has_or_had_structured_description: description: | Narrative description of the transfer event. range: Description @@ -139,6 +139,6 @@ classes: location_name: "Old Storage Facility" ends_or_ended_at_location: location_name: "New Archive Building" - has_or_had_description: + has_or_had_structured_description: description_text: "Transfer of historical photographs to new climate-controlled facility" description: "Collection relocation transfer" diff --git a/schemas/20251121/linkml/modules/classes/TransferPolicy.yaml b/schemas/20251121/linkml/modules/classes/TransferPolicy.yaml index 1ece8c4d41..bfd9af0a56 100644 --- a/schemas/20251121/linkml/modules/classes/TransferPolicy.yaml +++ b/schemas/20251121/linkml/modules/classes/TransferPolicy.yaml @@ -24,7 +24,7 @@ imports: - ../metadata - ../slots/policy_name - ../slots/policy_text - - ../slots/has_or_had_description + - ../slots/has_or_had_structured_description - ../slots/specificity_annotation - ../slots/template_specificity - ./Description @@ -65,7 +65,7 @@ classes: slots: - policy_name - policy_text - - has_or_had_description + - has_or_had_structured_description - specificity_annotation - template_specificity @@ -80,7 +80,7 @@ classes: Full text of the policy. range: string required: false - has_or_had_description: + has_or_had_structured_description: description: | Summary description of the policy. range: Description diff --git a/schemas/20251121/linkml/modules/classes/TypeClass.yaml b/schemas/20251121/linkml/modules/classes/TypeClass.yaml new file mode 100644 index 0000000000..cf67f13d3c --- /dev/null +++ b/schemas/20251121/linkml/modules/classes/TypeClass.yaml @@ -0,0 +1,61 @@ +# TypeClass - Abstract base class for all type classification classes +# +# This class serves as the common ancestor for all *Type classes in the heritage ontology. +# It enables the `any_of` pattern in generic slots like `has_or_had_type` to accept +# both string values and class instances, resolving OWL "Ambiguous type" warnings. +# +# Rule compliance: +# - Rule 53: Enables generic slots via any_of pattern +# - Rule 39: Supports RiC-O temporal naming convention +# - Rule 0b: Part of Type/Types naming convention +# +# Generation date: 2026-01-16 +# Purpose: Resolve 18 OWL ambiguous type warnings from gen-owl + +id: https://nde.nl/ontology/hc/class/TypeClass +name: type_class +title: Type Class (Abstract Base) + +prefixes: + linkml: https://w3id.org/linkml/ + hc: https://nde.nl/ontology/hc/ + skos: http://www.w3.org/2004/02/skos/core# + +default_prefix: hc + +imports: + - linkml:types + +classes: + TypeClass: + class_uri: skos:Concept + abstract: true + description: | + Abstract base class for all type classification classes in the heritage ontology. + + **Purpose**: + Enables the `any_of` pattern in generic slots (e.g., `has_or_had_type`) to accept + both string literals and class instances. This resolves OWL "Ambiguous type" + warnings that occur when slots have `range: string` but classes override to + class types via `slot_usage`. + + **Inheritance**: + All concrete Type classes (AddressType, BackupType, ConditionType, etc.) + should inherit from this class using `is_a: TypeClass`. + + **Ontological Alignment**: + - class_uri: `skos:Concept` - Type classes represent controlled vocabulary concepts + + **See Also**: + - Rule 0b: LinkML Type/Types File Naming Convention + - Rule 53: No bespoke slots - generic/reusable patterns + + annotations: + specificity_score: 0.10 + specificity_rationale: | + Very low specificity - this is a foundational abstract class used as the + base for all type classification classes across the entire ontology. + owl_fix_date: "2026-01-16" + owl_fix_purpose: | + Created to resolve 18 "Ambiguous type" warnings from gen-owl. + Serves as range in any_of patterns for generic slots. diff --git a/schemas/20251121/linkml/modules/classes/VideoSubtitle.yaml b/schemas/20251121/linkml/modules/classes/VideoSubtitle.yaml index 99cd17d66a..6563f39663 100644 --- a/schemas/20251121/linkml/modules/classes/VideoSubtitle.yaml +++ b/schemas/20251121/linkml/modules/classes/VideoSubtitle.yaml @@ -16,7 +16,7 @@ imports: - ../slots/is_sdh - ../slots/raw_subtitle_content - ../slots/specificity_annotation - - ../slots/has_or_had_format # was: subtitle_format - migrated per Rule 53 (2026-01-15) + - ../slots/has_or_had_subtitle_format # was: has_or_had_format - specialized for SubtitleFormatEnum (2026-01-16) - ../slots/template_specificity - ../slots/has_or_had_identifier # MIGRATED: was ../slots/track_id (2026-01-14) - ./TrackIdentifier # Added for has_or_had_identifier migration @@ -241,7 +241,7 @@ classes: - is_sdh - raw_subtitle_content - specificity_annotation - - has_or_had_format # was: subtitle_format - migrated per Rule 53 (2026-01-15) + - has_or_had_subtitle_format # was: has_or_had_format - specialized for SubtitleFormatEnum (2026-01-16) - template_specificity - has_or_had_identifier # MIGRATED: was track_id (2026-01-14) - has_or_had_label # was: track_name @@ -250,11 +250,11 @@ classes: required: true includes_timestamp: ifabsent: 'true' - has_or_had_format: # was: subtitle_format - migrated per Rule 53 (2026-01-15) + has_or_had_subtitle_format: # was: has_or_had_format - specialized for SubtitleFormatEnum (2026-01-16) description: | The subtitle format for this video subtitle track. Migrated from subtitle_format to align with RiC-O naming conventions. - range: SubtitleFormatEnum + # range: SubtitleFormatEnum - defined in slot, no override needed required: true examples: - value: VTT diff --git a/schemas/20251121/linkml/modules/classes/VideoTranscript.yaml b/schemas/20251121/linkml/modules/classes/VideoTranscript.yaml index f639edbe56..6d6bf557ef 100644 --- a/schemas/20251121/linkml/modules/classes/VideoTranscript.yaml +++ b/schemas/20251121/linkml/modules/classes/VideoTranscript.yaml @@ -16,9 +16,8 @@ imports: - ../slots/speaker_count - ../slots/specificity_annotation - ../slots/template_specificity - # REMOVED 2026-01-14: ../slots/transcript_format - migrated to has_or_had_format with TranscriptFormat - - ../slots/has_or_had_format - - ./TranscriptFormat + # REMOVED 2026-01-14: ../slots/transcript_format - migrated to has_or_had_transcript_format with TranscriptFormat + - ../slots/has_or_had_transcript_format - ./SpecificityAnnotation - ./TemplateSpecificityScores - ../enums/TranscriptFormatEnum @@ -82,8 +81,8 @@ classes: - speaker_count - specificity_annotation - template_specificity - # REMOVED 2026-01-14: transcript_format - migrated to has_or_had_format with TranscriptFormat - - has_or_had_format + # REMOVED 2026-01-14: transcript_format - migrated to has_or_had_transcript_format with TranscriptFormat + - has_or_had_transcript_format slot_usage: full_text: range: string @@ -105,7 +104,7 @@ classes: ' description: Transcript with speaker labels - # REMOVED 2026-01-14: transcript_format - migrated to has_or_had_format with TranscriptFormat + # REMOVED 2026-01-14: transcript_format - migrated to has_or_had_transcript_format with TranscriptFormat # transcript_format: # range: TranscriptFormatEnum # required: false @@ -113,8 +112,8 @@ classes: # examples: # - value: STRUCTURED # description: Text with speaker labels and paragraph breaks - has_or_had_format: - range: TranscriptFormat + has_or_had_transcript_format: + # range: TranscriptFormat - defined in slot, no override needed required: false description: The format of the transcript (plain text, structured, timestamped, etc.) examples: diff --git a/schemas/20251121/linkml/modules/classes/Warehouse.yaml b/schemas/20251121/linkml/modules/classes/Warehouse.yaml index e4bc6ce99f..151b3689d8 100644 --- a/schemas/20251121/linkml/modules/classes/Warehouse.yaml +++ b/schemas/20251121/linkml/modules/classes/Warehouse.yaml @@ -15,19 +15,18 @@ imports: - ../slots/specificity_annotation - ../slots/template_specificity # Migrated per slot_fixes.yaml (Rule 53) - 2026-01-14 - # warehouse_description → has_or_had_description + Description + # warehouse_description → has_or_had_structured_description + Description # warehouse_floor_area_sqm → has_or_had_area + Area # warehouse_id → has_or_had_identifier (uriorcurie range) # warehouse_managed_by → is_or_was_managed_by + Group # warehouse_name → has_or_had_label # warehouse_security_level → has_or_had_security_level + SecurityLevel - - ../slots/has_or_had_description + - ../slots/has_or_had_structured_description # was: warehouse_description - uses Description class - ../slots/has_or_had_area - ../slots/has_or_had_identifier - ../slots/is_or_was_managed_by - ../slots/has_or_had_label - ../slots/has_or_had_security_level - - ./Description - ./Area - ./Group - ./SecurityLevel @@ -92,7 +91,7 @@ classes: - specificity_annotation - template_specificity # Migrated per slot_fixes.yaml (Rule 53) - 2026-01-14 - - has_or_had_description # was: warehouse_description + - has_or_had_structured_description # was: warehouse_description (uses Description class) - has_or_had_area # was: warehouse_floor_area_sqm - has_or_had_identifier # was: warehouse_id - is_or_was_managed_by # was: warehouse_managed_by @@ -124,12 +123,10 @@ classes: description: Museum logistics facility - value: KB Operations Warehouse Leiden description: Library operations warehouse - has_or_had_description: # was: warehouse_description + has_or_had_structured_description: # was: warehouse_description (uses Description class) description: | Description of warehouse purpose and contents. MIGRATED from warehouse_description per slot_fixes.yaml (Rule 53). - range: Description - inlined: true examples: - value: description_text: Logistics warehouse for exhibition equipment, packing materials, and furniture. Facilities team access only. @@ -241,7 +238,7 @@ classes: - value: has_or_had_identifier: https://nde.nl/ontology/hc/aux/rm-logistics-warehouse # was: warehouse_id has_or_had_label: Rijksmuseum Logistics Warehouse # was: warehouse_name - has_or_had_description: # was: warehouse_description + has_or_had_structured_description: # was: warehouse_description description_text: Logistics warehouse for exhibition equipment and packing materials. Used by exhibition services team. description_type: warehouse has_or_had_type: EXHIBITION_EQUIPMENT @@ -265,7 +262,7 @@ classes: - value: has_or_had_identifier: https://nde.nl/ontology/hc/aux/na-supplies-warehouse # was: warehouse_id has_or_had_label: Nationaal Archief Supplies Warehouse # was: warehouse_name - has_or_had_description: # was: warehouse_description + has_or_had_structured_description: # was: warehouse_description description_text: General supplies warehouse for archival boxes, office furniture, and operational materials. description_type: warehouse has_or_had_type: GENERAL_SUPPLIES diff --git a/schemas/20251121/linkml/modules/slots/has_or_had_entity_status.yaml b/schemas/20251121/linkml/modules/slots/has_or_had_entity_status.yaml new file mode 100644 index 0000000000..dc1084b8ce --- /dev/null +++ b/schemas/20251121/linkml/modules/slots/has_or_had_entity_status.yaml @@ -0,0 +1,64 @@ +# has_or_had_entity_status - Entity operational status slot +# +# Following RiC-O style naming convention (Rule 39): +# - has_or_had_* indicates temporal relationship +# +# Created to resolve OWL ambiguous type warning on legal_status slot. +# This slot is for LegalStatus objects (ACTIVE, DISSOLVED, etc.), +# distinct from legal_status slot which links to CustodianLegalStatus. + +id: https://nde.nl/ontology/hc/slot/has_or_had_entity_status +name: has_or_had_entity_status_slot +title: Has Or Had Entity Status Slot + +prefixes: + linkml: https://w3id.org/linkml/ + hc: https://nde.nl/ontology/hc/ + gleif_base: https://www.gleif.org/ontology/Base/ + schema: http://schema.org/ + +imports: + - linkml:types + - ../classes/RegistrationInfo # Contains LegalStatus class + +default_prefix: hc + +slots: + has_or_had_entity_status: + slot_uri: hc:hasOrHadEntityStatus + range: LegalStatus + description: | + Current or past operational status of a legal entity. + + Links to LegalStatus with: + - status_code: ACTIVE, DISSOLVED, MERGED, SUSPENDED, etc. + - status_name: Human-readable status name + + **ONTOLOGY ALIGNMENT**: + + | Ontology | Property | Notes | + |----------|----------|-------| + | **GLEIF** | `gleif_base:hasEntityStatus` | Close - entity lifecycle status | + | **Schema.org** | `schema:status` | Related - general status | + + **DISTINCTION FROM legal_status SLOT**: + + | Slot | Range | Purpose | + |------|-------|---------| + | `legal_status` | `CustodianLegalStatus` | Links to legal entity object | + | `has_or_had_entity_status` | `LegalStatus` | Indicates operational status (ACTIVE/DISSOLVED) | + + required: true + close_mappings: + - gleif_base:hasEntityStatus + related_mappings: + - schema:status + examples: + - value: + status_code: ACTIVE + status_name: Active + description: Currently operating entity + - value: + status_code: DISSOLVED + status_name: Dissolved + description: Legally dissolved entity diff --git a/schemas/20251121/linkml/modules/slots/has_or_had_hypernym.yaml b/schemas/20251121/linkml/modules/slots/has_or_had_hypernym.yaml index 68a391f141..1745013b76 100644 --- a/schemas/20251121/linkml/modules/slots/has_or_had_hypernym.yaml +++ b/schemas/20251121/linkml/modules/slots/has_or_had_hypernym.yaml @@ -50,9 +50,13 @@ slots: to allow consistent class-valued ranges when classes override. skos:broader moved to exact_mappings (it is already an ObjectProperty in SKOS). - **Range**: `Any` (2026-01-16) - Allows uriorcurie values and class instances. + **Range**: `uriorcurie` (2026-01-16) - Allows both URIs and CURIE references. - range: string + Note: Individual Type classes may override to their specific type in slot_usage, + but since they're all referring to URIs, this causes no OWL ambiguity when + the base range is uriorcurie (compatible with ObjectProperty). + + range: uriorcurie required: false multivalued: false diff --git a/schemas/20251121/linkml/modules/slots/has_or_had_observation_source_document.yaml b/schemas/20251121/linkml/modules/slots/has_or_had_observation_source_document.yaml new file mode 100644 index 0000000000..2afbfc205a --- /dev/null +++ b/schemas/20251121/linkml/modules/slots/has_or_had_observation_source_document.yaml @@ -0,0 +1,67 @@ +# has_or_had_observation_source_document - Source document for observation +# +# Following RiC-O style naming convention (Rule 39): +# - has_or_had_* indicates temporal relationship +# +# Created to resolve OWL ambiguous type warning on observation_source slot. +# This slot is for SourceDocument objects (structured source references), +# distinct from observation_source slot which uses string values. + +id: https://nde.nl/ontology/hc/slot/has_or_had_observation_source_document +name: has_or_had_observation_source_document_slot +title: Has Or Had Observation Source Document Slot + +prefixes: + linkml: https://w3id.org/linkml/ + hc: https://nde.nl/ontology/hc/ + prov: http://www.w3.org/ns/prov# + dcterms: http://purl.org/dc/terms/ + +imports: + - linkml:types + - ../classes/SourceDocument + +default_prefix: hc + +slots: + has_or_had_observation_source_document: + slot_uri: hc:hasOrHadObservationSourceDocument + range: SourceDocument + description: | + Structured source document where this observation was found. + + Links to SourceDocument with: + - source_type: "Staff directory", "Annual report", etc. + - source_uri: URL if available + - observation_date: When source was consulted + + **PiCo Pattern**: PersonObservation MUST link to source (evidence-based) + **PROV-O**: `prov:hadPrimarySource` for provenance tracking + + **Source Types**: + - Staff directory (online or print) + - Organizational chart + - Annual report + - Institutional website + - Archival personnel records + - Publication credits + - Email signature + + **DISTINCTION FROM observation_source SLOT**: + + | Slot | Range | Purpose | + |------|-------|---------| + | `observation_source` | `string` | Simple text reference to source | + | `has_or_had_observation_source_document` | `SourceDocument` | Structured source with metadata | + + required: false + close_mappings: + - prov:hadPrimarySource + related_mappings: + - dcterms:source + examples: + - value: + source_type: Staff directory + source_uri: https://example.org/staff + observation_date: "2025-01-10" + description: Staff directory source with structured metadata diff --git a/schemas/20251121/linkml/modules/slots/has_or_had_structured_description.yaml b/schemas/20251121/linkml/modules/slots/has_or_had_structured_description.yaml new file mode 100644 index 0000000000..f21f4cf90d --- /dev/null +++ b/schemas/20251121/linkml/modules/slots/has_or_had_structured_description.yaml @@ -0,0 +1,62 @@ +# has_or_had_structured_description slot +# Slot for structured Description class instances +# +# Following RiC-O naming convention (Rule 39): "hasOrHad..." pattern +# for temporal relationships in heritage domain. +# +# Created to resolve OWL ambiguous type warning on has_or_had_description slot. +# This slot is for Description class (structured descriptions with language/type), +# distinct from has_or_had_description slot which uses string values. +# +# Generation date: 2026-01-16 +# Rule compliance: 38 (slot centralization + semantic URI), 39 (RiC-O naming), 42 (no prefix) + +id: https://nde.nl/ontology/hc/slot/has_or_had_structured_description +name: has_or_had_structured_description_slot +title: Has Or Had Structured Description Slot + +prefixes: + linkml: https://w3id.org/linkml/ + hc: https://nde.nl/ontology/hc/ + dcterms: http://purl.org/dc/terms/ + skos: http://www.w3.org/2004/02/skos/core# + +default_prefix: hc + +imports: + - linkml:types + - ../classes/Description + +slots: + has_or_had_structured_description: + slot_uri: hc:hasOrHadStructuredDescription + range: Description + description: | + Structured description with language, type, and provenance metadata. + + **DISTINCTION FROM has_or_had_description SLOT**: + + | Slot | Range | Purpose | + |------|-------|---------| + | `has_or_had_description` | `string` | Simple text description | + | `has_or_had_structured_description` | `Description` | Typed description with metadata | + + **Temporal Semantics** (RiC-O Pattern): + The "hasOrHad" naming follows RiC-O convention indicating this relationship + may be historical - an entity's description may change over time. + + **Ontological Alignment**: + - **Primary** (`slot_uri`): `hc:hasOrHadStructuredDescription` - ObjectProperty + - **Close**: `dcterms:description` - Dublin Core (DatatypeProperty) + - **Close**: `skos:definition` - SKOS definition + required: false + inlined: true + close_mappings: + - dcterms:description + - skos:definition + examples: + - value: + description_text: "A historic reading room with original 19th century furnishings" + description_type: FACILITY + description_language: en + description: Structured facility description with language tag diff --git a/schemas/20251121/linkml/modules/slots/has_or_had_subtitle_format.yaml b/schemas/20251121/linkml/modules/slots/has_or_had_subtitle_format.yaml new file mode 100644 index 0000000000..1d67a4fb52 --- /dev/null +++ b/schemas/20251121/linkml/modules/slots/has_or_had_subtitle_format.yaml @@ -0,0 +1,64 @@ +# has_or_had_subtitle_format - Subtitle format specification slot +# +# Created to fix gen-owl "Ambiguous type for: has_or_had_format" warning. +# The base has_or_had_format slot has range: string, but VideoSubtitle needs +# range: SubtitleFormatEnum. This specialized slot provides the enum-typed range. +# +# Creation date: 2026-01-16 +# Rule compliance: 39 (RiC-O naming), 50 (ontology mapping) + +id: https://nde.nl/ontology/hc/slot/has_or_had_subtitle_format +name: has_or_had_subtitle_format +title: Has or Had Subtitle Format + +prefixes: + linkml: https://w3id.org/linkml/ + hc: https://nde.nl/ontology/hc/ + schema: http://schema.org/ + +default_prefix: hc + +imports: + - linkml:types + - ../enums/SubtitleFormatEnum + +slots: + has_or_had_subtitle_format: + slot_uri: hc:hasOrHadSubtitleFormat + description: | + The subtitle format for video subtitle tracks. + + **USAGE**: + Specifies the technical format of a subtitle file (SRT, VTT, TTML, etc.). + This slot is used exclusively by VideoSubtitle and related classes. + + **SUBTITLE FORMATS**: + | Format | Extension | Features | Use Case | + |--------|-----------|----------|----------| + | SRT | .srt | Simple, universal | Most video players | + | VTT | .vtt | W3C standard, styling | HTML5 video, web | + | TTML | .ttml/.dfxp | XML, rich styling | Broadcast, streaming | + | SBV | .sbv | YouTube native | YouTube uploads | + | ASS | .ass | Advanced styling | Anime, complex layouts | + + **ONTOLOGY ALIGNMENT**: + - **Primary** (`slot_uri`): `hc:hasOrHadSubtitleFormat` - Heritage Custodian property + - **Related**: `schema:encodingFormat` - General encoding format + + range: SubtitleFormatEnum + required: false + + close_mappings: + - schema:encodingFormat + + examples: + - value: VTT + description: WebVTT format (W3C standard for HTML5 video) + - value: SRT + description: SubRip format (most widely supported) + - value: TTML + description: Timed Text Markup Language (broadcast standard) + + annotations: + custodian_types: '["*"]' + custodian_types_rationale: "Subtitle formats applicable to all custodian types with video content." diff --git a/schemas/20251121/linkml/modules/slots/has_or_had_transcript_format.yaml b/schemas/20251121/linkml/modules/slots/has_or_had_transcript_format.yaml new file mode 100644 index 0000000000..bcc3bccd73 --- /dev/null +++ b/schemas/20251121/linkml/modules/slots/has_or_had_transcript_format.yaml @@ -0,0 +1,64 @@ +# has_or_had_transcript_format - Transcript format specification slot +# +# Created to fix gen-owl "Ambiguous type for: has_or_had_format" warning. +# The base has_or_had_format slot has range: string, but VideoTranscript needs +# range: TranscriptFormat. This specialized slot provides the class-typed range. +# +# Creation date: 2026-01-16 +# Rule compliance: 39 (RiC-O naming), 50 (ontology mapping) + +id: https://nde.nl/ontology/hc/slot/has_or_had_transcript_format +name: has_or_had_transcript_format +title: Has or Had Transcript Format + +prefixes: + linkml: https://w3id.org/linkml/ + hc: https://nde.nl/ontology/hc/ + schema: http://schema.org/ + +default_prefix: hc + +imports: + - linkml:types + - ../classes/TranscriptFormat + +slots: + has_or_had_transcript_format: + slot_uri: hc:hasOrHadTranscriptFormat + description: | + The format of a transcript (plain text, structured, timestamped, etc.). + + **USAGE**: + Specifies the structural format of a transcript. This slot is used + exclusively by VideoTranscript and related classes. + + **TRANSCRIPT FORMATS**: + | Format | Description | Use Case | + |--------|-------------|----------| + | PLAIN_TEXT | Continuous text, no structure | Simple search indexing | + | PARAGRAPHED | Text broken into paragraphs | Human reading | + | STRUCTURED | Segments with speaker labels | Research, analysis | + | TIMESTAMPED | Segments with time markers | Navigation, subtitling | + + **ONTOLOGY ALIGNMENT**: + - **Primary** (`slot_uri`): `hc:hasOrHadTranscriptFormat` - Heritage Custodian property + - **Related**: `schema:encodingFormat` - General encoding format + + range: TranscriptFormat + required: false + inlined: true + + close_mappings: + - schema:encodingFormat + + examples: + - value: STRUCTURED + description: Text with speaker labels and paragraph breaks + - value: TIMESTAMPED + description: Segments with time codes for navigation + - value: PLAIN_TEXT + description: Continuous text without structure + + annotations: + custodian_types: '["*"]' + custodian_types_rationale: "Transcript formats applicable to all custodian types with video content." diff --git a/schemas/20251121/linkml/modules/slots/has_or_had_type.yaml b/schemas/20251121/linkml/modules/slots/has_or_had_type.yaml index 040f24ee94..47390e3b4c 100644 --- a/schemas/20251121/linkml/modules/slots/has_or_had_type.yaml +++ b/schemas/20251121/linkml/modules/slots/has_or_had_type.yaml @@ -5,7 +5,8 @@ # for temporal relationships in heritage domain. # # Generation date: 2026-01-13 -# Rule compliance: 38 (slot centralization + semantic URI), 39 (RiC-O naming), 42 (no prefix) +# Updated: 2026-01-16 - Added any_of pattern to resolve OWL ambiguous type warning +# Rule compliance: 38 (slot centralization + semantic URI), 39 (RiC-O naming), 42 (no prefix), 53 (generic slots) id: https://nde.nl/ontology/hc/slot/has_or_had_type name: has_or_had_type_slot @@ -23,6 +24,7 @@ default_prefix: hc imports: - linkml:types + - ../classes/TypeClass slots: has_or_had_type: @@ -44,20 +46,19 @@ slots: **Usage**: This is a GENERIC slot intended for reuse across multiple classes. - Classes should specialize the range in slot_usage to reference the + Classes may specialize the range in slot_usage to reference the appropriate Type class hierarchy (e.g., StorageType, ZoneType, etc.). + **Range** (any_of pattern): + Accepts both string literals and TypeClass instances. This resolves the + OWL "Ambiguous type" warning by explicitly declaring both valid ranges. + **Cardinality**: Multivalued - entities may have multiple type classifications. - **Note**: slot_uri changed from crm:P2_has_type to hc:hasOrHadType (2026-01-16) - to resolve OWL ambiguous type warning when classes override range to class types. - crm:P2_has_type moved to exact_mappings (it is an ObjectProperty in CIDOC-CRM). - - **Range**: `Any` (2026-01-16) - Allows both string values and class instances. - Classes narrow this to specific Type class hierarchies via slot_usage. - - range: string + any_of: + - range: string + - range: TypeClass required: false multivalued: true inlined_as_list: true @@ -86,20 +87,27 @@ slots: zone_type, warehouse_type, unit_type, treatment_type, storage_type, statement_type, sub_guide_type, wikidata_mapping_type migration_date: "2026-01-13" + any_of_fix_date: "2026-01-16" + any_of_fix_purpose: | + Added any_of pattern with TypeClass to resolve OWL "Ambiguous type" warning. + gen-owl was reporting ambiguity because slot had range:string but classes + overrode to class types via slot_usage. predicate_clarification: | - slot_uri references a PREDICATE (crm:P2_has_type), not a class. - The range should be specialized per class to reference appropriate Type classes. + slot_uri references a PREDICATE (hc:hasOrHadType), not a class. + The range is polymorphic via any_of, accepting both strings and TypeClass instances. comments: - "Generic type slot for reuse across multiple classes" - - "Specialize range in slot_usage for specific Type class hierarchies" - - "slot_uri=crm:P2_has_type is a PREDICATE, not a class" + - "any_of pattern: accepts both string and TypeClass values" + - "slot_uri=hc:hasOrHadType is a PREDICATE, not a class" - "RiC-O naming: hasOrHad indicates potentially historical relationship" - "Multivalued: entities may have multiple type classifications" - - "Range: Any - resolves OWL ambiguous type warning (2026-01-16)" + - "OWL fix: any_of pattern resolves Ambiguous type warning (2026-01-16)" examples: - value: StorageType:ARCHIVE_DEPOT - description: "Storage typed as archive depot" + description: "Storage typed as archive depot (class instance)" - value: ZoneType:CLIMATE_CONTROLLED - description: "Environmental zone type" + description: "Environmental zone type (class instance)" + - value: "historic building" + description: "Free-text type description (string literal)" diff --git a/src/glam_extractor/api/entity_review.py b/src/glam_extractor/api/entity_review.py index 4ae9899b50..7b97eec0df 100644 --- a/src/glam_extractor/api/entity_review.py +++ b/src/glam_extractor/api/entity_review.py @@ -72,14 +72,83 @@ _candidates_by_linkedin = None # For LinkedIn profile lookup _candidates_by_email = None # For email-to-ppid lookup (semantic search) _candidates_list_index = {} # Map (wcms_ppid, linkedin_ppid) -> index in candidates list +# Cache for WCMS-only profiles (no LinkedIn candidates) +_wcms_only_profiles: Optional[List[dict]] = None + def invalidate_cache(): """Invalidate the candidates cache to force reload.""" - global _candidates_cache, _candidates_by_wcms, _candidates_by_linkedin, _candidates_by_email, _candidates_list_index + global _candidates_cache, _candidates_by_wcms, _candidates_by_linkedin, _candidates_by_email, _candidates_list_index, _wcms_only_profiles _candidates_cache = None _candidates_by_wcms = None _candidates_by_linkedin = None _candidates_by_email = None _candidates_list_index = {} + _wcms_only_profiles = None + + +def load_wcms_email_index() -> dict: + """Load the WCMS email index if not already loaded.""" + global _WCMS_EMAIL_INDEX_CACHE + + if _WCMS_EMAIL_INDEX_CACHE is not None: + return _WCMS_EMAIL_INDEX_CACHE + + if WCMS_EMAIL_INDEX_FILE.exists(): + try: + with open(WCMS_EMAIL_INDEX_FILE, 'r') as f: + _WCMS_EMAIL_INDEX_CACHE = json.load(f) + print(f"Loaded {len(_WCMS_EMAIL_INDEX_CACHE)} entries from WCMS email index") + except Exception as e: + print(f"Error loading WCMS email index: {e}") + _WCMS_EMAIL_INDEX_CACHE = {} + else: + _WCMS_EMAIL_INDEX_CACHE = {} + + return _WCMS_EMAIL_INDEX_CACHE + + +def load_wcms_only_profiles() -> List[dict]: + """ + Load WCMS profiles that have NO LinkedIn candidates. + These are candidates for LinkedIn search. + """ + global _wcms_only_profiles + + if _wcms_only_profiles is not None: + return _wcms_only_profiles + + # Ensure both caches are loaded + load_candidates() + wcms_index = load_wcms_email_index() + + # Get emails that already have candidates + emails_with_candidates = set() + if _candidates_by_email: + emails_with_candidates = set(_candidates_by_email.keys()) + + # Build list of WCMS-only profiles + _wcms_only_profiles = [] + for email, profile_data in wcms_index.items(): + email_lower = email.lower().strip() + if email_lower not in emails_with_candidates: + _wcms_only_profiles.append({ + "email": profile_data.get("email", email), + "name": profile_data.get("full_name", "Unknown"), + "user_id": profile_data.get("user_id"), + "username": profile_data.get("username"), + "status": profile_data.get("status"), + "roles": profile_data.get("roles", []), + "registered_since": profile_data.get("registered_since"), + "last_access": profile_data.get("last_access"), + "abs_id": profile_data.get("abs_id"), + "crm_id": profile_data.get("crm_id"), + }) + + # Sort by name + _wcms_only_profiles.sort(key=lambda x: (x.get('name') or '').lower()) + + print(f"Loaded {len(_wcms_only_profiles)} WCMS-only profiles (no LinkedIn candidates)") + return _wcms_only_profiles def load_candidates(): """Load candidates from the aggregated candidates file.""" @@ -591,6 +660,77 @@ async def list_candidates( ) +class WcmsOnlyProfile(BaseModel): + """A WCMS profile without LinkedIn candidates - ready for LinkedIn search.""" + email: str + name: str + user_id: Optional[str] = None + username: Optional[str] = None + status: Optional[str] = None + roles: List[str] = [] + registered_since: Optional[str] = None + last_access: Optional[str] = None + abs_id: Optional[str] = None + crm_id: Optional[str] = None + + +class WcmsOnlyListResponse(BaseModel): + """Response for listing WCMS-only profiles.""" + total: int + page: int + page_size: int + profiles: List[WcmsOnlyProfile] + + +@router.get("/wcms-only", response_model=WcmsOnlyListResponse) +async def list_wcms_only_profiles( + page: int = Query(1, ge=1, description="Page number"), + page_size: int = Query(50, ge=1, le=200, description="Items per page"), + search: Optional[str] = Query(None, description="Search by name or email"), + status: Optional[str] = Query(None, description="Filter by status (Active, Blocked, etc.)"), + sort_by: str = Query("name", description="Sort by: name, registered_since, last_access"), +): + """ + List WCMS profiles that have NO LinkedIn candidates yet. + + These profiles can be searched on LinkedIn to find potential matches. + This enables proactive LinkedIn research for WCMS users. + """ + profiles = load_wcms_only_profiles() + + # Apply filters + filtered = profiles + + if search: + search_lower = search.lower() + filtered = [p for p in filtered if + search_lower in (p.get('name') or '').lower() or + search_lower in (p.get('email') or '').lower()] + + if status: + filtered = [p for p in filtered if p.get('status') == status] + + # Sort + if sort_by == "name": + filtered.sort(key=lambda x: (x.get('name') or '').lower()) + elif sort_by == "registered_since": + filtered.sort(key=lambda x: x.get('registered_since') or '', reverse=True) + elif sort_by == "last_access": + filtered.sort(key=lambda x: x.get('last_access') or '', reverse=True) + + # Paginate + start = (page - 1) * page_size + end = start + page_size + page_profiles = filtered[start:end] + + return WcmsOnlyListResponse( + total=len(filtered), + page=page, + page_size=page_size, + profiles=[WcmsOnlyProfile(**p) for p in page_profiles] + ) + + @router.get("/profile/by-email/{email}") async def get_profile_by_email(email: str): """ @@ -1201,9 +1341,19 @@ async def get_review_stats(): if c.get('is_likely_wrong_person'): likely_wrong_person += 1 + # Get WCMS-only profiles count + wcms_only_profiles = load_wcms_only_profiles() + wcms_only_count = len(wcms_only_profiles) + + # Total WCMS in email index + wcms_email_index = load_wcms_email_index() + total_wcms_in_index = len(wcms_email_index) + return { "total_profiles": metadata.get('wcms_profiles_processed', 0), "profiles_with_candidates": len(_candidates_by_wcms), + "wcms_only_count": wcms_only_count, # WCMS profiles without LinkedIn candidates + "total_wcms_in_index": total_wcms_in_index, # Total in WCMS email index "total_candidates": total_candidates, "reviewed_candidates": reviewed_candidates, "pending_candidates": total_candidates - reviewed_candidates,