Compare commits

...

5 commits

Author SHA1 Message Date
kempersc
e922ff3640 Refactor UML module selection UI and enhance schema mappings across various YAML files
All checks were successful
Deploy Frontend / build-and-deploy (push) Successful in 2m3s
2026-02-20 12:05:37 +01:00
kempersc
2fd6f491ef Add UML density section with display mode options and module picker 2026-02-19 13:00:41 +01:00
kempersc
ee4f31ea1b Update generated timestamp in manifest.json for accuracy 2026-02-18 21:44:27 +01:00
kempersc
3e540832b7 Add new slots for payment context and update manifest with generated timestamp 2026-02-18 21:43:53 +01:00
kempersc
b34a8ac777 Update LinkML manifest generation timestamp and enhance MappingExplorer with schema validation
- Updated the generated timestamp in the LinkML manifest file.
- Added new CSS styles for schema status and warning indicators in MappingExplorer.
- Implemented schema validation logic in MappingExplorer to check field validity against the loaded LinkML schema.
- Enhanced the UI to display schema status and warnings for invalid fields in the mapping interface.
- Refactored field details panel to show schema validity messages for target classes and slots.
- Updated various target classes and slots in custodian data mappings for consistency and accuracy.
2026-02-18 18:44:03 +01:00
89 changed files with 6043 additions and 3481 deletions

View file

@ -6,8 +6,9 @@
"scripts": {
"sync-schemas": "rsync -av --delete --exclude=\"archive/\" ../schemas/20251121/linkml/ public/schemas/20251121/linkml/",
"generate-manifest": "node scripts/generate-schema-manifest.cjs",
"dev": "pnpm run sync-schemas && pnpm run generate-manifest && vite",
"build": "pnpm run sync-schemas && pnpm run generate-manifest && tsc -b && vite build",
"generate-uml": "node scripts/generate-ontology-uml.cjs",
"dev": "pnpm run sync-schemas && pnpm run generate-manifest && pnpm run generate-uml && vite",
"build": "pnpm run sync-schemas && pnpm run generate-manifest && pnpm run generate-uml && tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
"test": "vitest",

File diff suppressed because it is too large Load diff

View file

@ -16,7 +16,7 @@ imports:
classes:
Curator:
is_a: StaffRole
class_uri: schema:curator
class_uri: hc:Curator
description: |
Curator responsible for collections research, acquisition, and exhibitions.
@ -41,8 +41,7 @@ classes:
- Object interpretation and labeling
- Loan negotiations
exact_mappings:
- schema:curator
- wikidata:Q674426
- wikidata:Q674426
slot_usage:
role_category:
ifabsent: string(CURATORIAL)
@ -53,8 +52,8 @@ classes:
range: TemplateSpecificityScores
inlined: true
slots:
- specificity_annotation
- template_specificity
- specificity_annotation
- template_specificity
CollectionsManager:
is_a: StaffRole
description: |
@ -80,7 +79,7 @@ classes:
- Rights and reproductions
- Insurance and valuation coordination
exact_mappings:
- wikidata:Q65963513
- wikidata:Q65963513
slot_usage:
role_category:
ifabsent: string(CURATORIAL)
@ -91,8 +90,8 @@ classes:
range: TemplateSpecificityScores
inlined: true
slots:
- specificity_annotation
- template_specificity
- specificity_annotation
- template_specificity
Conservator:
is_a: StaffRole
description: |

View file

@ -7,6 +7,7 @@ prefixes:
schema: http://schema.org/
crm: http://www.cidoc-crm.org/cidoc-crm/
gn: http://www.geonames.org/ontology#
rdac: http://rdaregistry.info/Elements/c/
wdt: http://www.wikidata.org/prop/direct/
dcterms: http://purl.org/dc/terms/
prov: http://www.w3.org/ns/prov#
@ -58,6 +59,7 @@ classes:
close_mappings:
- crm:E53_Place
- gn:Feature
- rdac:C10009 # RDA Registry: Place (RDA Classes)
slots:
- has_label
- has_name

View file

@ -1,12 +1,12 @@
{
"generated": "2026-02-18T11:39:46.457Z",
"generated": "2026-02-18T20:43:54.475Z",
"schemaRoot": "/schemas/20251121/linkml",
"totalFiles": 2183,
"totalFiles": 2187,
"categoryCounts": {
"main": 4,
"class": 1377,
"enum": 158,
"slot": 640,
"slot": 644,
"module": 4
},
"categories": [
@ -10387,6 +10387,21 @@
"path": "modules/slots/owned_by.yaml",
"category": "slot"
},
{
"name": "paid_amount",
"path": "modules/slots/paid_amount.yaml",
"category": "slot"
},
{
"name": "paid_from",
"path": "modules/slots/paid_from.yaml",
"category": "slot"
},
{
"name": "paid_to",
"path": "modules/slots/paid_to.yaml",
"category": "slot"
},
{
"name": "part_of",
"path": "modules/slots/part_of.yaml",
@ -10847,6 +10862,11 @@
"path": "modules/slots/transferred.yaml",
"category": "slot"
},
{
"name": "transferred_from",
"path": "modules/slots/transferred_from.yaml",
"category": "slot"
},
{
"name": "transferred_to",
"path": "modules/slots/transferred_to.yaml",

View file

@ -91,7 +91,7 @@ classes:
- crm:E8_Acquisition
close_mappings:
- prov:Activity
- schema:AcquireAction
- schema:TradeAction
related_mappings:
- rico:Event
- dwc:Event

View file

@ -17,7 +17,9 @@ imports:
- ../slots/temporal_extent
classes:
Agreement:
class_uri: schema:Contract
class_uri: hc:Agreement
close_mappings:
- schema:Thing
description: >-
Formal arrangement between two or more parties establishing mutual
obligations, rights, or expectations, typically documented in writing

View file

@ -17,7 +17,9 @@ imports:
- ../slots/categorized_as
classes:
Animal:
class_uri: schema:Animal
class_uri: wd:Q729
exact_mappings:
- wikidata:Q729
description: >-
Multicellular living organism characterized by heterotrophic nutrition,
motility at some life stage, and sensory response capacity.
@ -83,10 +85,7 @@ classes:
- zoological
- wildlife
- fauna
exact_mappings:
- schema:Animal
broad_mappings:
- wd:Q729
- schema:Thing
slots:
- has_label
@ -103,7 +102,7 @@ classes:
Preserved from prior description:
**Ontological Alignment**:
- `schema:Animal`: Generic animal class.
- `wikidata:Q729`: Animal (kingdom Animalia) - Wikidata entity for animals.
- 'Preserved from prior description: Living organism belonging to the kingdom Animalia, characterized by voluntary movement, consumption of organic material, and typically sensory and nervous systems, relevant to natural history collections and zoological specimens.'
annotations:
specificity_score: "0.2"

View file

@ -42,7 +42,7 @@ classes:
description: Specialized auction house
close_mappings:
- schema:Organization
- schema:AuctionHouse
- schema:LocalBusiness
broad_mappings:
- skos:Concept
structured_aliases:

View file

@ -15,7 +15,9 @@ prefixes:
default_prefix: hc
classes:
AvailabilityStatus:
class_uri: schema:Availability
class_uri: hc:AvailabilityStatus
broad_mappings:
- schema:ItemAvailability
description: >-
Availability state of a resource, service, or feature, indicating whether
something is currently available for use with optional temporal validity

View file

@ -12,6 +12,7 @@ prefixes:
gbif: http://rs.gbif.org/terms/
aat: http://vocab.getty.edu/aat/
imports:
- ./ExhibitedObject
- linkml:types
- ../enums/PreservationMethodEnum
- ../metadata
@ -47,6 +48,7 @@ imports:
default_prefix: hc
classes:
BiologicalObject:
is_a: ExhibitedObject
class_uri: crm:E20_Biological_Object
description: >-
Natural specimen or organism-derived item held in a heritage collection, with associated taxonomic identification and preservation metadata.
@ -397,29 +399,64 @@ classes:
in_language: zh
- literal_form: value: https://nde.nl/ontology/hc/taxon/raphus-cucullatus
broad_mappings:
- crm:E20_Biological_Object
close_mappings:
- dwc:Occurrence
- gbif:Specimen
related_mappings:
- crm:E19_Physical_Object
- crm:E22_Human-Made_Object
- schema:Taxon
predicate: EXACT_SYNONYM
slots:
- identified_by
- has_label
- has_name
- has_rank
- has_authority
- commented_on
- identified_through
- has_specimen
- symbolize
- has_status
- has_gender
- has_stage
- contain
- has_quantity
- has_method
- has_detail
- prepared_on
- prepared_by
- acquired_through
- in_place
- describe
- acquired_by
- has_habitat
- has_hypernym
- listed_in
- has_provenance
- has_type
in_language: zh
# range: string # uriorcurie
slot_usage:
identified_by:
multivalued: true
inlined: false # Fixed invalid inline for primitive type
inlined_as_list: false # Fixed invalid inline for primitive type
required: false
any_of:
- range: FieldNumber
- range: BOLDIdentifier
- range: WikiDataIdentifier
- range: string # uriorcurie
- range: FieldNumber
- range: BOLDIdentifier
- range: WikiDataIdentifier
- range: string # uriorcurie
examples:
- value:
has_type: FieldNumber
- value:
id: https://nde.nl/ontology/hc/bold-id/NLNAT001-21
has_type: BOLDIdentifier
- value:
has_type: WikiDataIdentifier
- value:
has_type: FieldNumber
- value:
id: https://nde.nl/ontology/hc/bold-id/NLNAT001-21
has_type: BOLDIdentifier
- value:
has_type: WikiDataIdentifier
has_label:
range: TaxonName
inlined: true

View file

@ -5,6 +5,7 @@ prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
schema: http://schema.org/
rec: https://w3id.org/rec#
qudt: http://qudt.org/schema/qudt/
default_prefix: hc
imports:
@ -15,7 +16,7 @@ imports:
classes:
Classroom:
is_a: Facility
class_uri: schema:Classroom
class_uri: rec:Classroom
description: >-
Instructional room entity for teaching, workshops, and guided learning sessions within institutional premises.
alt_descriptions:
@ -42,7 +43,7 @@ classes:
examples:
- value: 'unit_type: CLASSROOM '
exact_mappings:
- schema:Classroom
- rec:Classroom
close_mappings:
- schema:Room
- schema:Place

View file

@ -54,8 +54,6 @@ classes:
required: false
close_mappings:
- dcterms:conformsTo
related_mappings:
- schema:assessment
annotations:
custodian_types: '["A", "L", "M"]'
custodian_types_rationale: Compliance tracking is common for archives, libraries, and museums with preservation mandates.

View file

@ -17,7 +17,9 @@ imports:
- ../slots/has_score
classes:
Currency:
class_uri: schema:Currency
class_uri: qudt:CurrencyUnit
exact_mappings:
- qudt:CurrencyUnit
description: >-
Standardized monetary unit with ISO 4217 code representation for pricing, budgeting, and financial valuation metadata.
alt_descriptions:

View file

@ -17,7 +17,9 @@ imports:
- ../slots/temporal_extent
classes:
DeceasedStatus:
class_uri: schema:DeathEvent
class_uri: hc:DeceasedStatus
broad_mappings:
- schema:Event
description: >-
Structured event record for a person's death including causal,
temporal, and contextual attributes.
@ -38,9 +40,8 @@ classes:
- {literal_form: status kematian, in_language: id}
- {literal_form: 死亡状态记录, in_language: zh}
exact_mappings:
- schema:DeathEvent
close_mappings:
- crm:E69_Death
close_mappings:
related_mappings:
- prov:End
keywords:

View file

@ -36,195 +36,81 @@ classes:
zh: >-
描述平台界面、操作程序或用户实施指南的技术参考资料。
structured_aliases:
- literal_form: documentatie
predicate: EXACT_SYNONYM
in_language: nl
- literal_form: technische handleiding
predicate: EXACT_SYNONYM
in_language: nl
- literal_form: Dokumentation
predicate: EXACT_SYNONYM
in_language: de
- literal_form: technische Anleitung
predicate: EXACT_SYNONYM
in_language: de
- literal_form: documentation
predicate: EXACT_SYNONYM
in_language: fr
- literal_form: guide technique
predicate: EXACT_SYNONYM
in_language: fr
- literal_form: documentación
predicate: EXACT_SYNONYM
in_language: es
- literal_form: guía técnica
predicate: EXACT_SYNONYM
in_language: es
- literal_form: التوثيق
predicate: EXACT_SYNONYM
in_language: ar
- literal_form: الدليل الفني
predicate: EXACT_SYNONYM
in_language: ar
- literal_form: dokumentasi
predicate: EXACT_SYNONYM
in_language: id
- literal_form: panduan teknis
predicate: EXACT_SYNONYM
in_language: id
- literal_form: 文档
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: 技术指南
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: documentation
broad_mappings:
- schema:TechArticle
- foaf:Document
close_mappings:
- crm:E73_Information_Object
predicate: EXACT_SYNONYM
slots:
- identified_by
- has_label
- has_description
- temporal_extent
in_language: zh
- literal_form: technical guide
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: API reference
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: user manual
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: operational guide
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: schema:CreativeWork
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: foaf:Document
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: schema:TechArticle
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: crm:E73_Information_Object
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: dcterms:references
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_description
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: identified_by
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: temporal_extent
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: https://data.rijksmuseum.nl/object-metadata/api/
predicate: EXACT_SYNONYM
in_language: zh
# range: string
slot_usage:
identified_by:
range: uri
required: true
examples:
- value: API Reference Documentation
- value: Developer Integration Guide
- value: https://data.rijksmuseum.nl/object-metadata/api/
has_label:
required: true
examples:
- value: Rijksmuseum Collection API
has_description:
# range: string
examples:
- value: Complete REST API reference with endpoint specifications, authentication, and response formats.
temporal_extent:
range: TimeSpan
inlined: true
required: false
examples:
- value:
begin_of_the_begin: '2015-01-01'
- value: Complete REST API reference with endpoint specifications, authentication, and response formats.
temporal_extent:
required: false
examples:
- value:
begin_of_the_begin: '2015-01-01'
has_verbatim_value: 2015-
comments:
- Generic documentation class replacing domain-specific documentation slots
- Supports multiple documentation types (API, user, developer, system)

View file

@ -186,19 +186,19 @@ classes:
in_language: zh
- literal_form: value: |
- literal_form: "value: |"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: expense_type: ADMINISTRATIVE
- literal_form: "expense_type: ADMINISTRATIVE"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: expense_type: PROGRAM
- literal_form: "expense_type: PROGRAM"
predicate: EXACT_SYNONYM

View file

@ -12,7 +12,7 @@ imports:
- ../slots/has_description
classes:
ForkliftAccess:
class_uri: schema:AmenityFeature
class_uri: schema:LocationFeatureSpecification
description: >-
Facility access feature indicating whether spaces permit forklift entry
for logistics, handling, or collection movement.

View file

@ -12,7 +12,7 @@ imports:
- ../slots/has_description
classes:
HandsOnFacility:
class_uri: schema:AmenityFeature
class_uri: schema:LocationFeatureSpecification
description: >-
Amenity feature indicating availability of interactive, practical learning
or handling spaces for visitors and participants.

View file

@ -15,7 +15,9 @@ imports:
classes:
InstitutionalRepository:
is_a: ArchiveOrganizationType
class_uri: schema:DigitalLibrary
class_uri: hc:InstitutionalRepository
broad_mappings:
- schema:Library
description: >-
Digital repository service for preserving, publishing, and providing access
to scholarly output produced by an academic or research institution.
@ -62,8 +64,6 @@ classes:
- literal_form: 机构仓储
predicate: EXACT_SYNONYM
in_language: zh
broad_mappings:
- schema:DigitalLibrary
close_mappings:
- skos:Concept
slots:

View file

@ -11,7 +11,7 @@ imports:
- ../slots/has_description
classes:
LoadingDock:
class_uri: schema:AmenityFeature
class_uri: schema:LocationFeatureSpecification
description: >-
Designated area for receiving and dispatching physical items through vehicular transport.
alt_descriptions:

View file

@ -11,7 +11,7 @@ imports:
- ../slots/has_description
classes:
Locker:
class_uri: schema:AmenityFeature
class_uri: schema:LocationFeatureSpecification
description: >-
Secure storage compartment provided as a visitor or staff amenity for temporary personal item retention.
alt_descriptions:
@ -52,7 +52,7 @@ classes:
predicate: EXACT_SYNONYM
in_language: zh
broad_mappings:
- schema:AmenityFeature
- schema:LocationFeatureSpecification
slots:
- has_description
comments:

View file

@ -185,7 +185,7 @@ classes:
in_language: zh
- literal_form: value: |
- literal_form: "value: |"
predicate: EXACT_SYNONYM
@ -197,37 +197,37 @@ classes:
in_language: zh
- literal_form: has_label: Manufacturer name (String or Label)
- literal_form: "has_label: Manufacturer name (String or Label)"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_url: Manufacturer website (URL)
- literal_form: "has_url: Manufacturer website (URL)"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: identified_by: Unique identifier
- literal_form: "identified_by: Unique identifier"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: **Primary**: `schema:Organization` - Schema.org organization
- literal_form: "**Primary**: `schema:Organization` - Schema.org organization"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: **Close**: `org:Organization` - W3C ORG organization
- literal_form: "**Close**: `org:Organization` - W3C ORG organization"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: **Related**: `prov:Agent` - PROV-O agent responsible for production
- literal_form: "**Related**: `prov:Agent` - PROV-O agent responsible for production"
predicate: EXACT_SYNONYM

View file

@ -174,13 +174,13 @@ classes:
in_language: zh
- literal_form: value: PRIMARY
- literal_form: "value: PRIMARY"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: CO_ORGANIZER
- literal_form: "value: CO_ORGANIZER"
predicate: EXACT_SYNONYM
@ -192,43 +192,43 @@ classes:
in_language: zh
- literal_form: PRIMARY: Main organizing institution
- literal_form: "PRIMARY: Main organizing institution"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: CO_ORGANIZER: Partner institution with significant organizational role
- literal_form: "CO_ORGANIZER: Partner institution with significant organizational role"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: SPONSOR_ORGANIZER: Sponsor with curatorial/organizational input
- literal_form: "SPONSOR_ORGANIZER: Sponsor with curatorial/organizational input"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: LENDING_INSTITUTION: Institution lending objects with exhibition involvement
- literal_form: "LENDING_INSTITUTION: Institution lending objects with exhibition involvement"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: HOST_VENUE: Venue hosting a traveling exhibition
- literal_form: "HOST_VENUE: Venue hosting a traveling exhibition"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: **Primary**: `schema:Role` - Schema.org role
- literal_form: "**Primary**: `schema:Role` - Schema.org role"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: **Close**: `prov:Role` - PROV-O role in activity
- literal_form: "**Close**: `prov:Role` - PROV-O role in activity"
predicate: EXACT_SYNONYM

View file

@ -380,163 +380,163 @@ classes:
in_language: zh
- literal_form: value: https://nde.nl/ontology/hc/aux/kroller-muller-sculpture
- literal_form: "value: https://nde.nl/ontology/hc/aux/kroller-muller-sculpture"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: Kroller-Muller Beeldentuin
- literal_form: "value: Kroller-Muller Beeldentuin"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: Paleis Het Loo Tuinen
- literal_form: "value: Paleis Het Loo Tuinen"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: Archeologisch Park Matilo
- literal_form: "value: Archeologisch Park Matilo"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: One of Europe's largest sculpture gardens with 160 works set in 25 hectares of park landscape within De Hoge Veluwe National Park.
- literal_form: "value: One of Europe's largest sculpture gardens with 160 works set in 25 hectares of park landscape within De Hoge Veluwe National Park."
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: range: OutdoorSiteTypeEnum
- literal_form: "range: OutdoorSiteTypeEnum"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: range: FeatureType
- literal_form: "range: FeatureType"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: SCULPTURE_GARDEN
- literal_form: "value: SCULPTURE_GARDEN"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: ARCHAEOLOGICAL_SITE
- literal_form: "value: ARCHAEOLOGICAL_SITE"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: FORMAL_GARDEN
- literal_form: "value: FORMAL_GARDEN"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: BotanicalInstitutionClassification
- literal_form: "value: BotanicalInstitutionClassification"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: ZoologicalInstitutionClassification
- literal_form: "value: ZoologicalInstitutionClassification"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value:
- literal_form: "value:"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value:
- literal_form: "value:"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: 160
- literal_form: "value: 160"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: 2500
- literal_form: "value: 2500"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value:
- literal_form: "value:"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: area_value: 25.0
- literal_form: "area_value: 25.0"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label: Included with museum ticket
- literal_form: "has_label: Included with museum ticket"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label: Paved paths
- literal_form: "has_label: Paved paths"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label: Wheelchair routes available
- literal_form: "has_label: Wheelchair routes available"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value:
- literal_form: "value:"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: area_value: 650.0
- literal_form: "area_value: 650.0"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label: Included with palace ticket
- literal_form: "has_label: Included with palace ticket"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value:
- literal_form: "value:"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: area_value: 3.5
- literal_form: "area_value: 3.5"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label: Free
- literal_form: "has_label: Free"
predicate: EXACT_SYNONYM

View file

@ -200,13 +200,13 @@ classes:
in_language: zh
- literal_form: value: hc:ArchiveOrganizationType
- literal_form: "value: hc:ArchiveOrganizationType"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: hc:HolySacredSiteType
- literal_form: "value: hc:HolySacredSiteType"
predicate: EXACT_SYNONYM

View file

@ -42,7 +42,7 @@ classes:
zh: >-
分类概念,根据分类系统中的共同特征、功能、材料或预期用途对产品或项目进行分组。
exact_mappings:
- schema:Category
- schema:CategoryCode
broad_mappings:
- skos:Concept
- schema:Thing

View file

@ -13,7 +13,9 @@ imports:
- ../slots/has_description
classes:
SupervisedHandling:
class_uri: schema:Policy
class_uri: hc:SupervisedHandling
close_mappings:
- schema:Thing
description: >-
Policy mandating that access to or manipulation of materials occurs only under staff observation and guidance to ensure proper care.
alt_descriptions:

View file

@ -44,7 +44,6 @@ classes:
broad_mappings:
- prov:Entity
related_mappings:
- schema:Award
- crm:E7_Activity
- schema:Organization
slots: []

View file

@ -24,13 +24,12 @@ classes:
ar: نسخ فيديو YouTube بما في ذلك معرف الفيديو واللغة ونوع النسخ (يدوي أو مولد تلقائيًا) والنص الكامل وبيانات الاستخراج الوصفية للوصول وتحليل المحتوى.
id: Transkrip video YouTube termasuk ID video, bahasa, jenis transkrip (manual atau dihasilkan otomatis), teks lengkap, dan metadata ekstraksi untuk aksesibilitas dan analisis konten.
zh: YouTube视频字幕包括视频ID、语言、字幕类型手动或自动生成、 全文和提取元数据,用于无障碍访问和内容分析。
class_uri: schema:Transcript
class_uri: hc:YoutubeTranscript
close_mappings:
- schema:Transcript
- schema:MediaObject
related_mappings:
- oa:TextualBody
- prov:Entity
- schema:MediaObject
slots:
- in_language
structured_aliases:

View file

@ -573,7 +573,6 @@ enums:
SPONSORSHIP_EXHIBITION:
description: |
Corporate or individual sponsorship of temporary exhibition.
meaning: schema:SponsorAction
annotations:
category: sponsorship
typical_benefits: |
@ -586,7 +585,6 @@ enums:
SPONSORSHIP_GALLERY:
description: |
Named sponsorship of permanent gallery or wing.
meaning: schema:SponsorAction
annotations:
category: sponsorship
custodian_types: "G, M, B, H"
@ -595,7 +593,6 @@ enums:
description: |
Sponsorship of institutional event (gala, lecture series,
education program).
meaning: schema:SponsorAction
annotations:
category: sponsorship
custodian_types: "G, L, A, M, O, R, B, E, S, H, D"
@ -604,7 +601,6 @@ enums:
description: |
Sponsorship of ongoing program (education, outreach,
conservation, research).
meaning: schema:SponsorAction
annotations:
category: sponsorship
examples: |
@ -615,7 +611,6 @@ enums:
SPONSORSHIP_DIGITIZATION:
description: |
Sponsorship of digitization projects for online access.
meaning: schema:SponsorAction
annotations:
category: sponsorship
examples: |
@ -626,7 +621,6 @@ enums:
SPONSORSHIP_CONSERVATION:
description: |
Sponsorship of conservation or restoration work.
meaning: schema:SponsorAction
annotations:
category: sponsorship
examples: |

View file

@ -17,18 +17,13 @@ enums:
permissible_values:
NATIVE_BILINGUAL:
description: Native or bilingual proficiency
meaning: schema:Expert
FULL_PROFESSIONAL:
description: Full professional proficiency
meaning: schema:Advanced
PROFESSIONAL_WORKING:
description: Professional working proficiency
meaning: schema:Intermediate
LIMITED_WORKING:
description: Limited working proficiency
meaning: schema:Intermediate
ELEMENTARY:
description: Elementary proficiency
meaning: schema:Beginner
UNKNOWN:
description: Proficiency level not specified

View file

@ -57,7 +57,6 @@ enums:
description: |
Initial loan request submitted by borrowing institution.
Loan is awaiting review by lending institution.
meaning: schema:PendingAction
annotations:
loan_phase: "initiation"
next_states: '["UNDER_REVIEW", "DECLINED", "CANCELLED"]'
@ -110,7 +109,6 @@ enums:
description: |
Object in transit from lender to borrower.
May involve courier accompaniment for high-value items.
meaning: schema:InTransitAction
annotations:
loan_phase: "transit"
next_states: '["ON_LOAN", "RETURNED"]'
@ -144,7 +142,6 @@ enums:
description: |
Object in transit from borrower back to lender.
Return journey underway.
meaning: schema:InTransitAction
annotations:
loan_phase: "transit"
next_states: '["RETURNED"]'

View file

@ -23,7 +23,6 @@ enums:
meaning: schema:size
VISITOR_COUNT:
description: Number of visitors (annual, daily, etc.).
meaning: schema:numberOfAttendees
BUDGET_AMOUNT:
description: Financial amount (budget, revenue, cost).
meaning: schema:amount
@ -53,7 +52,6 @@ enums:
meaning: schema:numberOfItems
CHARACTER_COUNT:
description: Number of characters in text content.
meaning: schema:characterCount
WORD_COUNT:
description: Number of words in text content.
meaning: schema:wordCount

View file

@ -18,6 +18,7 @@ prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
schema: http://schema.org/
rec: https://w3id.org/rec#
imports:
- linkml:types
@ -34,7 +35,7 @@ enums:
# Educational spaces
CLASSROOM:
description: Teaching/learning space for group instruction
meaning: schema:Classroom
meaning: rec:Classroom
annotations:
usage_context: education
typical_capacity: "20-40"

View file

@ -538,6 +538,9 @@ imports:
- ./outbound_to
- ./overlap_with
- ./owned_by
- ./paid_amount
- ./paid_from
- ./paid_to
- ./part_of
- ./participate_in
- ./performed_by
@ -628,6 +631,7 @@ imports:
- ./threatened_by
- ./track
- ./transferred
- ./transferred_from
- ./transferred_to
- ./transmission
- ./transmit_through

View file

@ -31,6 +31,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
crm: http://www.cidoc-crm.org/cidoc-crm/
schema: http://schema.org/
la: https://linked.art/ns/terms/
imports:
- linkml:types
default_prefix: hc
@ -96,6 +97,7 @@ slots:
- schema:object # schemaorg.owl:27871-27890 - "The object upon which the action is carried out." Broader: object of any action, not specifically accessioning.
related_mappings:
- crm:P24_transferred_title_of # CIDOC_CRM_v7.1.3.rdf:1738-1750 - "Identifies the E18 Physical Thing involved in an E8 Acquisition." Legal transfer aspect of accessioning.
- la:added_member # Linked Art extensions: member added to a Set via an Addition activity
aliases:
- objects_added
examples:

View file

@ -24,6 +24,7 @@ prefixes:
skos: http://www.w3.org/2004/02/skos/core#
owl: http://www.w3.org/2002/07/owl#
schema: http://schema.org/
la: https://linked.art/ns/terms/
imports:
- linkml:types
default_prefix: hc
@ -87,6 +88,7 @@ slots:
close_mappings:
- owl:sameAs # W3C OWL 2 standard - Identity assertion (stronger claim than semantic equivalence).
- schema:sameAs # schemaorg.owl:34129-34148 - "URL unambiguously indicating the item's identity."
- la:equivalent # Linked Art extensions: equivalent instance (skos:exactMatch-like without Concept inference)
aliases:
- is_or_was_equivalent_to
- wikidata_equivalent

View file

@ -142,17 +142,17 @@ slots:
- has_or_had_accessibility_feature
- is_accessible
examples:
- value: "schema:wheelchairAccessible"
- value: "wheelchairAccessible"
description: Physical location is accessible by wheelchair
- value: "schema:audioDescription"
- value: "audioDescription"
description: Content includes audio description for visually impaired users
- value: "schema:captions"
- value: "captions"
description: Content includes captions for deaf or hard-of-hearing users
- value: "schema:signLanguage"
- value: "signLanguage"
description: Content includes sign language interpretation
- value: "schema:braille"
- value: "braille"
description: Materials available in Braille format
- value: "schema:largePrint"
- value: "largePrint"
description: Materials available in large print format
comments:
- Updated 2026-02-03 with verified ontology mappings and translations

View file

@ -25,6 +25,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
crm: http://www.cidoc-crm.org/cidoc-crm/
rico: https://www.ica.org/standards/RiC/ontology#
la: https://linked.art/ns/terms/
xsd: http://www.w3.org/2001/XMLSchema#
default_prefix: hc
@ -82,6 +83,8 @@ slots:
- crm:P49_has_former_or_current_keeper # CIDOC_CRM_v7.1.3.rdf:2383-2408 - includes former keepers (broader temporal scope)
- crm:P50_has_current_keeper # CIDOC_CRM_v7.1.3.rdf:2410-2424 - "current keeper" but domain E18 Physical Thing / range E39 Actor (typed objects)
- rico:hasOrHadHolder # RiC-O_1-1.rdf:6436-6475 - "has or had holder" — archival holding context, domain RecordResource/Instantiation
related_mappings:
- la:current_permanent_custodian # Linked Art extensions: normal/permanent custodian of a physical object (E19->E39)
comments:
- |
MIGRATED 2026-02-03 from has_or_had_custodian for conciseness.

View file

@ -23,6 +23,7 @@ prefixes:
schema: http://schema.org/
dcat: http://www.w3.org/ns/dcat#
dcterms: http://purl.org/dc/terms/
ardo: https://w3id.org/ardo/2.0/
imports:
- linkml:types
default_prefix: hc
@ -80,6 +81,8 @@ slots:
- dcat:keyword # dcat3.ttl:1208-1231 - "A keyword or tag describing a resource"
close_mappings:
- dcterms:subject # dcterms.rdf:1968-1988 - "A topic of the resource"
related_mappings:
- ardo:has_keyword # ArDO 2.0: links a thematic subcategory to a keyword (object property)
comments:
- "Used for discovery and classification."
annotations:

View file

@ -25,6 +25,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
org: http://www.w3.org/ns/org#
schema: http://schema.org/
la: https://linked.art/ns/terms/
xsd: http://www.w3.org/2001/XMLSchema#
default_prefix: hc
imports:
@ -80,5 +81,7 @@ slots:
exact_mappings:
- org:hasMember # org.rdf:427-446 - "Indicates a person who is a member of the subject Organization." Domain: Organization, Range: Agent. Organization membership only; this slot also covers collection elements.
- schema:member # schemaorg.owl:26055-26085 - "A member of an Organization or a ProgramMembership." Domain: Organization/ProgramMembership. Does not cover collection elements.
related_mappings:
- la:has_member # Linked Art extensions: membership (Set→Entity)
annotations:
custodian_types: '["*"]'

View file

@ -19,6 +19,7 @@ prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
dcterms: http://purl.org/dc/terms/
pca: http://rds.posccaesar.org/ontology/plm/rdl/
default_prefix: hc
imports:
- linkml:types
@ -82,3 +83,5 @@ slots:
custodian_types: '["*"]'
close_mappings:
- dcterms:conformsTo # dcterms.rdf:987-1010 - "An established standard to which the described resource conforms." Conformance relationship ≠ having a standard.
related_mappings:
- pca:PCA_100003538 # POSC Caesar RDL (PCA PLM core): Standard (class)

View file

@ -24,6 +24,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
schema: http://schema.org/
dcterms: http://purl.org/dc/terms/
pav: http://purl.org/pav/2.3#
default_prefix: hc
imports:
- linkml:types
@ -81,6 +82,7 @@ slots:
multivalued: false
related_mappings:
- dcterms:hasVersion # dcterms.rdf:1371-1395 — "A related resource that is a version, edition, or adaptation." Relates resources to each other, not a version identifier.
- pav:hasVersion # PAV 2.3 (used by ArDO): links a resource to a version resource
aliases:
- has_or_had_version
- api_ver

View file

@ -27,6 +27,7 @@ prefixes:
schema: http://schema.org/
crm: http://www.cidoc-crm.org/cidoc-crm/
rico: https://www.ica.org/standards/RiC/ontology#
la: https://linked.art/ns/terms/
imports:
- linkml:types
default_prefix: hc
@ -115,6 +116,7 @@ slots:
custodian_types: '["*"]'
related_mappings:
- schema:owns # schemaorg.owl:28732-28760 — "Things owned by the organization or person." Ownership ≠ custody/holding.
- la:current_permanent_custodian_of # Linked Art extensions: inverse of current_permanent_custodian (Actor->Physical Object)
close_mappings:
- crm:P49i_is_former_or_current_keeper_of # CIDOC_CRM:2410-2435 — "is former or current keeper of." Custody relationship.
- rico:isOrWasHolderOf # RiC-O_1-1.rdf:6436-6470 — "has or had holder" (inverse). Archives holding.

View file

@ -32,6 +32,7 @@ prefixes:
rico: https://www.ica.org/standards/RiC/ontology#
org: http://www.w3.org/ns/org#
foaf: http://xmlns.com/foaf/0.1/
la: https://linked.art/ns/terms/
default_prefix: hc
imports:
- linkml:types
@ -98,6 +99,7 @@ slots:
- rico:isOrWasMemberOf # RiC-O_1-1.rdf:14505-14550 - Person→Group (restricted domain)
related_mappings:
- foaf:member # foaf.ttl:410-417 - INVERSE: Group→Agent (not Agent→Group)
- la:member_of # Linked Art extensions: membership (Entity→Set)
annotations:
inverse_slot: has_or_had_member
deprecates: is_member_of

View file

@ -0,0 +1,41 @@
# ==============================================================================
# LinkML Slot Definition: paid_amount
# ==============================================================================
# Monetary amount paid in a payment/transfer context.
#
# ONTOLOGY ALIGNMENT (verified against linked.art terms):
#
# | Ontology | Property | Mapping | Notes |
# |----------------|------------------|---------|------------------------------------------------------|
# | **Linked Art** | `la:paid_amount` | exact | Payment -> Monetary Amount in Linked Art extensions. |
# | **Schema.org** | `schema:price` | close | Price/value expression, broader commerce usage. |
#
# CREATED: 2026-02-18
# ==============================================================================
id: https://nde.nl/ontology/hc/slot/paid_amount
name: paid_amount
title: Paid Amount
prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
la: https://linked.art/ns/terms/
schema: http://schema.org/
imports:
- linkml:types
default_prefix: hc
slots:
paid_amount:
slot_uri: hc:paidAmount
description: >-
Specifies the amount that was paid as part of a payment or transfer.
range: string
multivalued: false
exact_mappings:
- la:paid_amount
close_mappings:
- schema:price
aliases:
- is_or_was_paid_amount
annotations:
custodian_types: '["*"]'

View file

@ -0,0 +1,41 @@
# ==============================================================================
# LinkML Slot Definition: paid_from
# ==============================================================================
# Paying party/source in a payment context.
#
# ONTOLOGY ALIGNMENT (verified against linked.art terms):
#
# | Ontology | Property | Mapping | Notes |
# |----------------|----------------|---------|-----------------------------------------------------------|
# | **Linked Art** | `la:paid_from` | exact | Payment source actor in Linked Art extensions. |
# | **Schema.org** | `schema:buyer` | related | Buyer role; related commercial payer perspective. |
#
# CREATED: 2026-02-18
# ==============================================================================
id: https://nde.nl/ontology/hc/slot/paid_from
name: paid_from
title: Paid From
prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
la: https://linked.art/ns/terms/
schema: http://schema.org/
imports:
- linkml:types
default_prefix: hc
slots:
paid_from:
slot_uri: hc:paidFrom
description: >-
Identifies the party or source from which a payment originated.
range: string
multivalued: false
exact_mappings:
- la:paid_from
related_mappings:
- schema:buyer
aliases:
- is_or_was_paid_from
annotations:
custodian_types: '["*"]'

View file

@ -0,0 +1,41 @@
# ==============================================================================
# LinkML Slot Definition: paid_to
# ==============================================================================
# Receiving party/destination in a payment context.
#
# ONTOLOGY ALIGNMENT (verified against linked.art terms):
#
# | Ontology | Property | Mapping | Notes |
# |----------------|-----------------|---------|-----------------------------------------------------------|
# | **Linked Art** | `la:paid_to` | exact | Payment recipient actor in Linked Art extensions. |
# | **Schema.org** | `schema:seller` | related | Seller role; related commercial payee perspective. |
#
# CREATED: 2026-02-18
# ==============================================================================
id: https://nde.nl/ontology/hc/slot/paid_to
name: paid_to
title: Paid To
prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
la: https://linked.art/ns/terms/
schema: http://schema.org/
imports:
- linkml:types
default_prefix: hc
slots:
paid_to:
slot_uri: hc:paidTo
description: >-
Identifies the party or destination to which a payment was made.
range: string
multivalued: false
exact_mappings:
- la:paid_to
related_mappings:
- schema:seller
aliases:
- is_or_was_paid_to
annotations:
custodian_types: '["*"]'

View file

@ -35,6 +35,7 @@ prefixes:
time: http://www.w3.org/2006/time#
schema: http://schema.org/
dcterms: http://purl.org/dc/terms/
pav: http://purl.org/pav/2.3#
default_prefix: hc
imports:
- linkml:types
@ -110,6 +111,7 @@ slots:
- schema:predecessorOf # schemaorg.owl:30406-30420 - "Previous variant of product; ProductModel domain"
- schema:previousItem # schemaorg.owl:30559-30575 - "Preceding ListItem; ListItem domain"
- dcterms:replaces # dcterms.rdf:1827-1846 - "Supplants/supersedes described resource; implies replacement"
- pav:previousVersion # PAV 2.3 (used by ArDO): previous version link (version chain)
aliases:
- previous_observation
examples:

View file

@ -32,6 +32,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
crm: http://www.cidoc-crm.org/cidoc-crm/
schema: http://schema.org/
la: https://linked.art/ns/terms/
imports:
- linkml:types
default_prefix: hc
@ -98,6 +99,7 @@ slots:
- schema:object # schemaorg.owl:27871-27890 - "The object upon which the action is carried out." Broader: object of any action, not specifically deaccessioning.
related_mappings:
- crm:P24_transferred_title_of # CIDOC_CRM_v7.1.3.rdf:1738-1750 - "Identifies the E18 Physical Thing involved in an E8 Acquisition." Legal transfer aspect of deaccessioning/disposal.
- la:removed_member # Linked Art extensions: member removed from a Set via a Removal activity
aliases:
- objects_removed
examples:

View file

@ -22,6 +22,7 @@ prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
schema: http://schema.org/
la: https://linked.art/ns/terms/
skos: http://www.w3.org/2004/02/skos/core#
xsd: http://www.w3.org/2001/XMLSchema#
@ -83,5 +84,6 @@ slots:
- is_or_was_transferred
related_mappings:
- schema:TransferAction # schemaorg.owl:5808-5812 - "act of transferring animate or inanimate objects"
- la:transferred # Linked Art extensions: what was transferred (Transfer->Entity). Note: this slot is boolean, so mapping is conceptual only.
annotations:
custodian_types: '["*"]'

View file

@ -0,0 +1,45 @@
# ==============================================================================
# LinkML Slot Definition: transferred_from
# ==============================================================================
# Source party/place from which something was transferred.
#
# ONTOLOGY ALIGNMENT (verified against linked.art terms):
#
# | Ontology | Property | Mapping | Notes |
# |----------------|-----------------------|---------|-------------------------------------------------------------|
# | **Linked Art** | `la:transferred_from` | exact | Source entity in a transfer in Linked Art extensions. |
# | **CIDOC-CRM** | `crm:P27_moved_from` | narrow | Location-only origin (place), narrower than general source. |
# | **Schema.org** | `schema:fromLocation` | narrow | Location-only source in action logistics. |
#
# CREATED: 2026-02-18
# ==============================================================================
id: https://nde.nl/ontology/hc/slot/transferred_from
name: transferred_from
title: Transferred From
prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
la: https://linked.art/ns/terms/
crm: http://www.cidoc-crm.org/cidoc-crm/
schema: http://schema.org/
imports:
- linkml:types
default_prefix: hc
slots:
transferred_from:
slot_uri: hc:transferredFrom
description: >-
Identifies the source party, owner, custodian, or location from which
something was transferred.
range: string
multivalued: false
exact_mappings:
- la:transferred_from
narrow_mappings:
- crm:P27_moved_from
- schema:fromLocation
aliases:
- is_or_was_transferred_from
annotations:
custodian_types: '["*"]'

View file

@ -24,6 +24,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
schema: http://schema.org/
odrl: http://www.w3.org/ns/odrl/2/
la: https://linked.art/ns/terms/
skos: http://www.w3.org/2004/02/skos/core#
xsd: http://www.w3.org/2001/XMLSchema#
@ -86,5 +87,6 @@ slots:
- is_or_was_transferred_to
related_mappings:
- schema:TransferAction # schemaorg.owl:5808-5812 - "act of transferring animate or inanimate objects"
- la:transferred_to # Linked Art extensions: transferred to (Transfer→Entity)
annotations:
custodian_types: '["*"]'

View file

@ -0,0 +1,356 @@
#!/usr/bin/env node
/*
* Generate a Mermaid UML class diagram from the synced LinkML schemas.
*
* Output: frontend/public/data/heritage_custodian_ontology.mmd
*
* The Visualize page loads this file from /data/heritage_custodian_ontology.mmd.
*/
const fs = require('fs');
const path = require('path');
const SCHEMA_DIR = path.join(__dirname, '../public/schemas/20251121/linkml');
const MANIFEST_PATH = path.join(SCHEMA_DIR, 'manifest.json');
const OUTPUT_PATH = path.join(__dirname, '../public/data/heritage_custodian_ontology.mmd');
function readJson(p) {
return JSON.parse(fs.readFileSync(p, 'utf8'));
}
function readText(p) {
return fs.readFileSync(p, 'utf8');
}
function ensureDir(p) {
const dir = path.dirname(p);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
}
function safeArray(v) {
return Array.isArray(v) ? v : [];
}
function indentOf(line) {
const m = line.match(/^ */);
return m ? m[0].length : 0;
}
function isBlankOrComment(line) {
const t = line.trim();
return t === '' || t.startsWith('#');
}
function stripInlineComment(v) {
// Conservative: only strip comments when preceded by whitespace.
// This avoids clobbering URLs like http://example.com#fragment.
return v.replace(/\s+#.*$/, '').trim();
}
function stripQuotes(v) {
const s = v.trim();
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
return s.slice(1, -1);
}
return s;
}
function collectIndentedBlock(lines, startIndex, startIndent) {
const block = [lines[startIndex]];
for (let i = startIndex + 1; i < lines.length; i++) {
const line = lines[i];
if (isBlankOrComment(line)) {
block.push(line);
continue;
}
const ind = indentOf(line);
if (ind <= startIndent) break;
block.push(line);
}
return block;
}
function findScalarInBlock(blockLines, key, minIndent) {
// Returns a single-line scalar value for `${key}: value`.
// Ignores folded/literal multi-line scalars (>- / |).
for (const line of blockLines) {
if (isBlankOrComment(line)) continue;
if (indentOf(line) < minIndent) continue;
const m = line.match(new RegExp(`^\\s*${key}:\\s*(.+)\\s*$`));
if (!m) continue;
const raw = stripInlineComment(m[1]);
if (raw === '' || raw === '|' || raw === '>-') continue;
return stripQuotes(raw);
}
return null;
}
function findListInBlock(blockLines, key, minIndent) {
// Parses:
// key:
// - item
// - item2
const out = [];
for (let i = 0; i < blockLines.length; i++) {
const line = blockLines[i];
if (isBlankOrComment(line)) continue;
const ind = indentOf(line);
if (ind < minIndent) continue;
const keyMatch = line.match(new RegExp(`^\\s*${key}:\\s*$`));
if (!keyMatch) continue;
const keyIndent = ind;
for (let j = i + 1; j < blockLines.length; j++) {
const l = blockLines[j];
if (isBlankOrComment(l)) continue;
const li = indentOf(l);
if (li <= keyIndent) break;
const m = l.match(/^\s*-\s*(.+)\s*$/);
if (m) {
out.push(stripQuotes(stripInlineComment(m[1])));
}
}
break;
}
return out;
}
function parseSlotUsageOverrides(classBlockLines, minIndent) {
// Parses:
// slot_usage:
// slot_name:
// range: SomeClass
const overrides = new Map();
for (let i = 0; i < classBlockLines.length; i++) {
const line = classBlockLines[i];
if (isBlankOrComment(line)) continue;
const ind = indentOf(line);
if (ind < minIndent) continue;
if (!line.match(/^\s*slot_usage:\s*$/)) continue;
const usageIndent = ind;
let currentSlot = null;
let currentSlotIndent = null;
for (let j = i + 1; j < classBlockLines.length; j++) {
const l = classBlockLines[j];
if (isBlankOrComment(l)) continue;
const li = indentOf(l);
if (li <= usageIndent) break;
// Slot key line: ` slot_name:`
if (li === usageIndent + 2) {
const mKey = l.trim().match(/^([^\s:]+):\s*$/);
if (mKey) {
currentSlot = mKey[1];
currentSlotIndent = li;
continue;
}
}
if (currentSlot && currentSlotIndent !== null && li > currentSlotIndent) {
const mRange = l.match(/^\s*range:\s*(.+)\s*$/);
if (mRange) {
const range = stripQuotes(stripInlineComment(mRange[1]));
if (range) overrides.set(currentSlot, range);
}
}
}
break;
}
return overrides;
}
function extractNamedMappingBlock(text, rootKey, itemName) {
// Extract the YAML-ish block for `rootKey: { itemName: {...} }` without parsing YAML.
// This is resilient to invalid YAML elsewhere in the file.
const lines = text.split(/\r?\n/);
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (isBlankOrComment(line)) continue;
const rootMatch = line.match(new RegExp(`^\\s*${rootKey}:\\s*$`));
if (!rootMatch) continue;
const rootIndent = indentOf(line);
for (let j = i + 1; j < lines.length; j++) {
const l = lines[j];
if (isBlankOrComment(l)) continue;
const li = indentOf(l);
if (li <= rootIndent) break;
if (li === rootIndent + 2 && l.trim() === `${itemName}:`) {
return {
blockLines: collectIndentedBlock(lines, j, li),
baseIndent: li,
};
}
}
}
return null;
}
function extractAllNamedMappingBlocks(text, rootKey) {
// Extract all mapping entry blocks under `rootKey:`
// Example:
// classes:
// Foo:
// ...
// Bar:
// ...
const lines = text.split(/\r?\n/);
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (isBlankOrComment(line)) continue;
const rootMatch = line.match(new RegExp(`^\\s*${rootKey}:\\s*$`));
if (!rootMatch) continue;
const rootIndent = indentOf(line);
const blocks = [];
for (let j = i + 1; j < lines.length; j++) {
const l = lines[j];
if (isBlankOrComment(l)) continue;
const li = indentOf(l);
if (li <= rootIndent) break;
if (li === rootIndent + 2) {
const mKey = l.trim().match(/^([^\s:]+):\s*$/);
if (mKey) {
blocks.push({
name: mKey[1],
blockLines: collectIndentedBlock(lines, j, li),
baseIndent: li,
});
}
}
}
return blocks;
}
return [];
}
function main() {
if (!fs.existsSync(MANIFEST_PATH)) {
console.error(`[generate-ontology-uml] Missing manifest: ${MANIFEST_PATH}`);
console.error('[generate-ontology-uml] Run pnpm run generate-manifest first.');
process.exit(1);
}
const manifest = readJson(MANIFEST_PATH);
const classCategory = (manifest.categories || []).find((c) => c.name === 'class');
const slotCategory = (manifest.categories || []).find((c) => c.name === 'slot');
const classFiles = safeArray(classCategory && classCategory.files);
const slotFiles = safeArray(slotCategory && slotCategory.files);
const slotIndex = new Map();
for (const f of slotFiles) {
const fullPath = path.join(SCHEMA_DIR, f.path);
if (!fs.existsSync(fullPath)) continue;
const text = readText(fullPath);
const slotBlocks = extractAllNamedMappingBlocks(text, 'slots');
for (const b of slotBlocks) {
const range = findScalarInBlock(b.blockLines, 'range', b.baseIndent + 2);
const multivaluedRaw = findScalarInBlock(b.blockLines, 'multivalued', b.baseIndent + 2);
const multivalued = multivaluedRaw === 'true' || multivaluedRaw === 'True';
if (!slotIndex.has(b.name)) {
slotIndex.set(b.name, {
range,
multivalued,
});
}
}
}
const classIndex = new Map();
for (const f of classFiles) {
const fullPath = path.join(SCHEMA_DIR, f.path);
if (!fs.existsSync(fullPath)) continue;
const text = readText(fullPath);
const classBlocks = extractAllNamedMappingBlocks(text, 'classes');
for (const b of classBlocks) {
const baseIndent = b.baseIndent;
const parent = findScalarInBlock(b.blockLines, 'is_a', baseIndent + 2);
const slots = findListInBlock(b.blockLines, 'slots', baseIndent + 2);
const overrides = parseSlotUsageOverrides(b.blockLines, baseIndent + 2);
if (!classIndex.has(b.name)) {
classIndex.set(b.name, {
is_a: parent,
slots,
slot_usage_overrides: overrides,
});
}
}
}
const classNames = [...classIndex.keys()].sort((a, b) => a.localeCompare(b));
const lines = [];
lines.push('```mermaid');
lines.push('classDiagram');
lines.push(`%% Generated from LinkML manifest: /schemas/20251121/linkml/manifest.json`);
if (manifest.generated) {
lines.push(`%% Manifest generated: ${manifest.generated}`);
}
lines.push('direction LR');
for (const c of classNames) {
lines.push(`class ${c}`);
}
const edgeSet = new Set();
const addEdge = (a, b, kind, label) => {
const key = `${a}|${b}|${kind}|${label || ''}`;
if (edgeSet.has(key)) return;
edgeSet.add(key);
if (kind === 'inheritance') {
lines.push(`${b} <|-- ${a}`);
return;
}
if (kind === 'association') {
lines.push(`${a} --> ${b} : ${label}`);
}
};
for (const [className, classDef] of classIndex.entries()) {
const parent = classDef.is_a;
if (parent && classIndex.has(parent)) {
addEdge(className, parent, 'inheritance');
}
const slots = safeArray(classDef.slots);
const slotUsageOverrides = classDef.slot_usage_overrides instanceof Map ? classDef.slot_usage_overrides : new Map();
for (const slotName of slots) {
const slotDef = slotIndex.get(slotName) || {};
const range = slotUsageOverrides.get(slotName) || slotDef.range;
if (!range) continue;
if (classIndex.has(range)) {
addEdge(className, range, 'association', slotName);
}
}
}
lines.push('```');
lines.push('');
ensureDir(OUTPUT_PATH);
fs.writeFileSync(OUTPUT_PATH, lines.join('\n'), 'utf8');
console.log(`[generate-ontology-uml] Wrote ${OUTPUT_PATH}`);
console.log(`[generate-ontology-uml] Classes: ${classIndex.size}, Slots indexed: ${slotIndex.size}, Edges: ${edgeSet.size}`);
}
main();

View file

@ -263,6 +263,25 @@
flex-shrink: 0;
}
.mapping-explorer__schema-status {
display: flex;
align-items: center;
height: 34px;
padding: 0 10px;
border-radius: 6px;
border: 1px solid #e0e0e0;
background: #ffffff;
color: #172a59;
font-size: 12px;
font-family: 'JetBrains Mono', 'Fira Code', monospace;
white-space: nowrap;
}
.mapping-explorer__schema-status--warn {
border-color: #fa5200;
box-shadow: 0 0 0 2px rgba(250, 82, 0, 0.15);
}
/* ============================================================================
* STATISTICS BAR
* ============================================================================ */
@ -528,6 +547,29 @@
white-space: nowrap;
}
.mapping-explorer__schema-warning {
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
border-radius: 999px;
border: 1px solid rgba(250, 82, 0, 0.45);
background: rgba(250, 82, 0, 0.12);
color: #fa5200;
font-size: 12px;
font-weight: 700;
line-height: 1;
}
.mapping-explorer__schema-warning-text {
margin-left: 8px;
font-size: 12px;
color: #fa5200;
font-weight: 600;
}
/* ============================================================================
* CENTER PANEL: MAPPING ARROWS
* ============================================================================ */

View file

@ -25,6 +25,7 @@ import {
type MappingStatus,
type CategoryGroup,
} from '../../lib/linkml/custodian-data-mappings';
import { loadManifest } from '../../lib/linkml/schema-loader';
import './MappingExplorer.css';
// ============================================================================
@ -139,6 +140,7 @@ interface SourceTreeItemProps {
isSelected: boolean;
selectedField: FieldMapping | null;
dataSource: DataSourceType;
getFieldSchemaValidity?: (field: FieldMapping) => { classOk: boolean; slotOk: boolean };
onToggle: () => void;
onSelectSource: () => void;
onSelectField: (field: FieldMapping) => void;
@ -156,6 +158,7 @@ const SourceTreeItem: React.FC<SourceTreeItemProps> = ({
isSelected,
selectedField,
dataSource,
getFieldSchemaValidity,
onToggle,
onSelectSource,
onSelectField,
@ -182,19 +185,32 @@ const SourceTreeItem: React.FC<SourceTreeItemProps> = ({
{isExpanded && (
<div className="mapping-explorer__field-list">
{mapping.fields.map((field, idx) => (
<div
key={idx}
className={`mapping-explorer__field-item ${selectedField === field ? 'mapping-explorer__field-item--selected' : ''}`}
onClick={() => onSelectField(field)}
>
<span className="mapping-explorer__field-path">
{(field.sourcePath ?? '—').replace(`${mapping.sourceBlock}.`, '')}
</span>
{field.status && <StatusBadge status={field.status} />}
<TransformationBadge type={field.transformation} />
</div>
))}
{mapping.fields.map((field, idx) => {
const validity = getFieldSchemaValidity?.(field);
const isInvalid = validity ? (!validity.classOk || !validity.slotOk) : false;
return (
<div
key={idx}
className={`mapping-explorer__field-item ${selectedField === field ? 'mapping-explorer__field-item--selected' : ''}`}
onClick={() => onSelectField(field)}
>
<span className="mapping-explorer__field-path">
{(field.sourcePath ?? '—').replace(`${mapping.sourceBlock}.`, '')}
</span>
{isInvalid && (
<span
className="mapping-explorer__schema-warning"
title="Target class/slot not found in current LinkML schema"
>
!
</span>
)}
{field.status && <StatusBadge status={field.status} />}
<TransformationBadge type={field.transformation} />
</div>
);
})}
</div>
)}
</div>
@ -234,6 +250,7 @@ interface FieldDetailsPanelProps {
field: FieldMapping;
sourceBlock: string;
viewMode: 'linkml' | 'typedb' | 'rdf';
schemaValidity?: { classOk: boolean; slotOk: boolean };
translations: {
copyPath: string;
copyRdf: string;
@ -243,7 +260,7 @@ interface FieldDetailsPanelProps {
};
}
const FieldDetailsPanel: React.FC<FieldDetailsPanelProps> = ({ field, sourceBlock: _sourceBlock, viewMode, translations }) => {
const FieldDetailsPanel: React.FC<FieldDetailsPanelProps> = ({ field, sourceBlock: _sourceBlock, viewMode, schemaValidity, translations }) => {
const [copiedField, setCopiedField] = useState<string | null>(null);
const copyToClipboard = (text: string, fieldName: string) => {
@ -301,30 +318,36 @@ const FieldDetailsPanel: React.FC<FieldDetailsPanelProps> = ({ field, sourceBloc
)}
</div>
{viewMode === 'linkml' && (
<div className="mapping-explorer__details-section">
<h5>
<FileCode size={16} className="mapping-explorer__section-icon" />
LinkML Target
</h5>
<div className="mapping-explorer__details-row">
<span className="mapping-explorer__details-label">Class:</span>
<code className="mapping-explorer__details-value mapping-explorer__details-value--class">
{field.targetClass}
</code>
{viewMode === 'linkml' && (
<div className="mapping-explorer__details-section">
<h5>
<FileCode size={16} className="mapping-explorer__section-icon" />
LinkML Target
</h5>
<div className="mapping-explorer__details-row">
<span className="mapping-explorer__details-label">Class:</span>
<code className="mapping-explorer__details-value mapping-explorer__details-value--class">
{field.targetClass}
</code>
{schemaValidity && !schemaValidity.classOk && (
<span className="mapping-explorer__schema-warning-text">Unknown in schema</span>
)}
</div>
<div className="mapping-explorer__details-row">
<span className="mapping-explorer__details-label">Slot:</span>
<code className="mapping-explorer__details-value">{field.targetSlot}</code>
{schemaValidity && !schemaValidity.slotOk && (
<span className="mapping-explorer__schema-warning-text">Unknown in schema</span>
)}
</div>
<div className="mapping-explorer__details-row">
<span className="mapping-explorer__details-label">Required:</span>
<span className={`mapping-explorer__details-value ${field.required ? 'mapping-explorer__details-value--required' : ''}`}>
{field.required ? 'Yes' : 'No'}
</span>
</div>
</div>
<div className="mapping-explorer__details-row">
<span className="mapping-explorer__details-label">Slot:</span>
<code className="mapping-explorer__details-value">{field.targetSlot}</code>
</div>
<div className="mapping-explorer__details-row">
<span className="mapping-explorer__details-label">Required:</span>
<span className={`mapping-explorer__details-value ${field.required ? 'mapping-explorer__details-value--required' : ''}`}>
{field.required ? 'Yes' : 'No'}
</span>
</div>
</div>
)}
)}
{viewMode === 'typedb' && field.typedbEntity && (
<div className="mapping-explorer__details-section">
@ -522,6 +545,8 @@ export const MappingExplorer: React.FC<MappingExplorerProps> = ({ language = 'en
const [viewMode, setViewMode] = useState<'linkml' | 'typedb' | 'rdf'>('linkml');
const [isExporting, setIsExporting] = useState(false);
const [schemaIndex, setSchemaIndex] = useState<{ classes: Set<string>; slots: Set<string>; generated?: string } | null>(null);
// Reset selection when data source changes
useEffect(() => {
const defaultSource = dataSource === 'custodian' ? 'ghcid' : 'profile_identity';
@ -539,6 +564,56 @@ export const MappingExplorer: React.FC<MappingExplorerProps> = ({ language = 'en
const currentMappings = useMemo(() => getMappingsForDataSource(dataSource), [dataSource]);
const currentCategories = useMemo(() => getCategoriesForDataSource(dataSource), [dataSource]);
// Load LinkML schema manifest for validation (best-effort)
useEffect(() => {
let cancelled = false;
(async () => {
try {
const manifest = await loadManifest();
if (!manifest || cancelled) return;
const classes = new Set<string>();
const slots = new Set<string>();
for (const category of manifest.categories ?? []) {
if (category.name === 'class') {
for (const f of category.files ?? []) classes.add(f.name);
}
if (category.name === 'slot') {
for (const f of category.files ?? []) slots.add(f.name);
}
}
setSchemaIndex({ classes, slots, generated: manifest.generated });
} catch (e) {
// Ignore - mapping explorer should remain usable without schema assets
console.warn('[MappingExplorer] Could not load LinkML manifest for validation', e);
}
})();
return () => {
cancelled = true;
};
}, []);
const getFieldSchemaValidity = useCallback((field: FieldMapping) => {
if (!schemaIndex) return { classOk: true, slotOk: true };
const classOk = field.targetClass ? schemaIndex.classes.has(field.targetClass) : true;
const slotOk = field.targetSlot ? schemaIndex.slots.has(field.targetSlot) : true;
return { classOk, slotOk };
}, [schemaIndex]);
const schemaMismatchCounts = useMemo(() => {
if (!schemaIndex) return { invalidFields: 0 };
let invalidFields = 0;
currentMappings.forEach(m => {
m.fields.forEach(f => {
const v = getFieldSchemaValidity(f);
if (!v.classOk || !v.slotOk) invalidFields++;
});
});
return { invalidFields };
}, [schemaIndex, currentMappings, getFieldSchemaValidity]);
// Status counts for statistics panel
const statusCounts = useMemo((): StatusCounts => {
const counts: StatusCounts = { mapped: 0, partial: 0, out_of_scope: 0, future: 0 };
@ -849,6 +924,15 @@ export const MappingExplorer: React.FC<MappingExplorerProps> = ({ language = 'en
<Download size={16} />
{isExporting ? t('exporting') : t('exportLinkMLMap')}
</button>
{schemaIndex && (
<div
className={`mapping-explorer__schema-status ${schemaMismatchCounts.invalidFields > 0 ? 'mapping-explorer__schema-status--warn' : ''}`}
title={`LinkML schema manifest loaded${schemaIndex.generated ? ` (generated: ${schemaIndex.generated})` : ''}`}
>
Schema: {schemaMismatchCounts.invalidFields === 0 ? 'OK' : `${schemaMismatchCounts.invalidFields} invalid targets`}
</div>
)}
</div>
</div>
@ -872,6 +956,7 @@ export const MappingExplorer: React.FC<MappingExplorerProps> = ({ language = 'en
isSelected={selectedSource === mapping.sourceBlock}
selectedField={selectedField}
dataSource={dataSource}
getFieldSchemaValidity={getFieldSchemaValidity}
onToggle={() => toggleSource(mapping.sourceBlock)}
onSelectSource={() => handleSelectSource(mapping.sourceBlock)}
onSelectField={handleSelectField}
@ -913,6 +998,7 @@ export const MappingExplorer: React.FC<MappingExplorerProps> = ({ language = 'en
field={selectedField}
sourceBlock={selectedSource}
viewMode={viewMode}
schemaValidity={getFieldSchemaValidity(selectedField)}
translations={{
copyPath: t('copyPath'),
copyRdf: t('copyRdf'),

View file

@ -508,7 +508,7 @@ Example: NL-NH-AMS-M-RM (Rijksmuseum, Amsterdam, Netherlands)
GHCIDs are deterministically generated and hashed to multiple UUID formats
for different use cases (UUID v5 for primary, UUID v8 for future-proofing).
`.trim(),
linkmlClass: 'GHCID',
linkmlClass: 'GHCIdentifier',
typedbEntity: 'ghcid',
provenance: {
sourceType: 'computed',
@ -518,8 +518,8 @@ for different use cases (UUID v5 for primary, UUID v8 for future-proofing).
{
sourcePath: 'ghcid.ghcid_current',
sourceDescription: 'Current GHCID string',
targetClass: 'GHCID',
targetSlot: 'identified_by',
targetClass: 'GHCIdentifier',
targetSlot: 'has_value',
transformation: 'direct',
typedbEntity: 'ghcid',
typedbAttribute: 'ghcid-string',
@ -534,8 +534,8 @@ for different use cases (UUID v5 for primary, UUID v8 for future-proofing).
{
sourcePath: 'ghcid.ghcid_uuid',
sourceDescription: 'UUID v5 derived from GHCID string',
targetClass: 'GHCID',
targetSlot: 'identified_by',
targetClass: 'GHCIdentifier',
targetSlot: 'has_value',
transformation: 'computed',
transformationDetails: 'UUID v5 generated using SHA-1 hash of GHCID string with heritage namespace',
typedbEntity: 'ghcid',
@ -550,8 +550,8 @@ for different use cases (UUID v5 for primary, UUID v8 for future-proofing).
{
sourcePath: 'ghcid.ghcid_numeric',
sourceDescription: '64-bit numeric ID for database optimization',
targetClass: 'GHCID',
targetSlot: 'identified_by',
targetClass: 'GHCIdentifier',
targetSlot: 'has_value',
transformation: 'computed',
transformationDetails: 'SHA-256 hash truncated to 64-bit integer',
typedbEntity: 'ghcid',
@ -561,8 +561,8 @@ for different use cases (UUID v5 for primary, UUID v8 for future-proofing).
{
sourcePath: 'ghcid.location_resolution',
sourceDescription: 'GeoNames resolution metadata',
targetClass: 'GHCID',
targetSlot: 'has_location',
targetClass: 'LocationResolution',
targetSlot: 'based_on',
transformation: 'nested',
transformationDetails: 'Maps to LocationResolution class with GeoNames provenance',
typedbEntity: 'location-resolution',
@ -784,7 +784,7 @@ Each enrichment creates a CustodianObservation with google_maps_api provenance.
apiEndpoint: 'https://maps.googleapis.com/maps/api/place/',
updateFrequency: 'On-demand',
},
generatedClasses: ['Place', 'GeoCoordinates', 'OpeningHours'],
generatedClasses: ['Place', 'SourceCoordinates', 'OpeningHours'],
fields: [
{
sourcePath: 'google_maps_enrichment.place_id',
@ -804,7 +804,7 @@ Each enrichment creates a CustodianObservation with google_maps_api provenance.
{
sourcePath: 'google_maps_enrichment.coordinates.latitude',
sourceDescription: 'Latitude coordinate',
targetClass: 'GeoCoordinates',
targetClass: 'SourceCoordinates',
targetSlot: 'has_latitude',
transformation: 'nested',
typedbEntity: 'geo-coordinates',
@ -820,7 +820,7 @@ Each enrichment creates a CustodianObservation with google_maps_api provenance.
{
sourcePath: 'google_maps_enrichment.coordinates.longitude',
sourceDescription: 'Longitude coordinate',
targetClass: 'GeoCoordinates',
targetClass: 'SourceCoordinates',
targetSlot: 'has_longitude',
transformation: 'nested',
typedbEntity: 'geo-coordinates',
@ -964,7 +964,7 @@ Creates a CustodianObservation with wikidata_api provenance.
dataTier: 'TIER_3_CROWD_SOURCED',
apiEndpoint: 'https://www.wikidata.org/wiki/Special:EntityData/',
},
generatedClasses: ['WikidataEntity', 'Sitelink'],
generatedClasses: ['WikidataEntity', 'WikidataSitelinks'],
fields: [
{
sourcePath: 'wikidata_enrichment.entity_id',
@ -1006,7 +1006,7 @@ Creates a CustodianObservation with wikidata_api provenance.
{
sourcePath: 'wikidata_enrichment.sitelinks',
sourceDescription: 'Links to Wikipedia articles',
targetClass: 'Sitelink',
targetClass: 'WikidataSitelinks',
targetSlot: 'has_url',
transformation: 'array_map',
transformationDetails: 'Each sitelink maps to Wikipedia article URL',
@ -1033,7 +1033,7 @@ Creates a CustodianObservation with wikidata_api provenance.
{
sourcePath: 'wikidata_enrichment.coordinates',
sourceDescription: 'Geographic coordinates from Wikidata (P625)',
targetClass: 'GeoCoordinates',
targetClass: 'WikidataCoordinates',
targetSlot: 'has_coordinates',
transformation: 'nested',
typedbEntity: 'geo-coordinates',
@ -1104,7 +1104,7 @@ This is the single source of truth for the custodian's physical location.
sourcePath: 'location.city',
sourceDescription: 'City name',
targetClass: 'Place',
targetSlot: 'cover_place',
targetSlot: 'has_locality',
transformation: 'direct',
typedbEntity: 'place',
typedbAttribute: 'city',
@ -1119,7 +1119,7 @@ This is the single source of truth for the custodian's physical location.
sourcePath: 'location.country',
sourceDescription: 'ISO 3166-1 alpha-2 country code',
targetClass: 'Place',
targetSlot: 'cover_country',
targetSlot: 'in_country',
transformation: 'lookup',
transformationDetails: 'Maps to CountryCodeEnum',
typedbEntity: 'place',
@ -1139,7 +1139,7 @@ This is the single source of truth for the custodian's physical location.
sourcePath: 'location.region',
sourceDescription: 'Region/province name',
targetClass: 'Place',
targetSlot: 'region',
targetSlot: 'has_geographic_subdivision',
transformation: 'direct',
typedbEntity: 'place',
typedbAttribute: 'region',
@ -1214,7 +1214,7 @@ All claims must have XPath provenance per Rule 6.
sourcePath: 'web_enrichment.source_url',
sourceDescription: 'URL of scraped page',
targetClass: 'WebObservation',
targetSlot: 'source_url',
targetSlot: 'retrieved_from',
transformation: 'direct',
typedbEntity: 'web-observation',
typedbAttribute: 'source-url',
@ -1226,7 +1226,7 @@ All claims must have XPath provenance per Rule 6.
sourcePath: 'web_enrichment.retrieved_on',
sourceDescription: 'Timestamp when page was archived',
targetClass: 'WebObservation',
targetSlot: 'retrieved_on',
targetSlot: 'retrieved_at',
transformation: 'temporal',
typedbEntity: 'web-observation',
typedbAttribute: 'retrieved-on',
@ -1326,7 +1326,7 @@ Claims without XPath provenance are fabricated and must be removed per Rule 6.
sourcePath: 'web_claims[].source_url',
sourceDescription: 'URL where claim was extracted',
targetClass: 'WebClaim',
targetSlot: 'source_url',
targetSlot: 'retrieved_from',
transformation: 'direct',
typedbEntity: 'web-claim',
typedbAttribute: 'source-url',
@ -2222,7 +2222,7 @@ Classes: RegionalArchive, ProvincialArchive, ProvincialHistoricalArchive,
sourcePath: 'region',
sourceDescription: 'Geographic region served',
targetClass: 'RegionalArchive',
targetSlot: 'region',
targetSlot: 'cover_geographic_subdivision',
transformation: 'direct',
typedbEntity: 'regional-archive',
typedbAttribute: 'region',
@ -3500,7 +3500,7 @@ Supports:
sourcePath: 'person.experience[].role',
sourceDescription: 'Job title/role',
targetClass: 'WorkExperience',
targetSlot: 'role_title',
targetSlot: 'has_position',
transformation: 'direct',
typedbEntity: 'work-experience',
typedbAttribute: 'role-title',
@ -3510,8 +3510,8 @@ Supports:
{
sourcePath: 'person.education[].institution',
sourceDescription: 'Educational institution',
targetClass: 'EducationCredential',
targetSlot: 'affiliated_with',
targetClass: 'Education',
targetSlot: 'has_label',
transformation: 'direct',
typedbEntity: 'education-credential',
typedbAttribute: 'institution',
@ -3520,7 +3520,7 @@ Supports:
},
],
generatedClasses: [
'WorkExperience', 'EducationCredential', 'StaffRole', 'StaffRoles',
'WorkExperience', 'Education', 'StaffRole', 'StaffRoles',
],
exampleYaml: `
# Work and education
@ -3645,7 +3645,7 @@ Includes:
{
sourcePath: 'api_endpoints.iiif',
sourceDescription: 'IIIF Image API',
targetClass: 'IIPImageServer',
targetClass: 'IIIF',
targetSlot: 'has_url',
transformation: 'direct',
typedbEntity: 'iip-image-server',
@ -3656,7 +3656,7 @@ Includes:
],
generatedClasses: [
'DataServiceEndpoint', 'OAIPMHEndpoint', 'SearchAPI', 'FileAPI', 'EADDownload',
'METSAPI', 'IIPImageServer', 'InternetOfThings',
'METSAPI', 'IIIF', 'InternetOfThings',
],
exampleYaml: `
# API endpoints
@ -4208,7 +4208,7 @@ Includes:
sourcePath: 'funding.agendas',
sourceDescription: 'Funding agendas',
targetClass: 'FundingAgenda',
targetSlot: 'related_agenda',
targetSlot: 'has_detail',
transformation: 'array_direct',
typedbEntity: 'funding-agenda',
typedbAttribute: 'agenda-name',
@ -4797,7 +4797,7 @@ location, and organizational metadata like company size and industry.
This data is crucial for understanding a person's professional
trajectory and their experience in heritage-related roles.
`.trim(),
linkmlClass: 'CareerPosition',
linkmlClass: 'WorkExperience',
typedbEntity: 'career-position',
provenance: {
sourceType: 'external_api',
@ -4807,8 +4807,8 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].organization',
sourceDescription: 'Employer organization name',
targetClass: 'CareerPosition',
targetSlot: 'affiliated_with',
targetClass: 'WorkExperience',
targetSlot: 'employed_by',
typedbAttribute: 'organization-name',
rdfPredicate: 'schema:worksFor',
transformation: 'direct',
@ -4818,7 +4818,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].organization_linkedin',
sourceDescription: 'LinkedIn URL for organization',
targetClass: 'CareerPosition',
targetClass: 'Employer',
targetSlot: 'has_url',
typedbAttribute: 'organization-linkedin-url',
rdfPredicate: 'schema:sameAs',
@ -4829,8 +4829,8 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].role',
sourceDescription: 'Job title/role',
targetClass: 'CareerPosition',
targetSlot: 'has_role',
targetClass: 'WorkExperience',
targetSlot: 'has_position',
typedbAttribute: 'role-title',
rdfPredicate: 'schema:jobTitle',
transformation: 'direct',
@ -4840,8 +4840,8 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].role_english',
sourceDescription: 'English translation of role',
targetClass: 'CareerPosition',
targetSlot: 'has_role',
targetClass: 'WorkExperience',
targetSlot: 'has_position',
typedbAttribute: 'role-title-english',
rdfPredicate: 'schema:jobTitle',
transformation: 'direct',
@ -4851,7 +4851,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].dates',
sourceDescription: 'Employment date range',
targetClass: 'CareerPosition',
targetClass: 'WorkExperience',
targetSlot: 'temporal_extent',
typedbAttribute: 'date-range',
rdfPredicate: 'schema:temporalCoverage',
@ -4862,7 +4862,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].duration',
sourceDescription: 'Employment duration',
targetClass: 'CareerPosition',
targetClass: 'WorkExperience',
targetSlot: 'temporal_extent',
typedbAttribute: 'duration',
rdfPredicate: 'schema:duration',
@ -4873,7 +4873,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].location',
sourceDescription: 'Work location',
targetClass: 'CareerPosition',
targetClass: 'WorkExperience',
targetSlot: 'has_location',
typedbAttribute: 'work-location',
rdfPredicate: 'schema:workLocation',
@ -4884,7 +4884,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].current',
sourceDescription: 'Is current position',
targetClass: 'CareerPosition',
targetClass: 'WorkExperience',
targetSlot: 'current',
typedbAttribute: 'is-current',
rdfPredicate: 'schema:currentPosition',
@ -4895,7 +4895,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].company_size',
sourceDescription: 'Company employee count range',
targetClass: 'CareerPosition',
targetClass: 'Employer',
targetSlot: 'has_detail',
typedbAttribute: 'company-size',
rdfPredicate: 'schema:numberOfEmployees',
@ -4906,7 +4906,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].company_founded',
sourceDescription: 'Year company was founded',
targetClass: 'CareerPosition',
targetClass: 'Employer',
targetSlot: 'founded_through',
typedbAttribute: 'company-founded-year',
rdfPredicate: 'schema:foundingDate',
@ -4917,7 +4917,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].company_type',
sourceDescription: 'Type of company',
targetClass: 'CareerPosition',
targetClass: 'Employer',
targetSlot: 'has_type',
typedbAttribute: 'company-type',
rdfPredicate: 'schema:additionalType',
@ -4928,7 +4928,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].industry',
sourceDescription: 'Industry sector',
targetClass: 'CareerPosition',
targetClass: 'Employer',
targetSlot: 'has_domain',
typedbAttribute: 'industry',
rdfPredicate: 'schema:industry',
@ -4939,7 +4939,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].department',
sourceDescription: 'Department within organization',
targetClass: 'CareerPosition',
targetClass: 'WorkExperience',
targetSlot: 'department_of',
typedbAttribute: 'department',
rdfPredicate: 'schema:department',
@ -4950,7 +4950,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].level',
sourceDescription: 'Seniority level',
targetClass: 'CareerPosition',
targetClass: 'WorkExperience',
targetSlot: 'has_level',
typedbAttribute: 'seniority-level',
rdfPredicate: 'schema:occupationalCategory',
@ -4961,7 +4961,7 @@ trajectory and their experience in heritage-related roles.
{
sourcePath: 'profile_data.career_history[].description',
sourceDescription: 'Role description',
targetClass: 'CareerPosition',
targetClass: 'WorkExperience',
targetSlot: 'has_description',
typedbAttribute: 'role-description',
rdfPredicate: 'schema:description',
@ -5175,7 +5175,7 @@ current institution, sector role, and years of heritage experience.
This provides a quick overview of where the person fits
within the heritage ecosystem.
`.trim(),
linkmlClass: 'HeritageRelevance',
linkmlClass: 'HeritageRelevanceAssessment',
typedbEntity: 'heritage-relevance',
provenance: {
sourceType: 'computed',
@ -5185,7 +5185,7 @@ within the heritage ecosystem.
{
sourcePath: 'heritage_sector_relevance.heritage_type',
sourceDescription: 'Heritage type code',
targetClass: 'HeritageRelevance',
targetClass: 'HeritageRelevanceAssessment',
targetSlot: 'has_type',
typedbAttribute: 'heritage-type-code',
rdfPredicate: 'glam:heritageType',
@ -5196,8 +5196,8 @@ within the heritage ecosystem.
{
sourcePath: 'heritage_sector_relevance.heritage_type_label',
sourceDescription: 'Heritage type label',
targetClass: 'HeritageRelevance',
targetSlot: 'has_label',
targetClass: 'HeritageRelevanceAssessment',
targetSlot: 'has_note',
typedbAttribute: 'heritage-type-label',
rdfPredicate: 'rdfs:label',
transformation: 'direct',
@ -5207,8 +5207,8 @@ within the heritage ecosystem.
{
sourcePath: 'heritage_sector_relevance.current_institution',
sourceDescription: 'Current heritage institution',
targetClass: 'HeritageRelevance',
targetSlot: 'affiliated_with',
targetClass: 'HeritageRelevanceAssessment',
targetSlot: 'has_note',
typedbAttribute: 'current-institution',
rdfPredicate: 'schema:worksFor',
transformation: 'direct',
@ -5218,7 +5218,7 @@ within the heritage ecosystem.
{
sourcePath: 'heritage_sector_relevance.institution_type',
sourceDescription: 'Type of institution',
targetClass: 'HeritageRelevance',
targetClass: 'HeritageRelevanceAssessment',
targetSlot: 'has_type',
typedbAttribute: 'institution-type',
rdfPredicate: 'schema:additionalType',
@ -5229,8 +5229,8 @@ within the heritage ecosystem.
{
sourcePath: 'heritage_sector_relevance.sector_role',
sourceDescription: 'Role within heritage sector',
targetClass: 'HeritageRelevance',
targetSlot: 'has_role',
targetClass: 'HeritageRelevanceAssessment',
targetSlot: 'has_note',
typedbAttribute: 'sector-role',
rdfPredicate: 'schema:jobTitle',
transformation: 'direct',
@ -5240,8 +5240,8 @@ within the heritage ecosystem.
{
sourcePath: 'heritage_sector_relevance.years_in_heritage',
sourceDescription: 'Years of heritage experience',
targetClass: 'HeritageRelevance',
targetSlot: 'has_detail',
targetClass: 'HeritageRelevanceAssessment',
targetSlot: 'has_score',
typedbAttribute: 'years-in-heritage',
rdfPredicate: 'schema:experienceYears',
transformation: 'direct',
@ -5273,7 +5273,7 @@ Extracted from full career history with relevance annotations.
Includes both current and past positions at heritage institutions
with notes explaining their relevance to the GLAM sector.
`.trim(),
linkmlClass: 'HeritageExperience',
linkmlClass: 'WorkExperience',
typedbEntity: 'heritage-experience',
provenance: {
sourceType: 'computed',
@ -5283,8 +5283,8 @@ with notes explaining their relevance to the GLAM sector.
{
sourcePath: 'profile_data.heritage_relevant_experience[].organization',
sourceDescription: 'Heritage organization name',
targetClass: 'HeritageExperience',
targetSlot: 'affiliated_with',
targetClass: 'WorkExperience',
targetSlot: 'employed_by',
typedbAttribute: 'heritage-org-name',
rdfPredicate: 'schema:worksFor',
transformation: 'direct',
@ -5294,8 +5294,8 @@ with notes explaining their relevance to the GLAM sector.
{
sourcePath: 'profile_data.heritage_relevant_experience[].role',
sourceDescription: 'Role at heritage organization',
targetClass: 'HeritageExperience',
targetSlot: 'has_role',
targetClass: 'WorkExperience',
targetSlot: 'has_position',
typedbAttribute: 'heritage-role',
rdfPredicate: 'schema:jobTitle',
transformation: 'direct',
@ -5305,8 +5305,8 @@ with notes explaining their relevance to the GLAM sector.
{
sourcePath: 'profile_data.heritage_relevant_experience[].relevance',
sourceDescription: 'Relevance explanation',
targetClass: 'HeritageExperience',
targetSlot: 'has_significance',
targetClass: 'WorkExperience',
targetSlot: 'has_note',
typedbAttribute: 'relevance-notes',
rdfPredicate: 'schema:description',
transformation: 'direct',
@ -5316,7 +5316,7 @@ with notes explaining their relevance to the GLAM sector.
{
sourcePath: 'profile_data.heritage_relevant_experience[].current',
sourceDescription: 'Is current position',
targetClass: 'HeritageExperience',
targetClass: 'WorkExperience',
targetSlot: 'current',
typedbAttribute: 'is-current-heritage',
rdfPredicate: 'schema:currentPosition',
@ -5325,7 +5325,7 @@ with notes explaining their relevance to the GLAM sector.
notes: 'Whether this is a current position',
},
],
generatedClasses: ['HeritageExperience'],
generatedClasses: ['WorkExperience'],
exampleYaml: `
profile_data:
heritage_relevant_experience:
@ -5354,7 +5354,7 @@ role title, and heritage classification.
These affiliations enable network analysis across the heritage
sector workforce.
`.trim(),
linkmlClass: 'Affiliation',
linkmlClass: 'Membership',
typedbEntity: 'affiliation',
provenance: {
sourceType: 'computed',
@ -5364,7 +5364,7 @@ sector workforce.
{
sourcePath: 'affiliations[].custodian_name',
sourceDescription: 'Heritage custodian name',
targetClass: 'Affiliation',
targetClass: 'Membership',
targetSlot: 'has_name',
typedbAttribute: 'custodian-name',
rdfPredicate: 'schema:memberOf',
@ -5375,8 +5375,8 @@ sector workforce.
{
sourcePath: 'affiliations[].custodian_slug',
sourceDescription: 'Custodian identifier slug',
targetClass: 'Affiliation',
targetSlot: 'identified_by',
targetClass: 'Membership',
targetSlot: 'has_slug',
typedbAttribute: 'custodian-slug',
rdfPredicate: 'schema:identifier',
transformation: 'direct',
@ -5386,8 +5386,8 @@ sector workforce.
{
sourcePath: 'affiliations[].role_title',
sourceDescription: 'Role at custodian',
targetClass: 'Affiliation',
targetSlot: 'role_title',
targetClass: 'Membership',
targetSlot: 'has_role',
typedbAttribute: 'affiliation-role',
rdfPredicate: 'schema:jobTitle',
transformation: 'direct',
@ -5397,8 +5397,8 @@ sector workforce.
{
sourcePath: 'affiliations[].heritage_relevant',
sourceDescription: 'Is heritage relevant',
targetClass: 'Affiliation',
targetSlot: 'has_significance',
targetClass: 'Membership',
targetSlot: 'has_note',
typedbAttribute: 'is-heritage-relevant',
rdfPredicate: 'glam:heritageRelevant',
transformation: 'direct',
@ -5408,7 +5408,7 @@ sector workforce.
{
sourcePath: 'affiliations[].heritage_type',
sourceDescription: 'Heritage type code',
targetClass: 'Affiliation',
targetClass: 'Membership',
targetSlot: 'has_type',
typedbAttribute: 'affiliation-heritage-type',
rdfPredicate: 'glam:heritageType',
@ -5419,7 +5419,7 @@ sector workforce.
{
sourcePath: 'affiliations[].current',
sourceDescription: 'Is current affiliation',
targetClass: 'Affiliation',
targetClass: 'Membership',
targetSlot: 'current',
typedbAttribute: 'is-current-affiliation',
rdfPredicate: 'schema:currentPosition',
@ -5430,7 +5430,7 @@ sector workforce.
{
sourcePath: 'affiliations[].observed_on',
sourceDescription: 'Observation timestamp',
targetClass: 'Affiliation',
targetClass: 'Membership',
targetSlot: 'observed_in',
typedbAttribute: 'observed-on',
rdfPredicate: 'prov:generatedAtTime',
@ -5441,8 +5441,8 @@ sector workforce.
{
sourcePath: 'affiliations[].source_url',
sourceDescription: 'Source URL for affiliation',
targetClass: 'Affiliation',
targetSlot: 'source_url',
targetClass: 'Membership',
targetSlot: 'retrieved_from',
typedbAttribute: 'affiliation-source-url',
rdfPredicate: 'prov:wasDerivedFrom',
transformation: 'direct',
@ -5450,7 +5450,7 @@ sector workforce.
notes: 'URL where affiliation was discovered',
},
],
generatedClasses: ['Affiliation'],
generatedClasses: ['Membership'],
exampleYaml: `
affiliations:
- custodian_name: Nationaal Archief
@ -5478,7 +5478,7 @@ and custodian records (heritage institution YAML files).
These links enable navigation between person profiles and
the institutions they work for.
`.trim(),
linkmlClass: 'LinkedRecords',
linkmlClass: 'FileLocation',
typedbEntity: 'linked-records',
provenance: {
sourceType: 'computed',
@ -5488,8 +5488,8 @@ the institutions they work for.
{
sourcePath: 'linked_records.staff_record.file',
sourceDescription: 'Staff record file path',
targetClass: 'LinkedRecords',
targetSlot: 'has_file_location',
targetClass: 'FileLocation',
targetSlot: 'has_value',
typedbAttribute: 'staff-record-path',
rdfPredicate: 'prov:wasDerivedFrom',
transformation: 'direct',
@ -5499,8 +5499,8 @@ the institutions they work for.
{
sourcePath: 'linked_records.staff_record.staff_id',
sourceDescription: 'Staff record ID',
targetClass: 'LinkedRecords',
targetSlot: 'staff_id',
targetClass: 'Identifier',
targetSlot: 'has_value',
typedbAttribute: 'staff-id',
rdfPredicate: 'schema:identifier',
transformation: 'direct',
@ -5510,8 +5510,8 @@ the institutions they work for.
{
sourcePath: 'linked_records.custodian_record.ghcid',
sourceDescription: 'Custodian GHCID',
targetClass: 'LinkedRecords',
targetSlot: 'identified_by',
targetClass: 'GHCIdentifier',
targetSlot: 'has_value',
typedbAttribute: 'linked-ghcid',
rdfPredicate: 'glam:ghcid',
transformation: 'direct',
@ -5521,7 +5521,7 @@ the institutions they work for.
{
sourcePath: 'linked_records.custodian_record.notes',
sourceDescription: 'Custodian record notes',
targetClass: 'LinkedRecords',
targetClass: 'FileLocation',
targetSlot: 'has_note',
typedbAttribute: 'custodian-notes',
rdfPredicate: 'schema:description',
@ -5530,7 +5530,7 @@ the institutions they work for.
notes: 'Additional notes about the custodian link',
},
],
generatedClasses: ['LinkedRecords'],
generatedClasses: ['FileLocation'],
exampleYaml: `
linked_records:
staff_record:
@ -5555,7 +5555,7 @@ with confidence scores indicating reliability.
Also includes profile photo URLs and external lookup service links.
`.trim(),
linkmlClass: 'ContactData',
linkmlClass: 'ContactDetails',
typedbEntity: 'contact-data',
provenance: {
sourceType: 'computed',
@ -5565,7 +5565,7 @@ Also includes profile photo URLs and external lookup service links.
{
sourcePath: 'contact_data.provenance.source',
sourceDescription: 'Contact data source',
targetClass: 'ContactData',
targetClass: 'ContactDetails',
targetSlot: 'has_source',
typedbAttribute: 'contact-source',
rdfPredicate: 'prov:wasAttributedTo',
@ -5576,7 +5576,7 @@ Also includes profile photo URLs and external lookup service links.
{
sourcePath: 'contact_data.emails[].email',
sourceDescription: 'Email address',
targetClass: 'ContactData',
targetClass: 'ContactDetails',
targetSlot: 'has_email_address',
typedbAttribute: 'email-address',
rdfPredicate: 'schema:email',
@ -5587,7 +5587,7 @@ Also includes profile photo URLs and external lookup service links.
{
sourcePath: 'contact_data.emails[].type',
sourceDescription: 'Email type',
targetClass: 'ContactData',
targetClass: 'ContactDetails',
targetSlot: 'has_type',
typedbAttribute: 'email-type',
rdfPredicate: 'schema:contactType',
@ -5598,7 +5598,7 @@ Also includes profile photo URLs and external lookup service links.
{
sourcePath: 'contact_data.emails[].confidence',
sourceDescription: 'Email confidence score',
targetClass: 'ContactData',
targetClass: 'ContactDetails',
targetSlot: 'has_confidence_measure',
typedbAttribute: 'email-confidence',
rdfPredicate: 'prov:confidence',
@ -5609,7 +5609,7 @@ Also includes profile photo URLs and external lookup service links.
{
sourcePath: 'contact_data.emails[].verified',
sourceDescription: 'Email verification status',
targetClass: 'ContactData',
targetClass: 'ContactDetails',
targetSlot: 'has_detail',
typedbAttribute: 'email-verified',
rdfPredicate: 'schema:verified',
@ -5620,7 +5620,7 @@ Also includes profile photo URLs and external lookup service links.
{
sourcePath: 'contact_data.profile_photo_url',
sourceDescription: 'Profile photo URL',
targetClass: 'ContactData',
targetClass: 'ContactDetails',
targetSlot: 'has_image',
typedbAttribute: 'profile-photo',
rdfPredicate: 'schema:image',
@ -5631,7 +5631,7 @@ Also includes profile photo URLs and external lookup service links.
{
sourcePath: 'contact_data.rocketreach_url',
sourceDescription: 'RocketReach lookup URL',
targetClass: 'ContactData',
targetClass: 'ContactDetails',
targetSlot: 'has_url',
typedbAttribute: 'rocketreach-url',
rdfPredicate: 'schema:sameAs',
@ -5640,7 +5640,7 @@ Also includes profile photo URLs and external lookup service links.
notes: 'Link to RocketReach profile lookup',
},
],
generatedClasses: ['ContactData'],
generatedClasses: ['ContactDetails'],
exampleYaml: `
contact_data:
provenance:
@ -5685,7 +5685,7 @@ reproducibility of the extraction process.
sourcePath: 'extraction_metadata.source_file',
sourceDescription: 'Source file path',
targetClass: 'ExtractionMetadata',
targetSlot: 'source_file',
targetSlot: 'has_file_location',
typedbAttribute: 'source-file-path',
rdfPredicate: 'prov:wasDerivedFrom',
transformation: 'direct',
@ -5696,7 +5696,7 @@ reproducibility of the extraction process.
sourcePath: 'extraction_metadata.staff_id',
sourceDescription: 'Staff identifier',
targetClass: 'ExtractionMetadata',
targetSlot: 'staff_id',
targetSlot: 'has_slug',
typedbAttribute: 'extraction-staff-id',
rdfPredicate: 'schema:identifier',
transformation: 'direct',
@ -5762,7 +5762,7 @@ reproducibility of the extraction process.
sourcePath: 'extraction_metadata.request_id',
sourceDescription: 'API request identifier',
targetClass: 'ExtractionMetadata',
targetSlot: 'request_id',
targetSlot: 'has_code',
typedbAttribute: 'api-request-id',
rdfPredicate: 'schema:identifier',
transformation: 'direct',
@ -5830,7 +5830,7 @@ This follows the WebObservation pattern for verifiable data claims.
sourcePath: 'web_claims[].source_url',
sourceDescription: 'URL source of claim',
targetClass: 'WebClaim',
targetSlot: 'source_url',
targetSlot: 'retrieved_from',
typedbAttribute: 'claim-source-url',
rdfPredicate: 'prov:wasDerivedFrom',
transformation: 'direct',
@ -5841,7 +5841,7 @@ This follows the WebObservation pattern for verifiable data claims.
sourcePath: 'web_claims[].retrieved_on',
sourceDescription: 'Retrieval timestamp',
targetClass: 'WebClaim',
targetSlot: 'retrieved_on',
targetSlot: 'retrieved_at',
typedbAttribute: 'claim-retrieved-on',
rdfPredicate: 'prov:generatedAtTime',
transformation: 'direct',
@ -5852,7 +5852,7 @@ This follows the WebObservation pattern for verifiable data claims.
sourcePath: 'web_claims[].retrieval_agent',
sourceDescription: 'Agent that retrieved claim',
targetClass: 'WebClaim',
targetSlot: 'retrieval_agent',
targetSlot: 'retrieved_by',
typedbAttribute: 'claim-retrieval-agent',
rdfPredicate: 'prov:wasAttributedTo',
transformation: 'direct',
@ -5941,7 +5941,7 @@ showing the semantic alignment between the ontologies.
sourcePath: 'extraction_metadata.linkedin_url',
sourceDescription: 'LinkedIn profile URL as primary source',
targetClass: 'PersonObservation',
targetSlot: 'source_url',
targetSlot: 'retrieved_from',
typedbAttribute: 'source-url',
rdfPredicate: 'prov:hadPrimarySource',
transformation: 'direct',
@ -5952,8 +5952,8 @@ showing the semantic alignment between the ontologies.
{
sourcePath: 'affiliations[].role_title',
sourceDescription: 'Role at heritage institution',
targetClass: 'Affiliation',
targetSlot: 'role_title',
targetClass: 'Membership',
targetSlot: 'has_role',
typedbAttribute: 'role-title',
rdfPredicate: 'pico:hasRole',
transformation: 'direct',

View file

@ -1050,6 +1050,151 @@
cursor: not-allowed;
}
/* UML Density Section */
.uml-density-section {
padding: 1rem 1.5rem;
background: #f0f4ff;
border-bottom: 1px solid #4a7dff;
}
.uml-density-section h3 {
margin: 0 0 0.5rem 0;
font-size: 0.875rem;
color: #172a59;
font-weight: 600;
}
.uml-density-desc {
margin: 0 0 0.75rem 0;
font-size: 0.75rem;
color: #666;
}
.uml-density-options {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.uml-density-option {
display: flex;
flex-direction: column;
gap: 0.125rem;
width: 100%;
padding: 0.625rem 0.75rem;
background: white;
border: 1px solid #e0e0e0;
border-radius: 6px;
text-align: left;
cursor: pointer;
transition: all 0.15s ease;
}
.uml-density-option:hover {
border-color: #4a7dff;
background: #f8f9ff;
}
.uml-density-option.active {
border-color: #4a7dff;
background: #ebefff;
}
.uml-density-label {
font-size: 0.8125rem;
font-weight: 500;
color: #172a59;
}
.uml-density-hint {
font-size: 0.6875rem;
color: #888;
}
.uml-density-option.active .uml-density-label {
color: #4a7dff;
}
.uml-density-info {
margin: 0.625rem 0 0 0;
font-size: 0.75rem;
color: #4a7dff;
font-weight: 500;
}
.uml-module-picker {
margin-top: 0.75rem;
}
.uml-module-chips {
margin-top: 0.75rem;
}
.uml-module-chips-title {
margin: 0 0 0.375rem 0;
font-size: 0.75rem;
color: #172a59;
font-weight: 500;
}
.uml-module-chip-row {
display: flex;
flex-wrap: wrap;
gap: 0.375rem;
}
.uml-module-chip {
padding: 0.35rem 0.625rem;
border: 1px solid #c8d4ff;
background: white;
color: #2c5ce6;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 500;
cursor: pointer;
transition: all 0.15s ease;
}
.uml-module-chip:hover {
border-color: #4a7dff;
background: #f8f9ff;
}
.uml-module-chip.active {
border-color: #4a7dff;
background: #4a7dff;
color: white;
}
.uml-module-label {
display: block;
margin: 0 0 0.375rem 0;
font-size: 0.75rem;
color: #172a59;
font-weight: 500;
}
.uml-module-select {
width: 100%;
padding: 0.5rem;
border: 1px solid #c8d4ff;
border-radius: 4px;
background: white;
font-size: 0.8125rem;
color: #172a59;
cursor: pointer;
}
.uml-module-select:hover {
border-color: #4a7dff;
}
.uml-module-select:focus {
outline: none;
border-color: #4a7dff;
box-shadow: 0 0 0 2px rgba(74, 125, 255, 0.2);
}
/* Node Info Section */
.node-info-section {
padding: 1rem 1.5rem;
@ -2346,6 +2491,85 @@ body:has(.visualize-page.is-mobile .sidebar--mobile:not(.collapsed)) {
background: #6b9eff;
}
[data-theme="dark"] .uml-density-section {
background: #2d2d4a;
border-bottom-color: #4a7dff;
}
[data-theme="dark"] .uml-density-section h3 {
color: #e0e0e0;
}
[data-theme="dark"] .uml-density-desc {
color: #a0a0b0;
}
[data-theme="dark"] .uml-density-option {
background: #1e1e32;
border-color: #3d3d5c;
}
[data-theme="dark"] .uml-density-option:hover {
border-color: #4a7dff;
background: #2d2d4a;
}
[data-theme="dark"] .uml-density-option.active {
border-color: #4a7dff;
background: #2d2d4a;
}
[data-theme="dark"] .uml-density-label {
color: #e0e0e0;
}
[data-theme="dark"] .uml-density-hint {
color: #888;
}
[data-theme="dark"] .uml-density-option.active .uml-density-label {
color: #6b9eff;
}
[data-theme="dark"] .uml-density-info {
color: #6b9eff;
}
[data-theme="dark"] .uml-module-label {
color: #e0e0e0;
}
[data-theme="dark"] .uml-module-chips-title {
color: #e0e0e0;
}
[data-theme="dark"] .uml-module-chip {
background: #1e1e32;
border-color: #3d3d5c;
color: #a7c1ff;
}
[data-theme="dark"] .uml-module-chip:hover {
border-color: #4a7dff;
background: #2d2d4a;
}
[data-theme="dark"] .uml-module-chip.active {
border-color: #4a7dff;
background: #4a7dff;
color: white;
}
[data-theme="dark"] .uml-module-select {
background: #1e1e32;
border-color: #3d3d5c;
color: #e0e0e0;
}
[data-theme="dark"] .uml-module-select:hover {
border-color: #4a7dff;
}
/* Node Info Section */
[data-theme="dark"] .node-info-section {
background: #2d2d4a;

View file

@ -3,7 +3,7 @@
* Supports both RDF (Turtle, N-Triples) and UML (Mermaid, PlantUML, GraphViz) formats
*/
import React, { useState, useCallback, useRef, useEffect } from 'react';
import React, { useState, useCallback, useRef, useEffect, useMemo } from 'react';
import { useDatabase } from '@/hooks/useDatabase';
import { useRdfParser } from '@/hooks/useRdfParser';
import { useGraphData } from '@/hooks/useGraphData';
@ -60,6 +60,100 @@ function isAdvancedRdfLayout(layout: string): layout is RdfAdvancedLayoutType {
return ['chord', 'radial-tree', 'sankey', 'edge-bundling', 'pack', 'tree', 'sunburst'].includes(layout);
}
type UmlDensityMode = 'full' | 'streamlined' | 'module';
type UmlModuleOption = { id: string; label: string; count: number };
const POPULAR_UML_MODULE_IDS = ['custodian', 'legal', 'collections'];
function humanizeModuleName(moduleId: string): string {
return moduleId
.replace(/_/g, ' ')
.replace(/\b\w/g, (c) => c.toUpperCase());
}
function buildStreamlinedUmlDiagram(diagram: UMLDiagram): UMLDiagram {
if (!diagram || !diagram.nodes || !diagram.links) {
return diagram;
}
const nodeCount = diagram.nodes.length;
if (nodeCount <= 300) {
return diagram;
}
const degree = new Map<string, number>();
diagram.nodes.forEach((node) => degree.set(node.id, 0));
diagram.links.forEach((link) => {
if (!degree.has(link.source) || !degree.has(link.target)) return;
degree.set(link.source, (degree.get(link.source) || 0) + 1);
degree.set(link.target, (degree.get(link.target) || 0) + 1);
});
const threshold = nodeCount > 1200 ? 3 : nodeCount > 600 ? 2 : 1;
const keepIds = new Set<string>();
degree.forEach((d, id) => {
if (d >= threshold) keepIds.add(id);
});
const coreAnchors = ['Custodian', 'CustodianType', 'Organization', 'Person'];
coreAnchors.forEach((id) => {
if (degree.has(id)) keepIds.add(id);
});
const nodes = diagram.nodes.filter((node) => keepIds.has(node.id));
const links = diagram.links.filter((link) => keepIds.has(link.source) && keepIds.has(link.target));
if (nodes.length < 50) {
return diagram;
}
return {
...diagram,
nodes,
links,
};
}
function buildModuleFocusedUmlDiagram(diagram: UMLDiagram, moduleId: string): UMLDiagram {
if (!diagram || !diagram.nodes || !diagram.links) {
return diagram;
}
if (!moduleId || moduleId === '__all__') {
return diagram;
}
const primaryNodes = diagram.nodes.filter((node) => (node.module || 'other') === moduleId);
if (primaryNodes.length === 0) {
return diagram;
}
const keepIds = new Set<string>(primaryNodes.map((n) => n.id));
// Include one-hop neighbors for context around the selected module.
diagram.links.forEach((link) => {
if (keepIds.has(link.source)) keepIds.add(link.target);
if (keepIds.has(link.target)) keepIds.add(link.source);
});
let nodes = diagram.nodes.filter((node) => keepIds.has(node.id));
let links = diagram.links.filter((link) => keepIds.has(link.source) && keepIds.has(link.target));
// If the result is still too small, keep only direct module internals.
if (nodes.length < 20) {
const primaryIds = new Set(primaryNodes.map((n) => n.id));
nodes = primaryNodes;
links = diagram.links.filter((link) => primaryIds.has(link.source) && primaryIds.has(link.target));
}
return {
...diagram,
nodes,
links,
};
}
// Bilingual text object for translations
const TEXT = {
// Sidebar
@ -90,6 +184,18 @@ const TEXT = {
refreshUml: { nl: 'UML Vernieuwen', en: 'Refresh UML' },
refreshRdf: { nl: 'RDF Vernieuwen', en: 'Refresh RDF' },
refreshHint: { nl: 'Haal de nieuwste versie op', en: 'Fetch the latest version' },
umlDensity: { nl: 'UML Weergavemodus', en: 'UML Display Mode' },
umlDensityDesc: { nl: 'Kies tussen volledig en gestroomlijnd overzicht', en: 'Choose between full and streamlined overview' },
umlModeFull: { nl: 'Volledig', en: 'Full' },
umlModeFullHint: { nl: 'Toon alle klassen en relaties', en: 'Show all classes and relationships' },
umlModeStreamlined: { nl: 'Gestroomlijnd', en: 'Streamlined' },
umlModeStreamlinedHint: { nl: 'Toon de belangrijkste, meest verbonden klassen', en: 'Show key, high-connectivity classes' },
umlModeModule: { nl: 'Module Focus', en: 'Module Focus' },
umlModeModuleHint: { nl: 'Toon een domein met context', en: 'Show one domain with context' },
umlModuleSelect: { nl: 'Module', en: 'Module' },
umlModuleAll: { nl: 'Alle modules', en: 'All modules' },
umlPopularModules: { nl: 'Populaire modules', en: 'Popular modules' },
umlShowingClasses: { nl: 'klassen zichtbaar', en: 'classes visible' },
// View switcher
viewUml: { nl: 'UML Weergave', en: 'UML View' },
@ -364,6 +470,14 @@ export function Visualize() {
}
return null;
});
const [umlDensityMode, setUmlDensityMode] = useState<UmlDensityMode>(() => {
const saved = localStorage.getItem('visualize-uml-density-mode');
return (saved === 'streamlined' || saved === 'module') ? saved : 'full';
});
const [selectedUmlModule, setSelectedUmlModule] = useState<string>(() => {
return localStorage.getItem('visualize-uml-module') || '__all__';
});
// Dropdown state
const [exportDropdownOpen, setExportDropdownOpen] = useState<boolean>(false);
@ -1063,6 +1177,51 @@ export function Visualize() {
const hasUmlContent = umlDiagram !== null;
const hasContent = hasRdfContent || hasUmlContent;
const umlModuleOptions = useMemo<UmlModuleOption[]>(() => {
if (!umlDiagram) return [];
const counts = new Map<string, number>();
umlDiagram.nodes.forEach((node) => {
const moduleId = node.module || 'other';
counts.set(moduleId, (counts.get(moduleId) || 0) + 1);
});
const options: UmlModuleOption[] = [{ id: '__all__', label: t('umlModuleAll'), count: umlDiagram.nodes.length }];
const sorted = Array.from(counts.entries()).sort((a, b) => {
if (b[1] !== a[1]) return b[1] - a[1];
return a[0].localeCompare(b[0]);
});
sorted.forEach(([id, count]) => {
options.push({ id, label: humanizeModuleName(id), count });
});
return options;
}, [umlDiagram, t]);
const popularUmlModules = useMemo(() => {
if (!umlModuleOptions.length) return [];
return umlModuleOptions.filter((option) => POPULAR_UML_MODULE_IDS.includes(option.id));
}, [umlModuleOptions]);
useEffect(() => {
if (!umlModuleOptions.length) return;
if (umlModuleOptions.some((option) => option.id === selectedUmlModule)) return;
setSelectedUmlModule('__all__');
localStorage.setItem('visualize-uml-module', '__all__');
}, [umlModuleOptions, selectedUmlModule]);
const displayUmlDiagram = useMemo(() => {
if (!umlDiagram) return null;
if (umlDensityMode === 'streamlined') {
return buildStreamlinedUmlDiagram(umlDiagram);
}
if (umlDensityMode === 'module') {
return buildModuleFocusedUmlDiagram(umlDiagram, selectedUmlModule);
}
return umlDiagram;
}, [umlDiagram, umlDensityMode, selectedUmlModule]);
return (
<div className={`visualize-page ${isFullscreen ? 'fullscreen-active' : ''} ${isMobile ? 'is-mobile' : ''}`}>
{/* Mobile overlay when sidebar is open */}
@ -1350,6 +1509,99 @@ export function Visualize() {
</div>
)}
{hasUmlContent && currentCategory === 'uml' && (
<div className="uml-density-section">
<h3>{t('umlDensity')}</h3>
<p className="uml-density-desc">{t('umlDensityDesc')}</p>
<div className="uml-density-options">
<button
className={`uml-density-option ${umlDensityMode === 'full' ? 'active' : ''}`}
onClick={() => {
setUmlDensityMode('full');
localStorage.setItem('visualize-uml-density-mode', 'full');
}}
>
<span className="uml-density-label">{t('umlModeFull')}</span>
<span className="uml-density-hint">{t('umlModeFullHint')}</span>
</button>
<button
className={`uml-density-option ${umlDensityMode === 'streamlined' ? 'active' : ''}`}
onClick={() => {
setUmlDensityMode('streamlined');
localStorage.setItem('visualize-uml-density-mode', 'streamlined');
}}
>
<span className="uml-density-label">{t('umlModeStreamlined')}</span>
<span className="uml-density-hint">{t('umlModeStreamlinedHint')}</span>
</button>
<button
className={`uml-density-option ${umlDensityMode === 'module' ? 'active' : ''}`}
onClick={() => {
setUmlDensityMode('module');
localStorage.setItem('visualize-uml-density-mode', 'module');
}}
>
<span className="uml-density-label">{t('umlModeModule')}</span>
<span className="uml-density-hint">{t('umlModeModuleHint')}</span>
</button>
</div>
{umlDensityMode === 'module' && umlModuleOptions.length > 0 && (
<>
<div className="uml-module-chips">
<p className="uml-module-chips-title">{t('umlPopularModules')}</p>
<div className="uml-module-chip-row">
<button
className={`uml-module-chip ${selectedUmlModule === '__all__' ? 'active' : ''}`}
onClick={() => {
setSelectedUmlModule('__all__');
localStorage.setItem('visualize-uml-module', '__all__');
}}
>
{t('umlModuleAll')}
</button>
{popularUmlModules.map((option) => (
<button
key={option.id}
className={`uml-module-chip ${selectedUmlModule === option.id ? 'active' : ''}`}
onClick={() => {
setSelectedUmlModule(option.id);
localStorage.setItem('visualize-uml-module', option.id);
}}
>
{option.label}
</button>
))}
</div>
</div>
<div className="uml-module-picker">
<label htmlFor="uml-module-select" className="uml-module-label">{t('umlModuleSelect')}</label>
<select
id="uml-module-select"
className="uml-module-select"
value={selectedUmlModule}
onChange={(e) => {
const next = e.target.value;
setSelectedUmlModule(next);
localStorage.setItem('visualize-uml-module', next);
}}
>
{umlModuleOptions.map((option) => (
<option key={option.id} value={option.id}>
{option.label} ({option.count.toLocaleString()})
</option>
))}
</select>
</div>
</>
)}
{displayUmlDiagram && umlDiagram && (
<p className="uml-density-info">
{displayUmlDiagram.nodes.length.toLocaleString()} / {umlDiagram.nodes.length.toLocaleString()} {t('umlShowingClasses')}
</p>
)}
</div>
)}
{/* Graph Controls (RDF only) */}
{hasRdfContent && currentCategory === 'rdf' && (
<GraphControls
@ -1814,7 +2066,7 @@ export function Visualize() {
{!isLoading && !umlError && hasUmlContent && currentCategory === 'uml' && (
<div className="uml-canvas">
<UMLVisualization
diagram={umlDiagram!}
diagram={displayUmlDiagram || umlDiagram!}
width={1400}
height={900}
layoutType={layoutType}

View file

@ -7,6 +7,7 @@ prefixes:
schema: http://schema.org/
crm: http://www.cidoc-crm.org/cidoc-crm/
gn: http://www.geonames.org/ontology#
rdac: http://rdaregistry.info/Elements/c/
wdt: http://www.wikidata.org/prop/direct/
dcterms: http://purl.org/dc/terms/
prov: http://www.w3.org/ns/prov#
@ -58,6 +59,7 @@ classes:
close_mappings:
- crm:E53_Place
- gn:Feature
- rdac:C10009 # RDA Registry: Place (RDA Classes)
slots:
- has_label
- has_name

View file

@ -1,12 +1,12 @@
{
"generated": "2026-02-18T10:16:43.406Z",
"generated": "2026-02-18T20:43:54.475Z",
"schemaRoot": "/schemas/20251121/linkml",
"totalFiles": 2183,
"totalFiles": 2187,
"categoryCounts": {
"main": 4,
"class": 1377,
"enum": 158,
"slot": 640,
"slot": 644,
"module": 4
},
"categories": [
@ -10387,6 +10387,21 @@
"path": "modules/slots/owned_by.yaml",
"category": "slot"
},
{
"name": "paid_amount",
"path": "modules/slots/paid_amount.yaml",
"category": "slot"
},
{
"name": "paid_from",
"path": "modules/slots/paid_from.yaml",
"category": "slot"
},
{
"name": "paid_to",
"path": "modules/slots/paid_to.yaml",
"category": "slot"
},
{
"name": "part_of",
"path": "modules/slots/part_of.yaml",
@ -10847,6 +10862,11 @@
"path": "modules/slots/transferred.yaml",
"category": "slot"
},
{
"name": "transferred_from",
"path": "modules/slots/transferred_from.yaml",
"category": "slot"
},
{
"name": "transferred_to",
"path": "modules/slots/transferred_to.yaml",

View file

@ -12,6 +12,7 @@ prefixes:
gbif: http://rs.gbif.org/terms/
aat: http://vocab.getty.edu/aat/
imports:
- ./ExhibitedObject
- linkml:types
- ../enums/PreservationMethodEnum
- ../metadata
@ -47,6 +48,7 @@ imports:
default_prefix: hc
classes:
BiologicalObject:
is_a: ExhibitedObject
class_uri: crm:E20_Biological_Object
description: >-
Natural specimen or organism-derived item held in a heritage collection, with associated taxonomic identification and preservation metadata.
@ -397,29 +399,64 @@ classes:
in_language: zh
- literal_form: value: https://nde.nl/ontology/hc/taxon/raphus-cucullatus
broad_mappings:
- crm:E20_Biological_Object
close_mappings:
- dwc:Occurrence
- gbif:Specimen
related_mappings:
- crm:E19_Physical_Object
- crm:E22_Human-Made_Object
- schema:Taxon
predicate: EXACT_SYNONYM
slots:
- identified_by
- has_label
- has_name
- has_rank
- has_authority
- commented_on
- identified_through
- has_specimen
- symbolize
- has_status
- has_gender
- has_stage
- contain
- has_quantity
- has_method
- has_detail
- prepared_on
- prepared_by
- acquired_through
- in_place
- describe
- acquired_by
- has_habitat
- has_hypernym
- listed_in
- has_provenance
- has_type
in_language: zh
# range: string # uriorcurie
slot_usage:
identified_by:
multivalued: true
inlined: false # Fixed invalid inline for primitive type
inlined_as_list: false # Fixed invalid inline for primitive type
required: false
any_of:
- range: FieldNumber
- range: BOLDIdentifier
- range: WikiDataIdentifier
- range: string # uriorcurie
- range: FieldNumber
- range: BOLDIdentifier
- range: WikiDataIdentifier
- range: string # uriorcurie
examples:
- value:
has_type: FieldNumber
- value:
id: https://nde.nl/ontology/hc/bold-id/NLNAT001-21
has_type: BOLDIdentifier
- value:
has_type: WikiDataIdentifier
- value:
has_type: FieldNumber
- value:
id: https://nde.nl/ontology/hc/bold-id/NLNAT001-21
has_type: BOLDIdentifier
- value:
has_type: WikiDataIdentifier
has_label:
range: TaxonName
inlined: true

View file

@ -36,195 +36,81 @@ classes:
zh: >-
描述平台界面、操作程序或用户实施指南的技术参考资料。
structured_aliases:
- literal_form: documentatie
predicate: EXACT_SYNONYM
in_language: nl
- literal_form: technische handleiding
predicate: EXACT_SYNONYM
in_language: nl
- literal_form: Dokumentation
predicate: EXACT_SYNONYM
in_language: de
- literal_form: technische Anleitung
predicate: EXACT_SYNONYM
in_language: de
- literal_form: documentation
predicate: EXACT_SYNONYM
in_language: fr
- literal_form: guide technique
predicate: EXACT_SYNONYM
in_language: fr
- literal_form: documentación
predicate: EXACT_SYNONYM
in_language: es
- literal_form: guía técnica
predicate: EXACT_SYNONYM
in_language: es
- literal_form: التوثيق
predicate: EXACT_SYNONYM
in_language: ar
- literal_form: الدليل الفني
predicate: EXACT_SYNONYM
in_language: ar
- literal_form: dokumentasi
predicate: EXACT_SYNONYM
in_language: id
- literal_form: panduan teknis
predicate: EXACT_SYNONYM
in_language: id
- literal_form: 文档
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: 技术指南
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: documentation
broad_mappings:
- schema:TechArticle
- foaf:Document
close_mappings:
- crm:E73_Information_Object
predicate: EXACT_SYNONYM
slots:
- identified_by
- has_label
- has_description
- temporal_extent
in_language: zh
- literal_form: technical guide
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: API reference
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: user manual
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: operational guide
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: schema:CreativeWork
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: foaf:Document
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: schema:TechArticle
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: crm:E73_Information_Object
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: dcterms:references
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_description
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: identified_by
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: temporal_extent
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: https://data.rijksmuseum.nl/object-metadata/api/
predicate: EXACT_SYNONYM
in_language: zh
# range: string
slot_usage:
identified_by:
range: uri
required: true
examples:
- value: API Reference Documentation
- value: Developer Integration Guide
- value: https://data.rijksmuseum.nl/object-metadata/api/
has_label:
required: true
examples:
- value: Rijksmuseum Collection API
has_description:
# range: string
examples:
- value: Complete REST API reference with endpoint specifications, authentication, and response formats.
temporal_extent:
range: TimeSpan
inlined: true
required: false
examples:
- value:
begin_of_the_begin: '2015-01-01'
- value: Complete REST API reference with endpoint specifications, authentication, and response formats.
temporal_extent:
required: false
examples:
- value:
begin_of_the_begin: '2015-01-01'
has_verbatim_value: 2015-
comments:
- Generic documentation class replacing domain-specific documentation slots
- Supports multiple documentation types (API, user, developer, system)

View file

@ -186,19 +186,19 @@ classes:
in_language: zh
- literal_form: value: |
- literal_form: "value: |"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: expense_type: ADMINISTRATIVE
- literal_form: "expense_type: ADMINISTRATIVE"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: expense_type: PROGRAM
- literal_form: "expense_type: PROGRAM"
predicate: EXACT_SYNONYM

View file

@ -185,7 +185,7 @@ classes:
in_language: zh
- literal_form: value: |
- literal_form: "value: |"
predicate: EXACT_SYNONYM
@ -197,37 +197,37 @@ classes:
in_language: zh
- literal_form: has_label: Manufacturer name (String or Label)
- literal_form: "has_label: Manufacturer name (String or Label)"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_url: Manufacturer website (URL)
- literal_form: "has_url: Manufacturer website (URL)"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: identified_by: Unique identifier
- literal_form: "identified_by: Unique identifier"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: **Primary**: `schema:Organization` - Schema.org organization
- literal_form: "**Primary**: `schema:Organization` - Schema.org organization"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: **Close**: `org:Organization` - W3C ORG organization
- literal_form: "**Close**: `org:Organization` - W3C ORG organization"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: **Related**: `prov:Agent` - PROV-O agent responsible for production
- literal_form: "**Related**: `prov:Agent` - PROV-O agent responsible for production"
predicate: EXACT_SYNONYM

View file

@ -174,13 +174,13 @@ classes:
in_language: zh
- literal_form: value: PRIMARY
- literal_form: "value: PRIMARY"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: CO_ORGANIZER
- literal_form: "value: CO_ORGANIZER"
predicate: EXACT_SYNONYM
@ -192,43 +192,43 @@ classes:
in_language: zh
- literal_form: PRIMARY: Main organizing institution
- literal_form: "PRIMARY: Main organizing institution"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: CO_ORGANIZER: Partner institution with significant organizational role
- literal_form: "CO_ORGANIZER: Partner institution with significant organizational role"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: SPONSOR_ORGANIZER: Sponsor with curatorial/organizational input
- literal_form: "SPONSOR_ORGANIZER: Sponsor with curatorial/organizational input"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: LENDING_INSTITUTION: Institution lending objects with exhibition involvement
- literal_form: "LENDING_INSTITUTION: Institution lending objects with exhibition involvement"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: HOST_VENUE: Venue hosting a traveling exhibition
- literal_form: "HOST_VENUE: Venue hosting a traveling exhibition"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: **Primary**: `schema:Role` - Schema.org role
- literal_form: "**Primary**: `schema:Role` - Schema.org role"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: **Close**: `prov:Role` - PROV-O role in activity
- literal_form: "**Close**: `prov:Role` - PROV-O role in activity"
predicate: EXACT_SYNONYM

View file

@ -380,163 +380,163 @@ classes:
in_language: zh
- literal_form: value: https://nde.nl/ontology/hc/aux/kroller-muller-sculpture
- literal_form: "value: https://nde.nl/ontology/hc/aux/kroller-muller-sculpture"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: Kroller-Muller Beeldentuin
- literal_form: "value: Kroller-Muller Beeldentuin"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: Paleis Het Loo Tuinen
- literal_form: "value: Paleis Het Loo Tuinen"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: Archeologisch Park Matilo
- literal_form: "value: Archeologisch Park Matilo"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: One of Europe's largest sculpture gardens with 160 works set in 25 hectares of park landscape within De Hoge Veluwe National Park.
- literal_form: "value: One of Europe's largest sculpture gardens with 160 works set in 25 hectares of park landscape within De Hoge Veluwe National Park."
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: range: OutdoorSiteTypeEnum
- literal_form: "range: OutdoorSiteTypeEnum"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: range: FeatureType
- literal_form: "range: FeatureType"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: SCULPTURE_GARDEN
- literal_form: "value: SCULPTURE_GARDEN"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: ARCHAEOLOGICAL_SITE
- literal_form: "value: ARCHAEOLOGICAL_SITE"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: FORMAL_GARDEN
- literal_form: "value: FORMAL_GARDEN"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: BotanicalInstitutionClassification
- literal_form: "value: BotanicalInstitutionClassification"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: ZoologicalInstitutionClassification
- literal_form: "value: ZoologicalInstitutionClassification"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value:
- literal_form: "value:"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value:
- literal_form: "value:"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: 160
- literal_form: "value: 160"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: 2500
- literal_form: "value: 2500"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value:
- literal_form: "value:"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: area_value: 25.0
- literal_form: "area_value: 25.0"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label: Included with museum ticket
- literal_form: "has_label: Included with museum ticket"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label: Paved paths
- literal_form: "has_label: Paved paths"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label: Wheelchair routes available
- literal_form: "has_label: Wheelchair routes available"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value:
- literal_form: "value:"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: area_value: 650.0
- literal_form: "area_value: 650.0"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label: Included with palace ticket
- literal_form: "has_label: Included with palace ticket"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value:
- literal_form: "value:"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: area_value: 3.5
- literal_form: "area_value: 3.5"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: has_label: Free
- literal_form: "has_label: Free"
predicate: EXACT_SYNONYM

View file

@ -200,13 +200,13 @@ classes:
in_language: zh
- literal_form: value: hc:ArchiveOrganizationType
- literal_form: "value: hc:ArchiveOrganizationType"
predicate: EXACT_SYNONYM
in_language: zh
- literal_form: value: hc:HolySacredSiteType
- literal_form: "value: hc:HolySacredSiteType"
predicate: EXACT_SYNONYM

View file

@ -538,6 +538,9 @@ imports:
- ./outbound_to
- ./overlap_with
- ./owned_by
- ./paid_amount
- ./paid_from
- ./paid_to
- ./part_of
- ./participate_in
- ./performed_by
@ -628,6 +631,7 @@ imports:
- ./threatened_by
- ./track
- ./transferred
- ./transferred_from
- ./transferred_to
- ./transmission
- ./transmit_through

View file

@ -31,6 +31,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
crm: http://www.cidoc-crm.org/cidoc-crm/
schema: http://schema.org/
la: https://linked.art/ns/terms/
imports:
- linkml:types
default_prefix: hc
@ -96,6 +97,7 @@ slots:
- schema:object # schemaorg.owl:27871-27890 - "The object upon which the action is carried out." Broader: object of any action, not specifically accessioning.
related_mappings:
- crm:P24_transferred_title_of # CIDOC_CRM_v7.1.3.rdf:1738-1750 - "Identifies the E18 Physical Thing involved in an E8 Acquisition." Legal transfer aspect of accessioning.
- la:added_member # Linked Art extensions: member added to a Set via an Addition activity
aliases:
- objects_added
examples:

View file

@ -8,6 +8,7 @@ prefixes:
crm: http://www.cidoc-crm.org/cidoc-crm/
rico: https://www.ica.org/standards/RiC/ontology#
foaf: http://xmlns.com/foaf/0.1/
rdau: http://rdaregistry.info/Elements/u/
dcterms: http://purl.org/dc/terms/
prov: http://www.w3.org/ns/prov#
skos: http://www.w3.org/2004/02/skos/core#
@ -44,6 +45,7 @@ slots:
close_mappings:
- crm:P7_took_place_at
- rico:birthPlace
- rdau:P60593 # RDA Registry (Unconstrained): has place of birth
comments:
- MIGRATED from birth_place slot (Rule 53)
- Supports historical vs. modern place names

View file

@ -24,6 +24,7 @@ prefixes:
skos: http://www.w3.org/2004/02/skos/core#
owl: http://www.w3.org/2002/07/owl#
schema: http://schema.org/
la: https://linked.art/ns/terms/
imports:
- linkml:types
default_prefix: hc
@ -87,6 +88,7 @@ slots:
close_mappings:
- owl:sameAs # W3C OWL 2 standard - Identity assertion (stronger claim than semantic equivalence).
- schema:sameAs # schemaorg.owl:34129-34148 - "URL unambiguously indicating the item's identity."
- la:equivalent # Linked Art extensions: equivalent instance (skos:exactMatch-like without Concept inference)
aliases:
- is_or_was_equivalent_to
- wikidata_equivalent

View file

@ -25,6 +25,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
crm: http://www.cidoc-crm.org/cidoc-crm/
rico: https://www.ica.org/standards/RiC/ontology#
la: https://linked.art/ns/terms/
xsd: http://www.w3.org/2001/XMLSchema#
default_prefix: hc
@ -82,6 +83,8 @@ slots:
- crm:P49_has_former_or_current_keeper # CIDOC_CRM_v7.1.3.rdf:2383-2408 - includes former keepers (broader temporal scope)
- crm:P50_has_current_keeper # CIDOC_CRM_v7.1.3.rdf:2410-2424 - "current keeper" but domain E18 Physical Thing / range E39 Actor (typed objects)
- rico:hasOrHadHolder # RiC-O_1-1.rdf:6436-6475 - "has or had holder" — archival holding context, domain RecordResource/Instantiation
related_mappings:
- la:current_permanent_custodian # Linked Art extensions: normal/permanent custodian of a physical object (E19->E39)
comments:
- |
MIGRATED 2026-02-03 from has_or_had_custodian for conciseness.

View file

@ -23,6 +23,7 @@ prefixes:
schema: http://schema.org/
dcat: http://www.w3.org/ns/dcat#
dcterms: http://purl.org/dc/terms/
ardo: https://w3id.org/ardo/2.0/
imports:
- linkml:types
default_prefix: hc
@ -80,6 +81,8 @@ slots:
- dcat:keyword # dcat3.ttl:1208-1231 - "A keyword or tag describing a resource"
close_mappings:
- dcterms:subject # dcterms.rdf:1968-1988 - "A topic of the resource"
related_mappings:
- ardo:has_keyword # ArDO 2.0: links a thematic subcategory to a keyword (object property)
comments:
- "Used for discovery and classification."
annotations:

View file

@ -25,6 +25,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
org: http://www.w3.org/ns/org#
schema: http://schema.org/
la: https://linked.art/ns/terms/
xsd: http://www.w3.org/2001/XMLSchema#
default_prefix: hc
imports:
@ -80,5 +81,7 @@ slots:
exact_mappings:
- org:hasMember # org.rdf:427-446 - "Indicates a person who is a member of the subject Organization." Domain: Organization, Range: Agent. Organization membership only; this slot also covers collection elements.
- schema:member # schemaorg.owl:26055-26085 - "A member of an Organization or a ProgramMembership." Domain: Organization/ProgramMembership. Does not cover collection elements.
related_mappings:
- la:has_member # Linked Art extensions: membership (Set→Entity)
annotations:
custodian_types: '["*"]'

View file

@ -19,6 +19,7 @@ prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
dcterms: http://purl.org/dc/terms/
pca: http://rds.posccaesar.org/ontology/plm/rdl/
default_prefix: hc
imports:
- linkml:types
@ -82,3 +83,5 @@ slots:
custodian_types: '["*"]'
close_mappings:
- dcterms:conformsTo # dcterms.rdf:987-1010 - "An established standard to which the described resource conforms." Conformance relationship ≠ having a standard.
related_mappings:
- pca:PCA_100003538 # POSC Caesar RDL (PCA PLM core): Standard (class)

View file

@ -24,6 +24,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
schema: http://schema.org/
dcterms: http://purl.org/dc/terms/
pav: http://purl.org/pav/2.3#
default_prefix: hc
imports:
- linkml:types
@ -81,6 +82,7 @@ slots:
multivalued: false
related_mappings:
- dcterms:hasVersion # dcterms.rdf:1371-1395 — "A related resource that is a version, edition, or adaptation." Relates resources to each other, not a version identifier.
- pav:hasVersion # PAV 2.3 (used by ArDO): links a resource to a version resource
aliases:
- has_or_had_version
- api_ver

View file

@ -27,6 +27,7 @@ prefixes:
schema: http://schema.org/
crm: http://www.cidoc-crm.org/cidoc-crm/
rico: https://www.ica.org/standards/RiC/ontology#
la: https://linked.art/ns/terms/
imports:
- linkml:types
default_prefix: hc
@ -115,6 +116,7 @@ slots:
custodian_types: '["*"]'
related_mappings:
- schema:owns # schemaorg.owl:28732-28760 — "Things owned by the organization or person." Ownership ≠ custody/holding.
- la:current_permanent_custodian_of # Linked Art extensions: inverse of current_permanent_custodian (Actor->Physical Object)
close_mappings:
- crm:P49i_is_former_or_current_keeper_of # CIDOC_CRM:2410-2435 — "is former or current keeper of." Custody relationship.
- rico:isOrWasHolderOf # RiC-O_1-1.rdf:6436-6470 — "has or had holder" (inverse). Archives holding.

View file

@ -32,6 +32,7 @@ prefixes:
rico: https://www.ica.org/standards/RiC/ontology#
org: http://www.w3.org/ns/org#
foaf: http://xmlns.com/foaf/0.1/
la: https://linked.art/ns/terms/
default_prefix: hc
imports:
- linkml:types
@ -98,6 +99,7 @@ slots:
- rico:isOrWasMemberOf # RiC-O_1-1.rdf:14505-14550 - Person→Group (restricted domain)
related_mappings:
- foaf:member # foaf.ttl:410-417 - INVERSE: Group→Agent (not Agent→Group)
- la:member_of # Linked Art extensions: membership (Entity→Set)
annotations:
inverse_slot: has_or_had_member
deprecates: is_member_of

View file

@ -0,0 +1,41 @@
# ==============================================================================
# LinkML Slot Definition: paid_amount
# ==============================================================================
# Monetary amount paid in a payment/transfer context.
#
# ONTOLOGY ALIGNMENT (verified against linked.art terms):
#
# | Ontology | Property | Mapping | Notes |
# |----------------|------------------|---------|------------------------------------------------------|
# | **Linked Art** | `la:paid_amount` | exact | Payment -> Monetary Amount in Linked Art extensions. |
# | **Schema.org** | `schema:price` | close | Price/value expression, broader commerce usage. |
#
# CREATED: 2026-02-18
# ==============================================================================
id: https://nde.nl/ontology/hc/slot/paid_amount
name: paid_amount
title: Paid Amount
prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
la: https://linked.art/ns/terms/
schema: http://schema.org/
imports:
- linkml:types
default_prefix: hc
slots:
paid_amount:
slot_uri: hc:paidAmount
description: >-
Specifies the amount that was paid as part of a payment or transfer.
range: string
multivalued: false
exact_mappings:
- la:paid_amount
close_mappings:
- schema:price
aliases:
- is_or_was_paid_amount
annotations:
custodian_types: '["*"]'

View file

@ -0,0 +1,41 @@
# ==============================================================================
# LinkML Slot Definition: paid_from
# ==============================================================================
# Paying party/source in a payment context.
#
# ONTOLOGY ALIGNMENT (verified against linked.art terms):
#
# | Ontology | Property | Mapping | Notes |
# |----------------|----------------|---------|-----------------------------------------------------------|
# | **Linked Art** | `la:paid_from` | exact | Payment source actor in Linked Art extensions. |
# | **Schema.org** | `schema:buyer` | related | Buyer role; related commercial payer perspective. |
#
# CREATED: 2026-02-18
# ==============================================================================
id: https://nde.nl/ontology/hc/slot/paid_from
name: paid_from
title: Paid From
prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
la: https://linked.art/ns/terms/
schema: http://schema.org/
imports:
- linkml:types
default_prefix: hc
slots:
paid_from:
slot_uri: hc:paidFrom
description: >-
Identifies the party or source from which a payment originated.
range: string
multivalued: false
exact_mappings:
- la:paid_from
related_mappings:
- schema:buyer
aliases:
- is_or_was_paid_from
annotations:
custodian_types: '["*"]'

View file

@ -0,0 +1,41 @@
# ==============================================================================
# LinkML Slot Definition: paid_to
# ==============================================================================
# Receiving party/destination in a payment context.
#
# ONTOLOGY ALIGNMENT (verified against linked.art terms):
#
# | Ontology | Property | Mapping | Notes |
# |----------------|-----------------|---------|-----------------------------------------------------------|
# | **Linked Art** | `la:paid_to` | exact | Payment recipient actor in Linked Art extensions. |
# | **Schema.org** | `schema:seller` | related | Seller role; related commercial payee perspective. |
#
# CREATED: 2026-02-18
# ==============================================================================
id: https://nde.nl/ontology/hc/slot/paid_to
name: paid_to
title: Paid To
prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
la: https://linked.art/ns/terms/
schema: http://schema.org/
imports:
- linkml:types
default_prefix: hc
slots:
paid_to:
slot_uri: hc:paidTo
description: >-
Identifies the party or destination to which a payment was made.
range: string
multivalued: false
exact_mappings:
- la:paid_to
related_mappings:
- schema:seller
aliases:
- is_or_was_paid_to
annotations:
custodian_types: '["*"]'

View file

@ -35,6 +35,7 @@ prefixes:
time: http://www.w3.org/2006/time#
schema: http://schema.org/
dcterms: http://purl.org/dc/terms/
pav: http://purl.org/pav/2.3#
default_prefix: hc
imports:
- linkml:types
@ -110,6 +111,7 @@ slots:
- schema:predecessorOf # schemaorg.owl:30406-30420 - "Previous variant of product; ProductModel domain"
- schema:previousItem # schemaorg.owl:30559-30575 - "Preceding ListItem; ListItem domain"
- dcterms:replaces # dcterms.rdf:1827-1846 - "Supplants/supersedes described resource; implies replacement"
- pav:previousVersion # PAV 2.3 (used by ArDO): previous version link (version chain)
aliases:
- previous_observation
examples:

View file

@ -32,6 +32,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
crm: http://www.cidoc-crm.org/cidoc-crm/
schema: http://schema.org/
la: https://linked.art/ns/terms/
imports:
- linkml:types
default_prefix: hc
@ -98,6 +99,7 @@ slots:
- schema:object # schemaorg.owl:27871-27890 - "The object upon which the action is carried out." Broader: object of any action, not specifically deaccessioning.
related_mappings:
- crm:P24_transferred_title_of # CIDOC_CRM_v7.1.3.rdf:1738-1750 - "Identifies the E18 Physical Thing involved in an E8 Acquisition." Legal transfer aspect of deaccessioning/disposal.
- la:removed_member # Linked Art extensions: member removed from a Set via a Removal activity
aliases:
- objects_removed
examples:

View file

@ -22,6 +22,7 @@ prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
schema: http://schema.org/
la: https://linked.art/ns/terms/
skos: http://www.w3.org/2004/02/skos/core#
xsd: http://www.w3.org/2001/XMLSchema#
@ -83,5 +84,6 @@ slots:
- is_or_was_transferred
related_mappings:
- schema:TransferAction # schemaorg.owl:5808-5812 - "act of transferring animate or inanimate objects"
- la:transferred # Linked Art extensions: what was transferred (Transfer->Entity). Note: this slot is boolean, so mapping is conceptual only.
annotations:
custodian_types: '["*"]'

View file

@ -0,0 +1,45 @@
# ==============================================================================
# LinkML Slot Definition: transferred_from
# ==============================================================================
# Source party/place from which something was transferred.
#
# ONTOLOGY ALIGNMENT (verified against linked.art terms):
#
# | Ontology | Property | Mapping | Notes |
# |----------------|-----------------------|---------|-------------------------------------------------------------|
# | **Linked Art** | `la:transferred_from` | exact | Source entity in a transfer in Linked Art extensions. |
# | **CIDOC-CRM** | `crm:P27_moved_from` | narrow | Location-only origin (place), narrower than general source. |
# | **Schema.org** | `schema:fromLocation` | narrow | Location-only source in action logistics. |
#
# CREATED: 2026-02-18
# ==============================================================================
id: https://nde.nl/ontology/hc/slot/transferred_from
name: transferred_from
title: Transferred From
prefixes:
linkml: https://w3id.org/linkml/
hc: https://nde.nl/ontology/hc/
la: https://linked.art/ns/terms/
crm: http://www.cidoc-crm.org/cidoc-crm/
schema: http://schema.org/
imports:
- linkml:types
default_prefix: hc
slots:
transferred_from:
slot_uri: hc:transferredFrom
description: >-
Identifies the source party, owner, custodian, or location from which
something was transferred.
range: string
multivalued: false
exact_mappings:
- la:transferred_from
narrow_mappings:
- crm:P27_moved_from
- schema:fromLocation
aliases:
- is_or_was_transferred_from
annotations:
custodian_types: '["*"]'

View file

@ -24,6 +24,7 @@ prefixes:
hc: https://nde.nl/ontology/hc/
schema: http://schema.org/
odrl: http://www.w3.org/ns/odrl/2/
la: https://linked.art/ns/terms/
skos: http://www.w3.org/2004/02/skos/core#
xsd: http://www.w3.org/2001/XMLSchema#
@ -86,5 +87,6 @@ slots:
- is_or_was_transferred_to
related_mappings:
- schema:TransferAction # schemaorg.owl:5808-5812 - "act of transferring animate or inanimate objects"
- la:transferred_to # Linked Art extensions: transferred to (Transfer→Entity)
annotations:
custodian_types: '["*"]'

View file

@ -0,0 +1,282 @@
#!/usr/bin/env python3
"""Verify external ontology mappings used in LinkML YAML files.
Default behavior targets changed/untracked YAML files under:
schemas/20251121/linkml/
It validates mapping CURIEs under mapping keys:
exact_mappings, close_mappings, broad_mappings, narrow_mappings, related_mappings
Supported prefixes:
- la (Linked Art)
- rdac (RDA classes)
- rdau (RDA unconstrained properties)
- pav (PAV 2.3)
- ardo (ArDO)
- pca (POSC Caesar RDS)
"""
from __future__ import annotations
import argparse
import json
import re
import subprocess
import sys
import urllib.error
import urllib.request
import xml.etree.ElementTree as ET
from pathlib import Path
MAPPING_KEYS = {
"exact_mappings",
"close_mappings",
"broad_mappings",
"narrow_mappings",
"related_mappings",
}
SUPPORTED_PREFIXES = {"la", "rdac", "rdau", "pav", "ardo", "pca"}
CURIE_RE = re.compile(r"^(?P<prefix>[a-z][a-z0-9_-]*):(?P<local>[A-Za-z0-9_./-]+)$")
def fetch_text(url: str, timeout: int = 60) -> str:
with urllib.request.urlopen(url, timeout=timeout) as resp:
return resp.read().decode("utf-8", errors="ignore")
def fetch_bytes(url: str, timeout: int = 60) -> bytes:
with urllib.request.urlopen(url, timeout=timeout) as resp:
return resp.read()
def parse_mapping_curies(file_path: Path) -> list[tuple[int, str, str]]:
"""Return (line_number, prefix, local) mapping CURIEs from mapping blocks."""
out: list[tuple[int, str, str]] = []
lines = file_path.read_text(encoding="utf-8", errors="ignore").splitlines()
in_block = False
block_indent = -1
for idx, line in enumerate(lines, start=1):
stripped = line.strip()
indent = len(line) - len(line.lstrip(" "))
if not in_block:
if not stripped or stripped.startswith("#"):
continue
if ":" in stripped:
key = stripped.split(":", 1)[0].strip()
if key in MAPPING_KEYS and stripped.endswith(":"):
in_block = True
block_indent = indent
continue
# Exit mapping block on dedent to same or lower level and non-list content
if stripped and not stripped.startswith("#"):
if indent <= block_indent and not stripped.startswith("-"):
in_block = False
block_indent = -1
# re-process this line as potential new key
if ":" in stripped:
key = stripped.split(":", 1)[0].strip()
if key in MAPPING_KEYS and stripped.endswith(":"):
in_block = True
block_indent = indent
continue
if stripped.startswith("-"):
item = stripped[1:].strip()
# remove inline comment
if " #" in item:
item = item.split(" #", 1)[0].strip()
m = CURIE_RE.match(item)
if m:
pfx = m.group("prefix")
local = m.group("local")
out.append((idx, pfx, local))
return out
def changed_yaml_files(repo_root: Path, scope: Path) -> list[Path]:
"""Collect changed and untracked YAML files inside scope."""
files: set[Path] = set()
def run(cmd: list[str]) -> list[str]:
try:
out = subprocess.check_output(cmd, cwd=repo_root)
return [x for x in out.decode().splitlines() if x]
except subprocess.CalledProcessError:
return []
tracked = run(["git", "diff", "--name-only"])
untracked = run(["git", "ls-files", "--others", "--exclude-standard"])
for rel in tracked + untracked:
if not rel.endswith(".yaml"):
continue
p = (repo_root / rel).resolve()
try:
p.relative_to(scope.resolve())
except ValueError:
continue
if p.is_file():
files.add(p)
return sorted(files)
def load_linked_art_terms() -> tuple[set[str], set[str]]:
xml_data = fetch_bytes("https://linked.art/ns/terms/")
root = ET.fromstring(xml_data)
ns = {
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
}
props: set[str] = set()
classes: set[str] = set()
for p in root.findall("rdf:Property", ns):
uri = p.attrib.get("{http://www.w3.org/1999/02/22-rdf-syntax-ns#}about", "")
if uri.startswith("https://linked.art/ns/terms/"):
props.add(uri.rsplit("/", 1)[-1])
for c in root.findall("rdfs:Class", ns):
uri = c.attrib.get("{http://www.w3.org/1999/02/22-rdf-syntax-ns#}about", "")
if uri.startswith("https://linked.art/ns/terms/"):
classes.add(uri.rsplit("/", 1)[-1])
return props, classes
def load_rda_ids(path: str, marker: str) -> set[str]:
txt = fetch_text(f"https://www.rdaregistry.info/jsonld/Elements/{path}.jsonld")
return set(re.findall(marker, txt))
def main() -> int:
parser = argparse.ArgumentParser(description="Verify LinkML external mappings")
parser.add_argument(
"files",
nargs="*",
help="YAML files to verify (defaults to changed/untracked files under scope)",
)
parser.add_argument(
"--scope",
default="schemas/20251121/linkml",
help="Default scope used when no files are provided",
)
parser.add_argument(
"--all",
action="store_true",
help="Scan all YAML files under --scope (instead of changed/untracked files)",
)
args = parser.parse_args()
repo_root = Path(__file__).resolve().parents[1]
scope = (repo_root / args.scope).resolve()
if args.files:
files = [Path(f).resolve() for f in args.files]
elif args.all:
files = sorted(scope.rglob("*.yaml"))
else:
files = changed_yaml_files(repo_root, scope)
if not files:
print("No target YAML files found. Nothing to verify.")
return 0
occurrences: dict[str, list[tuple[Path, int, str]]] = {}
for file_path in files:
if not file_path.exists() or file_path.suffix != ".yaml":
continue
for line_no, pfx, local in parse_mapping_curies(file_path):
if pfx not in SUPPORTED_PREFIXES:
continue
occurrences.setdefault(pfx, []).append((file_path, line_no, local))
if not occurrences:
print("No supported external mapping CURIEs found in selected files.")
return 0
failures: list[str] = []
la_props: set[str] = set()
la_classes: set[str] = set()
rdac_ids: set[str] = set()
rdau_ids: set[str] = set()
pav_text = ""
try:
la_props, la_classes = load_linked_art_terms()
except Exception as e: # pragma: no cover - network failures
failures.append(f"[load] Linked Art: {e}")
try:
rdac_ids = load_rda_ids("c", r"Elements/c/(C\d+)")
except Exception as e: # pragma: no cover
failures.append(f"[load] RDA c.jsonld: {e}")
try:
rdau_ids = load_rda_ids("u", r"Elements/u/(P\d+)")
except Exception as e: # pragma: no cover
failures.append(f"[load] RDA u.jsonld: {e}")
try:
pav_text = fetch_text("https://purl.org/pav/2.3")
except Exception as e: # pragma: no cover
failures.append(f"[load] PAV 2.3: {e}")
print("Verifying mapping CURIEs:")
for prefix in sorted(occurrences):
locals_unique = sorted({x[2] for x in occurrences[prefix]})
print(f"- {prefix}: {', '.join(locals_unique)}")
# prefix-specific verification
for file_path, line_no, local in occurrences.get("la", []):
if local not in la_props and local not in la_classes:
failures.append(f"{file_path}:{line_no} la:{local} not found in linked.art/ns/terms")
for file_path, line_no, local in occurrences.get("rdac", []):
if local not in rdac_ids:
failures.append(f"{file_path}:{line_no} rdac:{local} not found in RDA Elements/c.jsonld")
for file_path, line_no, local in occurrences.get("rdau", []):
if local not in rdau_ids:
failures.append(f"{file_path}:{line_no} rdau:{local} not found in RDA Elements/u.jsonld")
for file_path, line_no, local in occurrences.get("pav", []):
if local not in pav_text:
failures.append(f"{file_path}:{line_no} pav:{local} not found in PAV 2.3 ontology")
for file_path, line_no, local in occurrences.get("ardo", []):
url = f"https://w3id.org/ardo/2.0/{local}"
try:
txt = fetch_text(url)
if local not in txt:
failures.append(f"{file_path}:{line_no} ardo:{local} not found at {url}")
except urllib.error.URLError as e:
failures.append(f"{file_path}:{line_no} ardo:{local} fetch error: {e}")
for file_path, line_no, local in occurrences.get("pca", []):
url = f"https://rds.posccaesar.org/ontology/plm/rdl/{local}"
try:
txt = fetch_text(url)
if local not in txt:
failures.append(f"{file_path}:{line_no} pca:{local} not found at {url}")
except urllib.error.URLError as e:
failures.append(f"{file_path}:{line_no} pca:{local} fetch error: {e}")
if failures:
print("\nFAIL")
for f in failures:
print(f"- {f}")
return 1
print("\nOK: all checked mapping CURIEs were verified against source ontologies.")
return 0
if __name__ == "__main__":
sys.exit(main())