diff --git a/src/fair_mappings_schema/datamodel/fair_mappings_schema.py b/src/fair_mappings_schema/datamodel/fair_mappings_schema.py index 6342772..46e1381 100644 --- a/src/fair_mappings_schema/datamodel/fair_mappings_schema.py +++ b/src/fair_mappings_schema/datamodel/fair_mappings_schema.py @@ -1,9 +1,9 @@ # Auto generated from fair_mappings_schema.yaml by pythongen.py version: 0.0.1 -# Generation date: 2025-10-04T08:59:50 +# Generation date: 2025-10-06T23:29:58 # Schema: fair-mappings-schema # # id: https://w3id.org/mapping-commons/fair-mappings-schema -# description: A basic metadata schema for FAIR mapping specifications +# description: A minimal metadata schema for FAIR mapping specifications # license: Apache-2.0 import dataclasses @@ -56,8 +56,7 @@ URIRef ) -from linkml_runtime.linkml_model.types import Date, Integer, String, Uriorcurie -from linkml_runtime.utils.metamodelcore import URIorCURIE, XSDDate +from linkml_runtime.linkml_model.types import String metamodel_version = "1.7.0" version = None @@ -75,49 +74,59 @@ # Types # Class references -class NamedThingId(URIorCURIE): - pass - -class PersonId(NamedThingId): - pass @dataclass(repr=False) -class NamedThing(YAMLRoot): +class Agent(YAMLRoot): """ - A generic grouping for any identifiable entity + An entity that can create or contribute to a digital object, such as an author or creator. """ _inherited_slots: ClassVar[list[str]] = [] - class_class_uri: ClassVar[URIRef] = SCHEMA["Thing"] - class_class_curie: ClassVar[str] = "schema:Thing" - class_name: ClassVar[str] = "NamedThing" - class_model_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA.NamedThing + class_class_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA["Agent"] + class_class_curie: ClassVar[str] = "fair_mappings_schema:Agent" + class_name: ClassVar[str] = "Agent" + class_model_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA.Agent - id: Union[str, NamedThingId] = None + id: Optional[str] = None name: Optional[str] = None - description: Optional[str] = None + type: Optional[str] = None def __post_init__(self, *_: str, **kwargs: Any): - if self._is_empty(self.id): - self.MissingRequiredField("id") - if not isinstance(self.id, NamedThingId): - self.id = NamedThingId(self.id) + if self.id is not None and not isinstance(self.id, str): + self.id = str(self.id) if self.name is not None and not isinstance(self.name, str): self.name = str(self.name) - if self.description is not None and not isinstance(self.description, str): - self.description = str(self.description) + self.type = str(self.class_name) super().__post_init__(**kwargs) + self.type = str(self.class_name) + + + def __new__(cls, *args, **kwargs): + + type_designator = "type" + if not type_designator in kwargs: + return super().__new__(cls,*args,**kwargs) + else: + type_designator_value = kwargs[type_designator] + target_cls = cls._class_for("class_name", type_designator_value) + + + if target_cls is None: + raise ValueError(f"Wrong type designator value: class {cls.__name__} " + f"has no subclass with ['class_name']='{kwargs[type_designator]}'") + return super().__new__(target_cls,*args,**kwargs) + @dataclass(repr=False) -class Person(NamedThing): +class Person(Agent): """ - Represents a Person + An individual person who contributes to a mapping specification """ _inherited_slots: ClassVar[list[str]] = [] @@ -126,100 +135,347 @@ class Person(NamedThing): class_name: ClassVar[str] = "Person" class_model_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA.Person - id: Union[str, PersonId] = None - primary_email: Optional[str] = None - birth_date: Optional[Union[str, XSDDate]] = None - age_in_years: Optional[int] = None - vital_status: Optional[Union[str, "PersonStatus"]] = None + orcid: Optional[str] = None + affiliation: Optional[str] = None + + def __post_init__(self, *_: str, **kwargs: Any): + if self.orcid is not None and not isinstance(self.orcid, str): + self.orcid = str(self.orcid) + + if self.affiliation is not None and not isinstance(self.affiliation, str): + self.affiliation = str(self.affiliation) + + super().__post_init__(**kwargs) + self.type = str(self.class_name) + + +@dataclass(repr=False) +class Organization(Agent): + """ + An organization or institution that contributes to a mapping specification + """ + _inherited_slots: ClassVar[list[str]] = [] + + class_class_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA["Organization"] + class_class_curie: ClassVar[str] = "fair_mappings_schema:Organization" + class_name: ClassVar[str] = "Organization" + class_model_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA.Organization + + ror_id: Optional[str] = None + url: Optional[str] = None + + def __post_init__(self, *_: str, **kwargs: Any): + if self.ror_id is not None and not isinstance(self.ror_id, str): + self.ror_id = str(self.ror_id) + + if self.url is not None and not isinstance(self.url, str): + self.url = str(self.url) + + super().__post_init__(**kwargs) + self.type = str(self.class_name) + + +@dataclass(repr=False) +class Software(Agent): + """ + A software tool or system used in creating mappings + """ + _inherited_slots: ClassVar[list[str]] = [] + + class_class_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA["Software"] + class_class_curie: ClassVar[str] = "fair_mappings_schema:Software" + class_name: ClassVar[str] = "Software" + class_model_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA.Software + + version: Optional[str] = None + repository_url: Optional[str] = None + + def __post_init__(self, *_: str, **kwargs: Any): + if self.version is not None and not isinstance(self.version, str): + self.version = str(self.version) + + if self.repository_url is not None and not isinstance(self.repository_url, str): + self.repository_url = str(self.repository_url) + + super().__post_init__(**kwargs) + self.type = str(self.class_name) + + +@dataclass(repr=False) +class Source(YAMLRoot): + """ + A data source from which entities are drawn, such as a database, ontology, or vocabulary. + """ + _inherited_slots: ClassVar[list[str]] = [] + + class_class_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA["Source"] + class_class_curie: ClassVar[str] = "fair_mappings_schema:Source" + class_name: ClassVar[str] = "Source" + class_model_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA.Source + + id: Optional[str] = None + name: Optional[str] = None + version: Optional[str] = None + type: Optional[Union[str, "SourceTypeEnum"]] = None + documentation: Optional[str] = None + content_url: Optional[str] = None + content_type: Optional[str] = None + metadata_url: Optional[str] = None + metadata_type: Optional[str] = None def __post_init__(self, *_: str, **kwargs: Any): - if self._is_empty(self.id): - self.MissingRequiredField("id") - if not isinstance(self.id, PersonId): - self.id = PersonId(self.id) + if self.id is not None and not isinstance(self.id, str): + self.id = str(self.id) - if self.primary_email is not None and not isinstance(self.primary_email, str): - self.primary_email = str(self.primary_email) + if self.name is not None and not isinstance(self.name, str): + self.name = str(self.name) + + if self.version is not None and not isinstance(self.version, str): + self.version = str(self.version) + + if self.type is not None and not isinstance(self.type, SourceTypeEnum): + self.type = SourceTypeEnum(self.type) - if self.birth_date is not None and not isinstance(self.birth_date, XSDDate): - self.birth_date = XSDDate(self.birth_date) + if self.documentation is not None and not isinstance(self.documentation, str): + self.documentation = str(self.documentation) - if self.age_in_years is not None and not isinstance(self.age_in_years, int): - self.age_in_years = int(self.age_in_years) + if self.content_url is not None and not isinstance(self.content_url, str): + self.content_url = str(self.content_url) - if self.vital_status is not None and not isinstance(self.vital_status, PersonStatus): - self.vital_status = PersonStatus(self.vital_status) + if self.content_type is not None and not isinstance(self.content_type, str): + self.content_type = str(self.content_type) + + if self.metadata_url is not None and not isinstance(self.metadata_url, str): + self.metadata_url = str(self.metadata_url) + + if self.metadata_type is not None and not isinstance(self.metadata_type, str): + self.metadata_type = str(self.metadata_type) super().__post_init__(**kwargs) @dataclass(repr=False) -class PersonCollection(YAMLRoot): +class MappingSpecification(YAMLRoot): """ - A holder for Person objects + A formal description of correspondences between entities in a source and a target, expressed as rules, functions, + or mapping statements. """ _inherited_slots: ClassVar[list[str]] = [] - class_class_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA["PersonCollection"] - class_class_curie: ClassVar[str] = "fair_mappings_schema:PersonCollection" - class_name: ClassVar[str] = "PersonCollection" - class_model_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA.PersonCollection + class_class_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA["MappingSpecification"] + class_class_curie: ClassVar[str] = "fair_mappings_schema:MappingSpecification" + class_name: ClassVar[str] = "MappingSpecification" + class_model_uri: ClassVar[URIRef] = FAIR_MAPPINGS_SCHEMA.MappingSpecification - entries: Optional[Union[dict[Union[str, PersonId], Union[dict, Person]], list[Union[dict, Person]]]] = empty_dict() + id: Optional[str] = None + name: Optional[str] = None + description: Optional[str] = None + author: Optional[Union[dict, Agent]] = None + creator: Optional[Union[dict, Agent]] = None + reviewer: Optional[Union[dict, Agent]] = None + publication_date: Optional[str] = None + license: Optional[str] = None + version: Optional[str] = None + type: Optional[Union[str, "MappingSpecificationTypeEnum"]] = None + mapping_method: Optional[str] = None + documentation: Optional[str] = None + content_url: Optional[str] = None + subject_source: Optional[Union[dict, Source]] = None + object_source: Optional[Union[dict, Source]] = None def __post_init__(self, *_: str, **kwargs: Any): - self._normalize_inlined_as_list(slot_name="entries", slot_type=Person, key_name="id", keyed=True) + if self.id is not None and not isinstance(self.id, str): + self.id = str(self.id) + + if self.name is not None and not isinstance(self.name, str): + self.name = str(self.name) + + if self.description is not None and not isinstance(self.description, str): + self.description = str(self.description) + + if self.author is not None and not isinstance(self.author, Agent): + self.author = Agent(**as_dict(self.author)) + + if self.creator is not None and not isinstance(self.creator, Agent): + self.creator = Agent(**as_dict(self.creator)) + + if self.reviewer is not None and not isinstance(self.reviewer, Agent): + self.reviewer = Agent(**as_dict(self.reviewer)) + + if self.publication_date is not None and not isinstance(self.publication_date, str): + self.publication_date = str(self.publication_date) + + if self.license is not None and not isinstance(self.license, str): + self.license = str(self.license) + + if self.version is not None and not isinstance(self.version, str): + self.version = str(self.version) + + if self.type is not None and not isinstance(self.type, MappingSpecificationTypeEnum): + self.type = MappingSpecificationTypeEnum(self.type) + + if self.mapping_method is not None and not isinstance(self.mapping_method, str): + self.mapping_method = str(self.mapping_method) + + if self.documentation is not None and not isinstance(self.documentation, str): + self.documentation = str(self.documentation) + + if self.content_url is not None and not isinstance(self.content_url, str): + self.content_url = str(self.content_url) + + if self.subject_source is not None and not isinstance(self.subject_source, Source): + self.subject_source = Source(**as_dict(self.subject_source)) + + if self.object_source is not None and not isinstance(self.object_source, Source): + self.object_source = Source(**as_dict(self.object_source)) super().__post_init__(**kwargs) # Enumerations -class PersonStatus(EnumDefinitionImpl): - - ALIVE = PermissibleValue( - text="ALIVE", - description="the person is living", - meaning=PATO["0001421"]) - DEAD = PermissibleValue( - text="DEAD", - description="the person is deceased", - meaning=PATO["0001422"]) - UNKNOWN = PermissibleValue( - text="UNKNOWN", - description="the vital status is not known") +class SourceTypeEnum(EnumDefinitionImpl): + """ + Types of data sources + """ + owl_ontology = PermissibleValue( + text="owl_ontology", + description="An ontology in OWL format") + database = PermissibleValue( + text="database", + description="A relational or other database") + skos_vocabulary = PermissibleValue( + text="skos_vocabulary", + description="A SKOS vocabulary or terminology") + rdf_vocabulary = PermissibleValue( + text="rdf_vocabulary", + description="An RDF vocabulary or schema") + schema = PermissibleValue( + text="schema", + description="A data schema or model") + api = PermissibleValue( + text="api", + description="An API or web service") + other = PermissibleValue( + text="other", + description="Other type of source") _defn = EnumDefinition( - name="PersonStatus", + name="SourceTypeEnum", + description="Types of data sources", + ) + +class MappingSpecificationTypeEnum(EnumDefinitionImpl): + """ + Types of mapping specifications + """ + sssom = PermissibleValue( + text="sssom", + description="Simple Standard for Sharing Ontological Mappings") + r2rml = PermissibleValue( + text="r2rml", + description="RDB to RDF Mapping Language") + rml = PermissibleValue( + text="rml", + description="RDF Mapping Language") + sparql = PermissibleValue( + text="sparql", + description="SPARQL-based mapping") + yarrrml = PermissibleValue( + text="yarrrml", + description="YARRRML mapping file") + xslt = PermissibleValue( + text="xslt", + description="XSLT-based mapping") + other = PermissibleValue( + text="other", + description="Other type of mapping specification") + + _defn = EnumDefinition( + name="MappingSpecificationTypeEnum", + description="Types of mapping specifications", ) # Slots class slots: pass -slots.id = Slot(uri=SCHEMA.identifier, name="id", curie=SCHEMA.curie('identifier'), - model_uri=FAIR_MAPPINGS_SCHEMA.id, domain=None, range=URIRef) +slots.id = Slot(uri=FAIR_MAPPINGS_SCHEMA.id, name="id", curie=FAIR_MAPPINGS_SCHEMA.curie('id'), + model_uri=FAIR_MAPPINGS_SCHEMA.id, domain=None, range=Optional[str]) -slots.name = Slot(uri=SCHEMA.name, name="name", curie=SCHEMA.curie('name'), +slots.name = Slot(uri=FAIR_MAPPINGS_SCHEMA.name, name="name", curie=FAIR_MAPPINGS_SCHEMA.curie('name'), model_uri=FAIR_MAPPINGS_SCHEMA.name, domain=None, range=Optional[str]) -slots.description = Slot(uri=SCHEMA.description, name="description", curie=SCHEMA.curie('description'), +slots.creator = Slot(uri=FAIR_MAPPINGS_SCHEMA.creator, name="creator", curie=FAIR_MAPPINGS_SCHEMA.curie('creator'), + model_uri=FAIR_MAPPINGS_SCHEMA.creator, domain=None, range=Optional[Union[dict, Agent]]) + +slots.author = Slot(uri=FAIR_MAPPINGS_SCHEMA.author, name="author", curie=FAIR_MAPPINGS_SCHEMA.curie('author'), + model_uri=FAIR_MAPPINGS_SCHEMA.author, domain=None, range=Optional[Union[dict, Agent]]) + +slots.reviewer = Slot(uri=FAIR_MAPPINGS_SCHEMA.reviewer, name="reviewer", curie=FAIR_MAPPINGS_SCHEMA.curie('reviewer'), + model_uri=FAIR_MAPPINGS_SCHEMA.reviewer, domain=None, range=Optional[Union[dict, Agent]]) + +slots.mapping_tool = Slot(uri=FAIR_MAPPINGS_SCHEMA.mapping_tool, name="mapping_tool", curie=FAIR_MAPPINGS_SCHEMA.curie('mapping_tool'), + model_uri=FAIR_MAPPINGS_SCHEMA.mapping_tool, domain=None, range=Optional[Union[dict, Agent]]) + +slots.publication_date = Slot(uri=FAIR_MAPPINGS_SCHEMA.publication_date, name="publication_date", curie=FAIR_MAPPINGS_SCHEMA.curie('publication_date'), + model_uri=FAIR_MAPPINGS_SCHEMA.publication_date, domain=None, range=Optional[str]) + +slots.license = Slot(uri=FAIR_MAPPINGS_SCHEMA.license, name="license", curie=FAIR_MAPPINGS_SCHEMA.curie('license'), + model_uri=FAIR_MAPPINGS_SCHEMA.license, domain=None, range=Optional[str]) + +slots.version = Slot(uri=FAIR_MAPPINGS_SCHEMA.version, name="version", curie=FAIR_MAPPINGS_SCHEMA.curie('version'), + model_uri=FAIR_MAPPINGS_SCHEMA.version, domain=None, range=Optional[str]) + +slots.description = Slot(uri=FAIR_MAPPINGS_SCHEMA.description, name="description", curie=FAIR_MAPPINGS_SCHEMA.curie('description'), model_uri=FAIR_MAPPINGS_SCHEMA.description, domain=None, range=Optional[str]) -slots.primary_email = Slot(uri=SCHEMA.email, name="primary_email", curie=SCHEMA.curie('email'), - model_uri=FAIR_MAPPINGS_SCHEMA.primary_email, domain=None, range=Optional[str]) +slots.type = Slot(uri=FAIR_MAPPINGS_SCHEMA.type, name="type", curie=FAIR_MAPPINGS_SCHEMA.curie('type'), + model_uri=FAIR_MAPPINGS_SCHEMA.type, domain=None, range=Optional[str]) + +slots.mapping_method = Slot(uri=FAIR_MAPPINGS_SCHEMA.mapping_method, name="mapping_method", curie=FAIR_MAPPINGS_SCHEMA.curie('mapping_method'), + model_uri=FAIR_MAPPINGS_SCHEMA.mapping_method, domain=None, range=Optional[str]) + +slots.documentation = Slot(uri=FAIR_MAPPINGS_SCHEMA.documentation, name="documentation", curie=FAIR_MAPPINGS_SCHEMA.curie('documentation'), + model_uri=FAIR_MAPPINGS_SCHEMA.documentation, domain=None, range=Optional[str]) + +slots.content_url = Slot(uri=FAIR_MAPPINGS_SCHEMA.content_url, name="content_url", curie=FAIR_MAPPINGS_SCHEMA.curie('content_url'), + model_uri=FAIR_MAPPINGS_SCHEMA.content_url, domain=None, range=Optional[str]) + +slots.content_type = Slot(uri=FAIR_MAPPINGS_SCHEMA.content_type, name="content_type", curie=FAIR_MAPPINGS_SCHEMA.curie('content_type'), + model_uri=FAIR_MAPPINGS_SCHEMA.content_type, domain=None, range=Optional[str]) + +slots.metadata_url = Slot(uri=FAIR_MAPPINGS_SCHEMA.metadata_url, name="metadata_url", curie=FAIR_MAPPINGS_SCHEMA.curie('metadata_url'), + model_uri=FAIR_MAPPINGS_SCHEMA.metadata_url, domain=None, range=Optional[str]) + +slots.metadata_type = Slot(uri=FAIR_MAPPINGS_SCHEMA.metadata_type, name="metadata_type", curie=FAIR_MAPPINGS_SCHEMA.curie('metadata_type'), + model_uri=FAIR_MAPPINGS_SCHEMA.metadata_type, domain=None, range=Optional[str]) + +slots.subject_source = Slot(uri=FAIR_MAPPINGS_SCHEMA.subject_source, name="subject_source", curie=FAIR_MAPPINGS_SCHEMA.curie('subject_source'), + model_uri=FAIR_MAPPINGS_SCHEMA.subject_source, domain=None, range=Optional[Union[dict, Source]]) + +slots.object_source = Slot(uri=FAIR_MAPPINGS_SCHEMA.object_source, name="object_source", curie=FAIR_MAPPINGS_SCHEMA.curie('object_source'), + model_uri=FAIR_MAPPINGS_SCHEMA.object_source, domain=None, range=Optional[Union[dict, Source]]) + +slots.orcid = Slot(uri=FAIR_MAPPINGS_SCHEMA.orcid, name="orcid", curie=FAIR_MAPPINGS_SCHEMA.curie('orcid'), + model_uri=FAIR_MAPPINGS_SCHEMA.orcid, domain=None, range=Optional[str]) + +slots.affiliation = Slot(uri=FAIR_MAPPINGS_SCHEMA.affiliation, name="affiliation", curie=FAIR_MAPPINGS_SCHEMA.curie('affiliation'), + model_uri=FAIR_MAPPINGS_SCHEMA.affiliation, domain=None, range=Optional[str]) + +slots.ror_id = Slot(uri=FAIR_MAPPINGS_SCHEMA.ror_id, name="ror_id", curie=FAIR_MAPPINGS_SCHEMA.curie('ror_id'), + model_uri=FAIR_MAPPINGS_SCHEMA.ror_id, domain=None, range=Optional[str]) -slots.birth_date = Slot(uri=SCHEMA.birthDate, name="birth_date", curie=SCHEMA.curie('birthDate'), - model_uri=FAIR_MAPPINGS_SCHEMA.birth_date, domain=None, range=Optional[Union[str, XSDDate]]) +slots.url = Slot(uri=FAIR_MAPPINGS_SCHEMA.url, name="url", curie=FAIR_MAPPINGS_SCHEMA.curie('url'), + model_uri=FAIR_MAPPINGS_SCHEMA.url, domain=None, range=Optional[str]) -slots.age_in_years = Slot(uri=FAIR_MAPPINGS_SCHEMA.age_in_years, name="age_in_years", curie=FAIR_MAPPINGS_SCHEMA.curie('age_in_years'), - model_uri=FAIR_MAPPINGS_SCHEMA.age_in_years, domain=None, range=Optional[int]) +slots.repository_url = Slot(uri=FAIR_MAPPINGS_SCHEMA.repository_url, name="repository_url", curie=FAIR_MAPPINGS_SCHEMA.curie('repository_url'), + model_uri=FAIR_MAPPINGS_SCHEMA.repository_url, domain=None, range=Optional[str]) -slots.vital_status = Slot(uri=FAIR_MAPPINGS_SCHEMA.vital_status, name="vital_status", curie=FAIR_MAPPINGS_SCHEMA.curie('vital_status'), - model_uri=FAIR_MAPPINGS_SCHEMA.vital_status, domain=None, range=Optional[Union[str, "PersonStatus"]]) +slots.Agent_type = Slot(uri=FAIR_MAPPINGS_SCHEMA.type, name="Agent_type", curie=FAIR_MAPPINGS_SCHEMA.curie('type'), + model_uri=FAIR_MAPPINGS_SCHEMA.Agent_type, domain=Agent, range=Optional[str]) -slots.personCollection__entries = Slot(uri=FAIR_MAPPINGS_SCHEMA.entries, name="personCollection__entries", curie=FAIR_MAPPINGS_SCHEMA.curie('entries'), - model_uri=FAIR_MAPPINGS_SCHEMA.personCollection__entries, domain=None, range=Optional[Union[dict[Union[str, PersonId], Union[dict, Person]], list[Union[dict, Person]]]]) +slots.Source_type = Slot(uri=FAIR_MAPPINGS_SCHEMA.type, name="Source_type", curie=FAIR_MAPPINGS_SCHEMA.curie('type'), + model_uri=FAIR_MAPPINGS_SCHEMA.Source_type, domain=Source, range=Optional[Union[str, "SourceTypeEnum"]]) -slots.Person_primary_email = Slot(uri=SCHEMA.email, name="Person_primary_email", curie=SCHEMA.curie('email'), - model_uri=FAIR_MAPPINGS_SCHEMA.Person_primary_email, domain=Person, range=Optional[str], - pattern=re.compile(r'^\S+@[\S+\.]+\S+')) +slots.MappingSpecification_type = Slot(uri=FAIR_MAPPINGS_SCHEMA.type, name="MappingSpecification_type", curie=FAIR_MAPPINGS_SCHEMA.curie('type'), + model_uri=FAIR_MAPPINGS_SCHEMA.MappingSpecification_type, domain=MappingSpecification, range=Optional[Union[str, "MappingSpecificationTypeEnum"]]) diff --git a/src/fair_mappings_schema/datamodel/fair_mappings_schema_pydantic.py b/src/fair_mappings_schema/datamodel/fair_mappings_schema_pydantic.py index 0d662cb..d33fda9 100644 --- a/src/fair_mappings_schema/datamodel/fair_mappings_schema_pydantic.py +++ b/src/fair_mappings_schema/datamodel/fair_mappings_schema_pydantic.py @@ -63,7 +63,7 @@ def __contains__(self, key:str) -> bool: linkml_meta = LinkMLMeta({'default_prefix': 'fair_mappings_schema', 'default_range': 'string', - 'description': 'A basic metadata schema for FAIR mapping specifications', + 'description': 'A minimal metadata schema for FAIR mapping specifications', 'id': 'https://w3id.org/mapping-commons/fair-mappings-schema', 'imports': ['linkml:types'], 'license': 'Apache-2.0', @@ -82,83 +82,192 @@ def __contains__(self, key:str) -> bool: 'prefix_reference': 'http://schema.org/'}}, 'see_also': ['https://mapping-commons.github.io/fair-mappings-schema'], 'source_file': 'src/fair_mappings_schema/schema/fair_mappings_schema.yaml', - 'title': 'fair-mappings-schema'} ) + 'title': 'FAIR Mappings Schema'} ) -class PersonStatus(str, Enum): - ALIVE = "ALIVE" +class SourceTypeEnum(str, Enum): """ - the person is living + Types of data sources """ - DEAD = "DEAD" + owl_ontology = "owl_ontology" """ - the person is deceased + An ontology in OWL format """ - UNKNOWN = "UNKNOWN" + database = "database" """ - the vital status is not known + A relational or other database + """ + skos_vocabulary = "skos_vocabulary" + """ + A SKOS vocabulary or terminology + """ + rdf_vocabulary = "rdf_vocabulary" + """ + An RDF vocabulary or schema + """ + schema = "schema" + """ + A data schema or model + """ + api = "api" + """ + An API or web service + """ + other = "other" + """ + Other type of source """ +class MappingSpecificationTypeEnum(str, Enum): + """ + Types of mapping specifications + """ + sssom = "sssom" + """ + Simple Standard for Sharing Ontological Mappings + """ + r2rml = "r2rml" + """ + RDB to RDF Mapping Language + """ + rml = "rml" + """ + RDF Mapping Language + """ + sparql = "sparql" + """ + SPARQL-based mapping + """ + yarrrml = "yarrrml" + """ + YARRRML mapping file + """ + xslt = "xslt" + """ + XSLT-based mapping + """ + other = "other" + """ + Other type of mapping specification + """ + + + +class Agent(ConfiguredBaseModel): + """ + An entity that can create or contribute to a digital object, such as an author or creator. + """ + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({'abstract': True, + 'from_schema': 'https://w3id.org/mapping-commons/fair-mappings-schema', + 'slot_usage': {'type': {'designates_type': True, + 'name': 'type', + 'range': 'string'}}}) + + id: Optional[str] = Field(default=None, description="""Identifier for the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'id', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + name: Optional[str] = Field(default=None, description="""Name of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'name', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + type: Literal["Agent"] = Field(default="Agent", description="""Type of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'type', + 'designates_type': True, + 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + -class NamedThing(ConfiguredBaseModel): +class Person(Agent): """ - A generic grouping for any identifiable entity + An individual person who contributes to a mapping specification """ - linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({'class_uri': 'schema:Thing', - 'from_schema': 'https://w3id.org/mapping-commons/fair-mappings-schema'}) + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({'from_schema': 'https://w3id.org/mapping-commons/fair-mappings-schema'}) - id: str = Field(default=..., description="""A unique identifier for a thing""", json_schema_extra = { "linkml_meta": {'alias': 'id', 'domain_of': ['NamedThing'], 'slot_uri': 'schema:identifier'} }) - name: Optional[str] = Field(default=None, description="""A human-readable name for a thing""", json_schema_extra = { "linkml_meta": {'alias': 'name', 'domain_of': ['NamedThing'], 'slot_uri': 'schema:name'} }) - description: Optional[str] = Field(default=None, description="""A human-readable description for a thing""", json_schema_extra = { "linkml_meta": {'alias': 'description', - 'domain_of': ['NamedThing'], - 'slot_uri': 'schema:description'} }) + orcid: Optional[str] = Field(default=None, description="""ORCID identifier for a person""", json_schema_extra = { "linkml_meta": {'alias': 'orcid', 'domain_of': ['Person']} }) + affiliation: Optional[str] = Field(default=None, description="""Institutional affiliation of a person""", json_schema_extra = { "linkml_meta": {'alias': 'affiliation', 'domain_of': ['Person']} }) + id: Optional[str] = Field(default=None, description="""Identifier for the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'id', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + name: Optional[str] = Field(default=None, description="""Name of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'name', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + type: Literal["Person"] = Field(default="Person", description="""Type of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'type', + 'designates_type': True, + 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) -class Person(NamedThing): +class Organization(Agent): """ - Represents a Person + An organization or institution that contributes to a mapping specification + """ + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({'from_schema': 'https://w3id.org/mapping-commons/fair-mappings-schema'}) + + ror_id: Optional[str] = Field(default=None, description="""ROR (Research Organization Registry) identifier""", json_schema_extra = { "linkml_meta": {'alias': 'ror_id', 'domain_of': ['Organization']} }) + url: Optional[str] = Field(default=None, description="""URL or web address""", json_schema_extra = { "linkml_meta": {'alias': 'url', 'domain_of': ['Organization']} }) + id: Optional[str] = Field(default=None, description="""Identifier for the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'id', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + name: Optional[str] = Field(default=None, description="""Name of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'name', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + type: Literal["Organization"] = Field(default="Organization", description="""Type of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'type', + 'designates_type': True, + 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + + +class Software(Agent): + """ + A software tool or system used in creating mappings + """ + linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({'from_schema': 'https://w3id.org/mapping-commons/fair-mappings-schema'}) + + version: Optional[str] = Field(default=None, description="""Version of the digital object""", json_schema_extra = { "linkml_meta": {'alias': 'version', + 'domain_of': ['Software', 'Source', 'MappingSpecification']} }) + repository_url: Optional[str] = Field(default=None, description="""URL to a code repository""", json_schema_extra = { "linkml_meta": {'alias': 'repository_url', 'domain_of': ['Software']} }) + id: Optional[str] = Field(default=None, description="""Identifier for the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'id', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + name: Optional[str] = Field(default=None, description="""Name of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'name', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + type: Literal["Software"] = Field(default="Software", description="""Type of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'type', + 'designates_type': True, + 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + + +class Source(ConfiguredBaseModel): + """ + A data source from which entities are drawn, such as a database, ontology, or vocabulary. """ linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({'from_schema': 'https://w3id.org/mapping-commons/fair-mappings-schema', - 'slot_usage': {'primary_email': {'name': 'primary_email', - 'pattern': '^\\S+@[\\S+\\.]+\\S+'}}}) - - primary_email: Optional[str] = Field(default=None, description="""The main email address of a person""", json_schema_extra = { "linkml_meta": {'alias': 'primary_email', 'domain_of': ['Person'], 'slot_uri': 'schema:email'} }) - birth_date: Optional[date] = Field(default=None, description="""Date on which a person is born""", json_schema_extra = { "linkml_meta": {'alias': 'birth_date', 'domain_of': ['Person'], 'slot_uri': 'schema:birthDate'} }) - age_in_years: Optional[int] = Field(default=None, description="""Number of years since birth""", json_schema_extra = { "linkml_meta": {'alias': 'age_in_years', 'domain_of': ['Person']} }) - vital_status: Optional[PersonStatus] = Field(default=None, description="""living or dead status""", json_schema_extra = { "linkml_meta": {'alias': 'vital_status', 'domain_of': ['Person']} }) - id: str = Field(default=..., description="""A unique identifier for a thing""", json_schema_extra = { "linkml_meta": {'alias': 'id', 'domain_of': ['NamedThing'], 'slot_uri': 'schema:identifier'} }) - name: Optional[str] = Field(default=None, description="""A human-readable name for a thing""", json_schema_extra = { "linkml_meta": {'alias': 'name', 'domain_of': ['NamedThing'], 'slot_uri': 'schema:name'} }) - description: Optional[str] = Field(default=None, description="""A human-readable description for a thing""", json_schema_extra = { "linkml_meta": {'alias': 'description', - 'domain_of': ['NamedThing'], - 'slot_uri': 'schema:description'} }) - - @field_validator('primary_email') - def pattern_primary_email(cls, v): - pattern=re.compile(r"^\S+@[\S+\.]+\S+") - if isinstance(v, list): - for element in v: - if isinstance(element, str) and not pattern.match(element): - err_msg = f"Invalid primary_email format: {element}" - raise ValueError(err_msg) - elif isinstance(v, str) and not pattern.match(v): - err_msg = f"Invalid primary_email format: {v}" - raise ValueError(err_msg) - return v - - -class PersonCollection(ConfiguredBaseModel): - """ - A holder for Person objects + 'slot_usage': {'type': {'name': 'type', 'range': 'SourceTypeEnum'}}}) + + id: Optional[str] = Field(default=None, description="""Identifier for the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'id', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + name: Optional[str] = Field(default=None, description="""Name of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'name', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + version: Optional[str] = Field(default=None, description="""Version of the digital object""", json_schema_extra = { "linkml_meta": {'alias': 'version', + 'domain_of': ['Software', 'Source', 'MappingSpecification']} }) + type: Optional[SourceTypeEnum] = Field(default=None, description="""Type of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'type', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + documentation: Optional[str] = Field(default=None, description="""URL or reference to documentation for the mapping specification""", json_schema_extra = { "linkml_meta": {'alias': 'documentation', 'domain_of': ['Source', 'MappingSpecification']} }) + content_url: Optional[str] = Field(default=None, description="""Reference to the actual content of the digital object""", json_schema_extra = { "linkml_meta": {'alias': 'content_url', 'domain_of': ['Source', 'MappingSpecification']} }) + content_type: Optional[str] = Field(default=None, description="""The type of the content of the digital object""", json_schema_extra = { "linkml_meta": {'alias': 'content_type', 'domain_of': ['Source']} }) + metadata_url: Optional[str] = Field(default=None, description="""Reference to metadata about the digital object""", json_schema_extra = { "linkml_meta": {'alias': 'metadata_url', 'domain_of': ['Source']} }) + metadata_type: Optional[str] = Field(default=None, description="""The type of the metadata about the digital object""", json_schema_extra = { "linkml_meta": {'alias': 'metadata_type', 'domain_of': ['Source']} }) + + +class MappingSpecification(ConfiguredBaseModel): + """ + A formal description of correspondences between entities in a source and a target, expressed as rules, functions, or mapping statements. """ linkml_meta: ClassVar[LinkMLMeta] = LinkMLMeta({'from_schema': 'https://w3id.org/mapping-commons/fair-mappings-schema', + 'slot_usage': {'type': {'name': 'type', + 'range': 'MappingSpecificationTypeEnum'}}, 'tree_root': True}) - entries: Optional[list[Person]] = Field(default=None, json_schema_extra = { "linkml_meta": {'alias': 'entries', 'domain_of': ['PersonCollection']} }) + id: Optional[str] = Field(default=None, description="""Identifier for the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'id', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + name: Optional[str] = Field(default=None, description="""Name of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'name', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + description: Optional[str] = Field(default=None, description="""A brief description of the mapping specification""", json_schema_extra = { "linkml_meta": {'alias': 'description', 'domain_of': ['MappingSpecification']} }) + author: Optional[Union[Agent,Person,Organization,Software]] = Field(default=None, description="""Author of the mapping specification""", json_schema_extra = { "linkml_meta": {'alias': 'author', 'domain_of': ['MappingSpecification']} }) + creator: Optional[Union[Agent,Person,Organization,Software]] = Field(default=None, description="""Creator of the mapping specification""", json_schema_extra = { "linkml_meta": {'alias': 'creator', 'domain_of': ['MappingSpecification']} }) + reviewer: Optional[Union[Agent,Person,Organization,Software]] = Field(default=None, description="""Reviewer of the mapping specification""", json_schema_extra = { "linkml_meta": {'alias': 'reviewer', 'domain_of': ['MappingSpecification']} }) + publication_date: Optional[str] = Field(default=None, description="""Date of publication of the mapping specification""", json_schema_extra = { "linkml_meta": {'alias': 'publication_date', 'domain_of': ['MappingSpecification']} }) + license: Optional[str] = Field(default=None, description="""License under which the mapping specification is released""", json_schema_extra = { "linkml_meta": {'alias': 'license', 'domain_of': ['MappingSpecification']} }) + version: Optional[str] = Field(default=None, description="""Version of the digital object""", json_schema_extra = { "linkml_meta": {'alias': 'version', + 'domain_of': ['Software', 'Source', 'MappingSpecification']} }) + type: Optional[MappingSpecificationTypeEnum] = Field(default=None, description="""Type of the information entity""", json_schema_extra = { "linkml_meta": {'alias': 'type', 'domain_of': ['Agent', 'Source', 'MappingSpecification']} }) + mapping_method: Optional[str] = Field(default=None, description="""Method used to create the mapping specification""", json_schema_extra = { "linkml_meta": {'alias': 'mapping_method', 'domain_of': ['MappingSpecification']} }) + documentation: Optional[str] = Field(default=None, description="""URL or reference to documentation for the mapping specification""", json_schema_extra = { "linkml_meta": {'alias': 'documentation', 'domain_of': ['Source', 'MappingSpecification']} }) + content_url: Optional[str] = Field(default=None, description="""Reference to the actual content of the digital object""", json_schema_extra = { "linkml_meta": {'alias': 'content_url', 'domain_of': ['Source', 'MappingSpecification']} }) + subject_source: Optional[Source] = Field(default=None, description="""The source from which the subject entities are drawn""", json_schema_extra = { "linkml_meta": {'alias': 'subject_source', 'domain_of': ['MappingSpecification']} }) + object_source: Optional[Source] = Field(default=None, description="""The source from which the object entities are drawn""", json_schema_extra = { "linkml_meta": {'alias': 'object_source', 'domain_of': ['MappingSpecification']} }) # Model rebuild # see https://pydantic-docs.helpmanual.io/usage/models/#rebuilding-a-model -NamedThing.model_rebuild() +Agent.model_rebuild() Person.model_rebuild() -PersonCollection.model_rebuild() +Organization.model_rebuild() +Software.model_rebuild() +Source.model_rebuild() +MappingSpecification.model_rebuild() diff --git a/src/fair_mappings_schema/schema/fair_mappings_schema.yaml b/src/fair_mappings_schema/schema/fair_mappings_schema.yaml index 917eba0..cfaeaca 100644 --- a/src/fair_mappings_schema/schema/fair_mappings_schema.yaml +++ b/src/fair_mappings_schema/schema/fair_mappings_schema.yaml @@ -1,7 +1,7 @@ --- id: https://w3id.org/mapping-commons/fair-mappings-schema name: fair-mappings-schema -title: fair-mappings-schema +title: FAIR Mappings Schema description: |- A minimal metadata schema for FAIR mapping specifications license: Apache-2.0 @@ -21,3 +21,195 @@ default_range: string imports: - linkml:types +enums: + SourceTypeEnum: + description: Types of data sources + permissible_values: + owl_ontology: + description: An ontology in OWL format + database: + description: A relational or other database + skos_vocabulary: + description: A SKOS vocabulary or terminology + rdf_vocabulary: + description: An RDF vocabulary or schema + schema: + description: A data schema or model + api: + description: An API or web service + other: + description: Other type of source + + MappingSpecificationTypeEnum: + description: Types of mapping specifications + permissible_values: + sssom: + description: Simple Standard for Sharing Ontological Mappings + r2rml: + description: RDB to RDF Mapping Language + rml: + description: RDF Mapping Language + sparql: + description: SPARQL-based mapping + yarrrml: + description: YARRRML mapping file + xslt: + description: XSLT-based mapping + other: + description: Other type of mapping specification + +classes: + + Agent: + description: An entity that can create or contribute to a digital object, such as an author or creator. + abstract: true + slots: + - id + - name + - type + slot_usage: + type: + designates_type: true + range: string + + Person: + description: An individual person who contributes to a mapping specification + is_a: Agent + slots: + - orcid + - affiliation + + Organization: + description: An organization or institution that contributes to a mapping specification + is_a: Agent + slots: + - ror_id + - url + + Software: + description: A software tool or system used in creating mappings + is_a: Agent + slots: + - version + - repository_url + + Source: + description: >- + A data source from which entities are drawn, such as a database, ontology, or vocabulary. + slots: + - id + - name + - version + - type + - documentation + - content_url + - content_type + - metadata_url + - metadata_type + slot_usage: + type: + range: SourceTypeEnum + + MappingSpecification: + description: >- + A formal description of correspondences between entities in a source and a target, expressed as rules, functions, or mapping statements. + tree_root: true + slots: + - id + - name + - description + - author + - creator + - reviewer + - publication_date + - license + - version + - type + - mapping_method + - documentation + - content_url + - subject_source + - object_source + slot_usage: + type: + range: MappingSpecificationTypeEnum + +slots: + id: + description: Identifier for the information entity + range: string + name: + description: Name of the information entity + range: string + creator: + description: Creator of the mapping specification + range: Agent + inlined: true + author: + description: Author of the mapping specification + range: Agent + inlined: true + reviewer: + description: Reviewer of the mapping specification + range: Agent + inlined: true + mapping_tool: + description: The tool used to create the mapping specification + range: Agent + inlined: true + publication_date: + description: Date of publication of the mapping specification + range: string + license: + description: License under which the mapping specification is released + range: string + version: + description: Version of the digital object + range: string + description: + description: A brief description of the mapping specification + range: string + type: + description: Type of the information entity + range: string + mapping_method: + description: Method used to create the mapping specification + range: string + documentation: + description: URL or reference to documentation for the mapping specification + range: string + content_url: + description: Reference to the actual content of the digital object + range: string + content_type: + description: The type of the content of the digital object + range: string + metadata_url: + description: Reference to metadata about the digital object + range: string + metadata_type: + description: The type of the metadata about the digital object + range: string + subject_source: + description: The source from which the subject entities are drawn + range: Source + inlined: true + object_source: + description: The source from which the object entities are drawn + range: Source + inlined: true + orcid: + description: ORCID identifier for a person + range: string + affiliation: + description: Institutional affiliation of a person + range: string + ror_id: + description: ROR (Research Organization Registry) identifier + range: string + url: + description: URL or web address + range: string + repository_url: + description: URL to a code repository + range: string diff --git a/tests/data/invalid/MappingSpecification-001.yaml b/tests/data/invalid/MappingSpecification-001.yaml new file mode 100644 index 0000000..c6435c1 --- /dev/null +++ b/tests/data/invalid/MappingSpecification-001.yaml @@ -0,0 +1,12 @@ +# Invalid MappingSpecification - author is a string instead of Agent object +--- +author: "Jane Smith" +content: https://example.org/mappings/invalid.sssom.tsv +subject_source: + name: Source X + content: https://example.org/source-x + content_type: text/plain +object_source: + name: Source Y + content: https://example.org/source-y + content_type: text/plain diff --git a/tests/data/invalid/MappingSpecification-002.yaml b/tests/data/invalid/MappingSpecification-002.yaml new file mode 100644 index 0000000..6772da0 --- /dev/null +++ b/tests/data/invalid/MappingSpecification-002.yaml @@ -0,0 +1,11 @@ +# Invalid MappingSpecification - subject_source is a string instead of Source object +--- +author: + id: example:author456 + name: Test Author +content: https://example.org/mapping.txt +subject_source: "Source A" +object_source: + name: Source B + content: https://example.org/source-b + content_type: text/plain diff --git a/tests/data/invalid/MappingSpecification-003.yaml b/tests/data/invalid/MappingSpecification-003.yaml new file mode 100644 index 0000000..a2e7ba0 --- /dev/null +++ b/tests/data/invalid/MappingSpecification-003.yaml @@ -0,0 +1,17 @@ +# Invalid MappingSpecification - creator is a list instead of Agent object +--- +author: + id: example:author789 + name: Another Author +creator: + - John Doe + - Jane Smith +content: https://example.org/mapping.txt +subject_source: + name: Valid Source + content: https://example.org/source-valid + content_type: text/plain +object_source: + name: Another Source + content: https://example.org/source-invalid + content_type: text/plain diff --git a/tests/data/valid/MappingSpecification-001.yaml b/tests/data/valid/MappingSpecification-001.yaml new file mode 100644 index 0000000..3c551ec --- /dev/null +++ b/tests/data/valid/MappingSpecification-001.yaml @@ -0,0 +1,24 @@ +# Valid MappingSpecification example - SSSOM mapping between two ontologies +--- +publication_date: "2024-01-15" +license: CC-BY-4.0 +version: "1.0.0" +description: Mappings between disease ontologies DO and MONDO +type: sssom +mapping_method: manual curation +documentation: https://example.org/do-mondo-mappings/docs +content_url: https://example.org/mappings/do-mondo.sssom.tsv +subject_source: + name: Disease Ontology + version: "2024-01-01" + type: owl_ontology + documentation: https://disease-ontology.org/ + content_url: http://purl.obolibrary.org/obo/doid.owl + content_type: application/rdf+xml +object_source: + name: Mondo Disease Ontology + version: "2024-01-10" + type: owl_ontology + documentation: https://mondo.monarchinitiative.org/ + content_url: http://purl.obolibrary.org/obo/mondo.owl + content_type: application/rdf+xml diff --git a/tests/data/valid/MappingSpecification-002.yaml b/tests/data/valid/MappingSpecification-002.yaml new file mode 100644 index 0000000..afe4c0c --- /dev/null +++ b/tests/data/valid/MappingSpecification-002.yaml @@ -0,0 +1,25 @@ +# Valid MappingSpecification example - R2RML mapping between database and RDF +--- + +publication_date: "2023-11-20" +license: Apache-2.0 +version: "2.1.0" +description: R2RML mapping from legacy patient database to FHIR RDF +type: r2rml +mapping_method: automated generation with manual review +documentation: https://example.org/patient-fhir-mapping +content_url: https://example.org/mappings/patient-db-to-fhir.r2rml.ttl +subject_source: + name: Patient Database + version: "v5.2" + type: database + documentation: https://example.org/db-schema + content_url: postgresql://example.org:5432/patients + content_type: application/sql +object_source: + name: FHIR RDF Ontology + version: "4.0.1" + type: rdf_vocabulary + documentation: http://hl7.org/fhir/ + content_url: http://hl7.org/fhir/fhir.ttl + content_type: text/turtle diff --git a/tests/data/valid/MappingSpecification-003.yaml b/tests/data/valid/MappingSpecification-003.yaml new file mode 100644 index 0000000..4868423 --- /dev/null +++ b/tests/data/valid/MappingSpecification-003.yaml @@ -0,0 +1,11 @@ +# Valid MappingSpecification example - minimal required fields only +--- +content_url: https://example.org/minimal-mapping.txt +subject_source: + name: Source A + content_url: https://example.org/source-a + content_type: text/plain +object_source: + name: Source B + content_url: https://example.org/source-b + content_type: text/plain