From 637286ce2e767f9dc47dcb3e087d7ac254393bc1 Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Fri, 11 Sep 2026 17:29:31 -0400 Subject: [PATCH] Add hasFormat relation for alternate-format sibling Building Blocks hasFormat represents dcterms:hasFormat/isFormatOf semantics: two Building Blocks that are substantially the same content in another format (e.g. a JSON vs an XML serialization of the same model), as opposed to isProfileOf's specialization/inheritance semantics. - bblock.schema.yaml: add hasFormat (array of bblock references), symmetric, intended to be declared on every sibling that should be linked - models.py: normalize bblocks:// prefixes on hasFormat like dependsOn/isProfileOf - register-context.yaml: map hasFormat to dct:hasFormat, and add the previously-missing seeAlso -> rdfs:seeAlso mapping (the property name itself had no JSON-LD context term, unlike its raw rdfs:seeAlso IRI counterpart) --- ogc/bblocks/models.py | 5 +++++ ogc/bblocks/register-context.yaml | 6 ++++++ ogc/bblocks/schemas/bblock.schema.yaml | 10 ++++++++++ 3 files changed, 21 insertions(+) diff --git a/ogc/bblocks/models.py b/ogc/bblocks/models.py index 7862af1..947acd6 100644 --- a/ogc/bblocks/models.py +++ b/ogc/bblocks/models.py @@ -233,6 +233,9 @@ def __init__(self, identifier: str, metadata_file: Path, if 'dependsOn' in self.metadata: self.metadata['dependsOn'] = [strip_bblocks_uri(d) for d in self.metadata['dependsOn']] + if 'hasFormat' in self.metadata: + self.metadata['hasFormat'] = [strip_bblocks_uri(d) for d in self.metadata['hasFormat']] + ep = self.metadata.get('extensionPoints') if ep: if 'baseBuildingBlock' in ep: @@ -692,6 +695,8 @@ def load(self, metadata_url: str) -> tuple[list[str], str]: bblock['register'] = self if 'dependsOn' in bblock: bblock['dependsOn'] = [strip_bblocks_uri(d) for d in bblock['dependsOn']] + if 'hasFormat' in bblock: + bblock['hasFormat'] = [strip_bblocks_uri(d) for d in bblock['hasFormat']] if 'isProfileOf' in bblock: v = bblock['isProfileOf'] bblock['isProfileOf'] = ([strip_bblocks_uri(i) for i in v] diff --git a/ogc/bblocks/register-context.yaml b/ogc/bblocks/register-context.yaml index 9ed33e0..18698c5 100644 --- a/ogc/bblocks/register-context.yaml +++ b/ogc/bblocks/register-context.yaml @@ -123,6 +123,12 @@ context: isProfileOf: '@id': prof:isProfileOf '@type': '@id' + hasFormat: + '@id': dct:hasFormat + '@type': '@id' + seeAlso: + '@id': rdfs:seeAlso + '@type': '@id' tags: dcat:keyword shaclShapes: '@id': prof:hasResource diff --git a/ogc/bblocks/schemas/bblock.schema.yaml b/ogc/bblocks/schemas/bblock.schema.yaml index 9a01974..c763b62 100644 --- a/ogc/bblocks/schemas/bblock.schema.yaml +++ b/ogc/bblocks/schemas/bblock.schema.yaml @@ -195,6 +195,16 @@ properties: type: array items: type: string + hasFormat: + description: >- + Identifiers of other Building Blocks that represent the same content as this one, but in + an alternate format (equivalent to Dublin Core's dcterms:hasFormat/isFormatOf). Unlike + isProfileOf, this does not imply one is a specialization of the other - they are siblings. + Symmetric: list the relation on every sibling that should be linked, since it is not + inferred automatically from the other side. + type: array + items: + type: string requirementClasses: description: URI(s) of requirement classes that can be used to validate this Building Block type: array