diff --git a/CHANGELOG.md b/CHANGELOG.md index 709cf2b..2638129 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,23 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ## [Unreleased] +### Added + + - Validate: added "mode" parameter + +### Fixed + +- respect owl:imports in OWL2 profile validation + +### Changed + + - Validate: the entity output includes the reasoner option on path "reason" + - Detailed axiom generator documentation + - The axiom generator ObjectPropertyCharacteristic does not yield results. Currently, this axiom generator and its + - (working) counterpart DataPropertyCharacteristic are removed from the Reason plugin. + +## [1.0.0beta5] 2024-08-15 + ### Added - defined input and output schema diff --git a/README-public.md b/README-public.md index 4500bbf..0689bbd 100644 --- a/README-public.md +++ b/README-public.md @@ -1,10 +1,13 @@ # cmem-plugin-reason -This [eccenca](https://eccenca.com) [Corporate Memory](https://documentation.eccenca.com) workflow plugin performs reasoning using [ROBOT](http://robot.obolibrary.org/). +This [eccenca](https://eccenca.com) [Corporate Memory](https://documentation.eccenca.com) workflow plugin bundle contains plugins performing reasoning (Reason) andontology consistency checking (Validate) using [ROBOT](http://robot.obolibrary.org/). ROBOT is published under the [BSD 3-Clause "New" or "Revised" License](https://choosealicense.com/licenses/bsd-3-clause/). -Copyright © 2015, the Authors - +Copyright © 2015, the authors. All rights reserved. +### Installation +``` +➜ cmemc admin workspace python install cmem-plugin-reason +``` diff --git a/README.md b/README.md index ea9783a..de66ab8 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This [eccenca](https://eccenca.com) [Corporate Memory](https://documentation.ecc [![eccenca Corporate Memory](https://img.shields.io/badge/eccenca-Corporate%20Memory-orange)](https://documentation.eccenca.com) [![workflow](https://github.com/eccenca/cmem-plugin-pyshacl/actions/workflows/check.yml/badge.svg)](https://github.com/eccenca/cmem-plugin-pyshacl/actions) [![pypi version](https://img.shields.io/pypi/v/cmem-plugin-reason)](https://pypi.org/project/cmem-plugin-reason/) [![license](https://img.shields.io/pypi/l/cmem-plugin-reason)](https://pypi.org/project/cmem-plugin-reasom) ROBOT is published under the [BSD 3-Clause "New" or "Revised" License](https://choosealicense.com/licenses/bsd-3-clause/). -Copyright © 2015, the Authors +Copyright © 2015, the authors. All rights reserved. ## Build @@ -39,11 +39,11 @@ and `owl:Ontology`. The IRI of the input ontology graph. The graph IRI is selected from a list of graphs of type`owl:Ontology`. -### Result graph IRI +### Output graph IRI The IRI of the output graph for the reasoning result. -:warning: Existing graphs will be overwritten. +⚠️ Existing graphs will be overwritten. ### Reasoner @@ -57,30 +57,105 @@ The following reasoner options are supported: ### Generated Axioms -By default, the reason operation will only assert inferred subclass axioms. The plugin provides the following -parameters to include inferred axiom generators: +The plugin provides the following parameters to include inferred axiom generators: #### Class axiom generators -- SubClass -- EquivalentClass -- DisjointClasses +- **Class inclusion (rdfs:subClassOf)** +The reasoner will infer assertions about the hierarchy of classes, i.e. +`SubClassOf:` statements. +If there are classes `Person`, `Student` and `Professor`, such that `Person DisjointUnionOf: +Student, Professor` holds, the reasoner will infer `Student SubClassOf: Person`. + + +- **Class equivalence (owl:equivalentClass)** +The reasoner will infer assertions about the equivalence of classes, i.e. +`EquivalentTo:` statements. +If there are classes `Person`, `Student` and `Professor`, such that `Person DisjointUnionOf: +Student, Professor` holds, the reasoner will infer `Person EquivalentTo: Student and Professor`. + + +- **Class disjointness (owl:disjointWith)** +The reasoner will infer assertions about the disjointness of classes, i.e. +`DisjointClasses:` statements. +If there are classes `Person`, `Student` and `Professor`, such that `Person DisjointUnionOf: +Student, Professor` holds, the reasoner will infer `DisjointClasses: Student, Professor`. + + +- **Data property equivalence (owl:equivalentProperty)** +The reasoner will infer axioms about the equivalence of data properties, + i.e. `EquivalentProperties` statements. +If there are data properties `identifier` and `enrollmentNumber`, such that `enrollmentNumber +SubPropertyOf: identifier` and `identifier SubPropertyOf: enrollmentNumber` holds, the reasoner +will infer `Student EquivalentProperties: identifier, enrollmentNumber`. + + +- **Data property inclusion (rdfs:subPropertyOf)** +The reasoner will infer axioms about the hierarchy of data properties, +i.e. `SubPropertyOf:` statements. +If there are data properties `identifier`, `studentIdentifier` and `enrollmentNumber`, such that +`studentIdentifier SubPropertyOf: identifier` and `enrollmentNumber SubPropertyOf: +studentIdentifier` holds, the reasoner will infer `enrollmentNumber SubPropertyOf: identifier`. -#### Data property axiom generators -- DataPropertyCharacteristic -- EquivalentDataProperties -- SubDataProperty #### Individual axiom generators -- ClassAssertion -- PropertyAssertion +- **Individual class assertions (rdf:type)** +The reasoner will infer assertions about the classes of individuals, i.e. +`Types:` statements. +Assume, there are classes `Person`, `Student` and `University` as well as the property +`enrolledIn`, such that `Student EquivalentTo: Person and enrolledIn some University` holds. For +the individual `John` with the assertions `John Types: Person; Facts: enrolledIn +LeipzigUniversity`, the reasoner will infer `John Types: Student`. + + +- **Individual property assertions** +The reasoner will infer assertions about the properties of individuals, +i.e. `Facts:` statements. +Assume, there are properties `enrolledIn` and `offers`, such that `enrolled SubPropertyChain: +enrolledIn o inverse (offers)` holds. For the individuals `John`and `LeipzigUniversity` with the +assertions `John Facts: enrolledIn KnowledgeRepresentation` and `LeipzigUniversity Facts: offers +KnowledgeRepresentation`, the reasoner will infer `John Facts: enrolledIn LeipzigUniversity`. + #### Object property axiom generators -- EquivalentObjectProperty -- InverseObjectProperties -- ObjectPropertyCharacteristic -- SubObjectProperty -- ObjectPropertyRange -- ObjectPropertyDomain +- **Object property equivalence (owl:equivalentProperty)** +The reasoner will infer assertions about the equivalence of object +properties, i.e. `EquivalentTo:` statements. +If there are object properties `hasAlternativeLecture` and `hasSameTopicAs`, such that +`hasAlternativeLecture Characteristics: Symmetric` and `hasSameTopicAs InverseOf: +hasAlternativeLecture` holds, the reasoner will infer `EquivalentProperties: hasAlternativeLecture, +hasSameTopicAs`. + + +- **Object property inversion (owl:inverseOf)** +The reasoner will infer axioms about the inversion about object +properties, i.e. `InverseOf:` statements. +If there is a object property `hasAlternativeLecture`, such that `hasAlternativeLecture +Characteristics: Symmetric` holds, the reasoner will infer `hasAlternativeLecture InverseOf: +hasAlternativeLecture`. + + +- **Object property inclusion (rdfs:subPropertyOf)** +The reasoner will infer axioms about the inclusion of object properties, +i.e. `SubPropertyOf:` statements. +If there are object properties `enrolledIn`, `studentOf` and `hasStudent`, such that `enrolledIn +SubPropertyOf: studentOf` and `enrolledIn InverseOf: hasStudent` holds, the reasoner will infer +`hasStudent SubPropertyOf: inverse (studentOf)`. + + +- **Object property ranges (rdfs:range)** +The reasoner will infer axioms about the ranges of object properties, +i.e. `Range:` statements. +If there are classes `Student` and `Lecture` as wells as object properties `hasStudent` and +`enrolledIn`, such that `hasStudent Range: Student and enrolledIn some Lecture` holds, the +reasoner will infer `hasStudent Range: Student`. + + +- **Object property domains (rdfs:domain)** +The reasoner will infer axioms about the domains of object +properties, i.e. `Domain:` statements. +If there are classes `Person`, `Student` and `Professor` as wells as the object property +`hasRoleIn`, such that `Professor SubClassOf: Person`, `Student SubClassOf: Person` and +`hasRoleIn Domain: Professor or Student` holds, the reasoner will infer `hasRoleIn Domain: Person`. ### Validate OWL2 profiles @@ -88,9 +163,9 @@ Validate the input ontology against OWL profiles (DL, EL, QL, RL, and Full) and ### Process valid OWL profiles from input -If enabled along with the "Validate OWL2 profiles" parameter, the valid profiles and ontology IRI is taken from the -config port input (parameters "valid_profiles" and "ontology_graph_iri") instead of from running the validation in the -plugin. The valid profiles input is a comma-separated string (e.g. "Full,DL"). +If enabled along with the "Validate OWL2 profiles" parameter, the valid profiles, ontology IRI and reasoner option is +taken from the config port input (parameters "valid_profiles", "ontology_graph_iri" and "reasoner") and the OWL2 +profiles validation is not done in the plugin. The valid profiles input is a comma-separated string (e.g. "Full,DL"). ### Add ontology graph import to result graph @@ -104,7 +179,7 @@ Add the triple ` owl:imports ` to the onto Maximum heap size for the Java virtual machine in the DI container running the reasoning process. -:warning: Setting the percentage too high may result in an out of memory error. +⚠️ Setting the percentage too high may result in an out of memory error. # Validate @@ -136,7 +211,7 @@ If enabled, an explanation graph is created. The IRI of the output graph for the reasoning result. -:warning: Existing graphs will be overwritten. +⚠️ Existing graphs will be overwritten. ### Write markdown explanation file @@ -146,11 +221,15 @@ If enabled, an explanation markdown file is written to the project. The filename of the Markdown file with the explanation of inconsistencies. -:warning: Existing files will be overwritten. +⚠️ Existing files will be overwritten. ### Stop at inconsistencies Raise an error if inconsistencies are found. If enabled, the plugin does not output entities. +### Mode +Mode _inconsistency_ generates an explanation for an inconsistent ontology. +Mode _unsatisfiability_ generates explanations for many unsatisfiable classes at once. + ### Validate OWL2 profiles Validate the input ontology against OWL profiles (DL, EL, QL, RL, and Full) and annotate the result graph. @@ -158,10 +237,11 @@ Validate the input ontology against OWL profiles (DL, EL, QL, RL, and Full) and ### Output entities Output entities. The plugin outputs the explanation as text in Markdown format on the path "markdown", the ontology IRI -on the path "ontology_graph_iri", and, if enabled, the valid OWL2 profiles on the path "valid_profiles +on the path "ontology_graph_iri", the reasoner option on the path "reasoner", and, if enabled, the valid OWL2 profiles +on the path "valid_profiles". ### Maximum RAM Percentage Maximum heap size for the Java virtual machine in the DI container running the reasoning process. -:warning: Setting the percentage too high may result in an out of memory error. \ No newline at end of file +⚠️ Setting the percentage too high may result in an out of memory error. \ No newline at end of file diff --git a/cmem_plugin_reason/doc/__init__.py b/cmem_plugin_reason/doc/__init__.py new file mode 100644 index 0000000..4dc90ec --- /dev/null +++ b/cmem_plugin_reason/doc/__init__.py @@ -0,0 +1,9 @@ +"""doc""" + +from pathlib import Path + +with (Path(__path__[0]) / "reason_doc.md").open("r") as f: + REASON_DOC = f.read() + +with (Path(__path__[0]) / "validate_doc.md").open("r") as f: + VALIDATE_DOC = f.read() diff --git a/cmem_plugin_reason/doc/reason_doc.md b/cmem_plugin_reason/doc/reason_doc.md new file mode 100644 index 0000000..b1c403e --- /dev/null +++ b/cmem_plugin_reason/doc/reason_doc.md @@ -0,0 +1,155 @@ +A task performing OWL reasoning. With an OWL ontology and a data graph as input the reasoning result is written to a specified graph. + +## Options + +### Data graph IRI + +The IRI of the input data graph. The graph IRI is selected from a list of graphs of types `di:Dataset`, `void:Dataset` +and `owl:Ontology`. + +### Ontology graph IRI + +The IRI of the input ontology graph. The graph IRI is selected from a list of graphs of type`owl:Ontology`. + +### Output graph IRI + +The IRI of the output graph for the reasoning result. + +⚠️ Existing graphs will be overwritten. + +### Reasoner + +The following reasoner options are supported: +- [ELK](https://code.google.com/p/elk-reasoner/) (elk) +- [Expression Materializing Reasoner](http://static.javadoc.io/org.geneontology/expression-materializing-reasoner/0.1.3/org/geneontology/reasoner/ExpressionMaterializingReasoner.html) (emr) +- [HermiT](http://www.hermit-reasoner.com/) (hermit) +- [JFact](http://jfact.sourceforge.net/) (jfact) +- [Structural Reasoner](http://owlcs.github.io/owlapi/apidocs_4/org/semanticweb/owlapi/reasoner/structural/StructuralReasoner.html) (structural) +- [Whelk](https://github.com/balhoff/whelk) (whelk) + +### Generated Axioms + +The plugin provides the following parameters to include inferred axiom generators: + +#### Class axiom generators +- **Class inclusion (rdfs:subClassOf)** +The reasoner will infer assertions about the hierarchy of classes, i.e. +`SubClassOf:` statements. +If there are classes `Person`, `Student` and `Professor`, such that `Person DisjointUnionOf: +Student, Professor` holds, the reasoner will infer `Student SubClassOf: Person`. + + +- **Class equivalence (owl:equivalentClass)** +The reasoner will infer assertions about the equivalence of classes, i.e. +`EquivalentTo:` statements. +If there are classes `Person`, `Student` and `Professor`, such that `Person DisjointUnionOf: +Student, Professor` holds, the reasoner will infer `Person EquivalentTo: Student and Professor`. + + +- **Class disjointness (owl:disjointWith)** +The reasoner will infer assertions about the disjointness of classes, i.e. +`DisjointClasses:` statements. +If there are classes `Person`, `Student` and `Professor`, such that `Person DisjointUnionOf: +Student, Professor` holds, the reasoner will infer `DisjointClasses: Student, Professor`. + + +- **Data property equivalence (owl:equivalentProperty)** +The reasoner will infer axioms about the equivalence of data properties, + i.e. `EquivalentProperties` statements. +If there are data properties `identifier` and `enrollmentNumber`, such that `enrollmentNumber +SubPropertyOf: identifier` and `identifier SubPropertyOf: enrollmentNumber` holds, the reasoner +will infer `Student EquivalentProperties: identifier, enrollmentNumber`. + + +- **Data property inclusion (rdfs:subPropertyOf)** +The reasoner will infer axioms about the hierarchy of data properties, +i.e. `SubPropertyOf:` statements. +If there are data properties `identifier`, `studentIdentifier` and `enrollmentNumber`, such that +`studentIdentifier SubPropertyOf: identifier` and `enrollmentNumber SubPropertyOf: +studentIdentifier` holds, the reasoner will infer `enrollmentNumber SubPropertyOf: identifier`. + + +#### Individual axiom generators +- **Individual class assertions (rdf:type)** +The reasoner will infer assertions about the classes of individuals, i.e. +`Types:` statements. +Assume, there are classes `Person`, `Student` and `University` as well as the property +`enrolledIn`, such that `Student EquivalentTo: Person and enrolledIn some University` holds. For +the individual `John` with the assertions `John Types: Person; Facts: enrolledIn +LeipzigUniversity`, the reasoner will infer `John Types: Student`. + + +- **Individual property assertions** +The reasoner will infer assertions about the properties of individuals, +i.e. `Facts:` statements. +Assume, there are properties `enrolledIn` and `offers`, such that `enrolled SubPropertyChain: +enrolledIn o inverse (offers)` holds. For the individuals `John`and `LeipzigUniversity` with the +assertions `John Facts: enrolledIn KnowledgeRepresentation` and `LeipzigUniversity Facts: offers +KnowledgeRepresentation`, the reasoner will infer `John Facts: enrolledIn LeipzigUniversity`. + + +#### Object property axiom generators +- **Object property equivalence (owl:equivalentProperty)** +The reasoner will infer assertions about the equivalence of object +properties, i.e. `EquivalentTo:` statements. +If there are object properties `hasAlternativeLecture` and `hasSameTopicAs`, such that +`hasAlternativeLecture Characteristics: Symmetric` and `hasSameTopicAs InverseOf: +hasAlternativeLecture` holds, the reasoner will infer `EquivalentProperties: hasAlternativeLecture, +hasSameTopicAs`. + + +- **Object property inversion (owl:inverseOf)** +The reasoner will infer axioms about the inversion about object +properties, i.e. `InverseOf:` statements. +If there is a object property `hasAlternativeLecture`, such that `hasAlternativeLecture +Characteristics: Symmetric` holds, the reasoner will infer `hasAlternativeLecture InverseOf: +hasAlternativeLecture`. + + +- **Object property inclusion (rdfs:subPropertyOf)** +The reasoner will infer axioms about the inclusion of object properties, +i.e. `SubPropertyOf:` statements. +If there are object properties `enrolledIn`, `studentOf` and `hasStudent`, such that `enrolledIn +SubPropertyOf: studentOf` and `enrolledIn InverseOf: hasStudent` holds, the reasoner will infer +`hasStudent SubPropertyOf: inverse (studentOf)`. + + +- **Object property ranges (rdfs:range)** +The reasoner will infer axioms about the ranges of object properties, +i.e. `Range:` statements. +If there are classes `Student` and `Lecture` as wells as object properties `hasStudent` and +`enrolledIn`, such that `hasStudent Range: Student and enrolledIn some Lecture` holds, the +reasoner will infer `hasStudent Range: Student`. + + +- **Object property domains (rdfs:domain)** +The reasoner will infer axioms about the domains of object +properties, i.e. `Domain:` statements. +If there are classes `Person`, `Student` and `Professor` as wells as the object property +`hasRoleIn`, such that `Professor SubClassOf: Person`, `Student SubClassOf: Person` and +`hasRoleIn Domain: Professor or Student` holds, the reasoner will infer `hasRoleIn Domain: Person`. + + +### Validate OWL2 profiles + +Validate the input ontology against OWL profiles (DL, EL, QL, RL, and Full) and annotate the result graph. + +### Process valid OWL profiles from input + +If enabled along with the "Validate OWL2 profiles" parameter, the valid profiles, ontology IRI and reasoner option is +taken from the config port input (parameters "valid_profiles", "ontology_graph_iri" and "reasoner") and the OWL2 +profiles validation is not done in the plugin. The valid profiles input is a comma-separated string (e.g. "Full,DL"). + +### Add ontology graph import to result graph + +Add the triple ` owl:imports ` to the output graph. + +### Add result graph import to ontology graph + +Add the triple ` owl:imports ` to the ontology graph + +### Maximum RAM Percentage + +Maximum heap size for the Java virtual machine in the DI container running the reasoning process. + +⚠️ Setting the percentage too high may result in an out of memory error. diff --git a/cmem_plugin_reason/validate_doc.md b/cmem_plugin_reason/doc/validate_doc.md similarity index 86% rename from cmem_plugin_reason/validate_doc.md rename to cmem_plugin_reason/doc/validate_doc.md index b21df69..3cc4ba3 100644 --- a/cmem_plugin_reason/validate_doc.md +++ b/cmem_plugin_reason/doc/validate_doc.md @@ -45,10 +45,15 @@ Raise an error if inconsistencies are found. If enabled, the plugin does not out Validate the input ontology against OWL profiles (DL, EL, QL, RL, and Full) and annotate the result graph. +### Mode +Mode _inconsistency_ generates an explanation for an inconsistent ontology. +Mode _unsatisfiability_ generates explanations for many unsatisfiable classes at once. + ### Output entities Output entities. The plugin outputs the explanation as text in Markdown format on the path "markdown", the ontology IRI -on the path "ontology_graph_iri", and, if enabled, the valid OWL2 profiles on the path "valid_profiles +on the path "ontology_graph_iri", the reasoner option on the path "reasoner", and, if enabled, the valid OWL2 profiles +on the path "valid_profiles". ### Maximum RAM Percentage diff --git a/cmem_plugin_reason/plugin_reason.py b/cmem_plugin_reason/plugin_reason.py index ef97a62..8b78971 100644 --- a/cmem_plugin_reason/plugin_reason.py +++ b/cmem_plugin_reason/plugin_reason.py @@ -10,7 +10,6 @@ from cmem.cmempy.dp.proxy.update import post from cmem_plugin_base.dataintegration.context import ExecutionContext, ExecutionReport from cmem_plugin_base.dataintegration.description import Icon, Plugin, PluginParameter -from cmem_plugin_base.dataintegration.parameter.choice import ChoiceParameterType from cmem_plugin_base.dataintegration.parameter.graph import GraphParameterType from cmem_plugin_base.dataintegration.plugins import WorkflowPlugin from cmem_plugin_base.dataintegration.ports import FixedNumberOfInputs @@ -18,11 +17,12 @@ from cmem_plugin_base.dataintegration.utils import setup_cmempy_user_access from inflection import underscore +from cmem_plugin_reason.doc import REASON_DOC from cmem_plugin_reason.utils import ( MAX_RAM_PERCENTAGE_DEFAULT, MAX_RAM_PERCENTAGE_PARAMETER, ONTOLOGY_GRAPH_IRI_PARAMETER, - REASON_DOC, + REASONER_PARAMETER, REASONERS, VALIDATE_PROFILES_PARAMETER, create_xml_catalog_file, @@ -35,6 +35,8 @@ validate_profiles, ) +REASONER_PARAMETER.description += " Select axiom generators below [Click (?) for documentation]." + @Plugin( label="Reason", @@ -44,27 +46,8 @@ parameters=[ ONTOLOGY_GRAPH_IRI_PARAMETER, VALIDATE_PROFILES_PARAMETER, + REASONER_PARAMETER, MAX_RAM_PERCENTAGE_PARAMETER, - PluginParameter( - param_type=GraphParameterType( - allow_only_autocompleted_values=False, - classes=[ - "https://vocab.eccenca.com/di/Dataset", - "http://rdfs.org/ns/void#Dataset", - "http://www.w3.org/2002/07/owl#Ontology", - ], - ), - name="output_graph_iri", - label="Output graph IRI", - description="""The IRI of the output graph for the inconsistency validation. ⚠️ Existing - graphs will be overwritten.""", - ), - PluginParameter( - param_type=ChoiceParameterType(REASONERS), - name="reasoner", - label="Reasoner", - description="Reasoner option. Additionally, select axiom generators below.", - ), PluginParameter( param_type=GraphParameterType( classes=[ @@ -78,101 +61,111 @@ description="The IRI of the input data graph.", ), PluginParameter( - param_type=BoolParameterType(), - name="sub_class", - label="SubClass", - description="", - default_value=True, + param_type=GraphParameterType( + allow_only_autocompleted_values=False, + classes=["http://www.w3.org/2002/07/owl#Ontology"], + ), + name="output_graph_iri", + label="Output graph IRI", + description="""The IRI of the output graph for the reasoning result. ⚠️ Existing graphs + will be overwritten.""", ), PluginParameter( param_type=BoolParameterType(), - name="equivalent_class", - label="EquivalentClass", - description="", + name="sub_class", + label="Class inclusion (rdfs:subClassOf)", + # description=SUBCLASS_DESC, default_value=False, ), PluginParameter( param_type=BoolParameterType(), - name="disjoint_classes", - label="DisjointClasses", - description="", + name="equivalent_class", + label="Class equivalence (owl:equivalentClass)", + # description=EQUIVALENCE_DESC, default_value=False, ), PluginParameter( param_type=BoolParameterType(), - name="data_property_characteristic", - label="DataPropertyCharacteristic", - description="", + name="disjoint_classes", + label="Class disjointness (owl:disjointWith)", + # description=DISJOINT_DESC, default_value=False, ), + # PluginParameter( + # param_type=BoolParameterType(), + # name="data_property_characteristic", + # label="Data property characteristics (Axiomatic triples)", + # description=DATA_PROP_CHAR_DESC, + # default_value=False, + # ), PluginParameter( param_type=BoolParameterType(), name="equivalent_data_properties", - label="EquivalentDataProperties", - description="", + label="Data property equivalence (owl:equivalentProperty)", + # description=DATA_PROP_EQUIV_DESC, default_value=False, ), PluginParameter( param_type=BoolParameterType(), name="sub_data_property", - label="SubDataProperty", - description="", + label="Data property inclusion (rdfs:subPropertyOf)", + # description=DATA_PROP_SUB_DESC, default_value=False, ), PluginParameter( param_type=BoolParameterType(), name="class_assertion", - label="ClassAssertion", - description="", - default_value=False, + label="Individual class assertions (rdf:type)", + # description=CLASS_ASSERT_DESC, + default_value=True, ), PluginParameter( param_type=BoolParameterType(), name="property_assertion", - label="PropertyAssertion", - description="", - default_value=False, + label="Individual property assertions", + # description=PROPERTY_ASSERT_DESC, + default_value=True, ), PluginParameter( param_type=BoolParameterType(), name="equivalent_object_property", - label="EquivalentObjectProperty", - description="", + label="Object property equivalence (owl:equivalentProperty)", + # description=OBJECT_PROP_EQUIV_DESC, default_value=False, ), PluginParameter( param_type=BoolParameterType(), name="inverse_object_properties", - label="InverseObjectProperties", - description="", - default_value=False, - ), - PluginParameter( - param_type=BoolParameterType(), - name="object_property_characteristic", - label="ObjectPropertyCharacteristic", - description="", + label="Object property inversion (owl:inverseOf)", + # description=OBJECT_PROP_INV_DESC, default_value=False, ), + # PluginParameter( + # param_type=BoolParameterType(), + # name="object_property_characteristic", + # label="Object property characteristic (Axiomatic triples)", + # description=OBJECT_PROP_CHAR_DESC, + # default_value=False, + # ), PluginParameter( param_type=BoolParameterType(), name="sub_object_property", - label="SubObjectProperty", - description="", + label="Object property inclusion (rdfs:subPropertyOf)", + # description=OBJECT_PROP_SUB_DESC, default_value=False, ), PluginParameter( param_type=BoolParameterType(), name="object_property_range", - label="ObjectPropertyRange", - description="", + label="Object property ranges (rdfs:range)", + # description=OBJECT_PROP_RANGE_DESC, default_value=False, ), PluginParameter( param_type=BoolParameterType(), name="object_property_domain", - label="ObjectPropertyDomain", - description="", + label="Object property domains (rdfs:domain)", + # description=OBJECT_PROP_DOMAIN_DESC, default_value=False, ), PluginParameter( @@ -180,9 +173,10 @@ name="input_profiles", label="Process valid OWL profiles from input", description="""If enabled along with the "Validate OWL2 profiles" parameter, the valid - profiles and ontology IRI is taken from the config port input (parameters - "valid_profiles" and "ontology_graph_iri") instead of from running the validation in the - plugin. The valid profiles input is a comma-separated string (e.g. "Full,DL").""", + profiles, ontology IRI and reasoner option is taken from the config port input + (parameters "valid_profiles", "ontology_graph_iri" and "reasoner") and the OWL2 profiles + validation is not done in the plugin. The valid profiles input is a comma-separated + string (e.g. "Full,DL").""", default_value=False, advanced=True, ), @@ -221,20 +215,23 @@ def __init__( # noqa: PLR0913 C901 ontology_graph_iri: str, output_graph_iri: str, reasoner: str, - class_assertion: bool = False, - data_property_characteristic: bool = False, - disjoint_classes: bool = False, + class_assertion: bool = True, + property_assertion: bool = True, + sub_class: bool = False, equivalent_class: bool = False, - equivalent_data_properties: bool = False, + disjoint_classes: bool = False, + sub_object_property: bool = False, equivalent_object_property: bool = False, - inverse_object_properties: bool = False, - object_property_characteristic: bool = False, + # This axiom generator does not yield any results. + # Issue: https://github.com/eccenca/cmem-plugin-reason/issues/10 + # object_property_characteristic: bool = False, object_property_domain: bool = False, object_property_range: bool = False, - property_assertion: bool = False, - sub_class: bool = True, + inverse_object_properties: bool = False, sub_data_property: bool = False, - sub_object_property: bool = False, + equivalent_data_properties: bool = False, + # Removed because the object property counterpart does not work + # data_property_characteristic: bool = False, validate_profile: bool = False, import_ontology: bool = True, import_result: bool = False, @@ -246,14 +243,14 @@ def __init__( # noqa: PLR0913 C901 "SubClass": sub_class, "EquivalentClass": equivalent_class, "DisjointClasses": disjoint_classes, - "DataPropertyCharacteristic": data_property_characteristic, + # "DataPropertyCharacteristic": data_property_characteristic, "EquivalentDataProperties": equivalent_data_properties, "SubDataProperty": sub_data_property, "ClassAssertion": class_assertion, "PropertyAssertion": property_assertion, "EquivalentObjectProperty": equivalent_object_property, "InverseObjectProperties": inverse_object_properties, - "ObjectPropertyCharacteristic": object_property_characteristic, + # "ObjectPropertyCharacteristic": object_property_characteristic, "SubObjectProperty": sub_object_property, "ObjectPropertyRange": object_property_range, "ObjectPropertyDomain": object_property_domain, @@ -419,7 +416,7 @@ def _execute(self, context: ExecutionContext) -> None: ) def execute(self, inputs: None, context: ExecutionContext) -> None: # noqa: ARG002 - """Validate input, execute plugin with temporary directory""" + """Execute plugin with temporary directory""" context.report.update( ExecutionReport( operation="reason", diff --git a/cmem_plugin_reason/plugin_validate.py b/cmem_plugin_reason/plugin_validate.py index 5f3dff6..6a71a67 100644 --- a/cmem_plugin_reason/plugin_validate.py +++ b/cmem_plugin_reason/plugin_validate.py @@ -1,5 +1,6 @@ """Ontology consistency validation workflow plugin module""" +from collections import OrderedDict from datetime import UTC, datetime from pathlib import Path from tempfile import TemporaryDirectory @@ -19,12 +20,13 @@ from cmem_plugin_base.dataintegration.utils import setup_cmempy_user_access from pathvalidate import is_valid_filename +from cmem_plugin_reason.doc import VALIDATE_DOC from cmem_plugin_reason.utils import ( MAX_RAM_PERCENTAGE_DEFAULT, MAX_RAM_PERCENTAGE_PARAMETER, ONTOLOGY_GRAPH_IRI_PARAMETER, + REASONER_PARAMETER, REASONERS, - VALIDATE_DOC, VALIDATE_PROFILES_PARAMETER, create_xml_catalog_file, get_graphs_tree, @@ -46,6 +48,15 @@ ONTOLOGY_GRAPH_IRI_PARAMETER, MAX_RAM_PERCENTAGE_PARAMETER, VALIDATE_PROFILES_PARAMETER, + REASONER_PARAMETER, + PluginParameter( + param_type=StringParameterType(), + name="md_filename", + label="Output filename", + description="The filename of the Markdown file with the explanation of " + "inconsistencies.⚠️ Existing files will be overwritten.", + default_value="", + ), PluginParameter( param_type=GraphParameterType( allow_only_autocompleted_values=False, @@ -61,20 +72,6 @@ graphs will be overwritten.""", default_value="", ), - PluginParameter( - param_type=ChoiceParameterType(REASONERS), - name="reasoner", - label="Reasoner", - description="Reasoner option.", - ), - PluginParameter( - param_type=StringParameterType(), - name="md_filename", - label="Output filename", - description="The filename of the Markdown file with the explanation of " - "inconsistencies.⚠️ Existing files will be overwritten.", - default_value="", - ), PluginParameter( param_type=BoolParameterType(), name="stop_at_inconsistencies", @@ -88,21 +85,39 @@ name="output_entities", label="Output entities", description="""Output entities. The plugin outputs the explanation as text in Markdown - format on the path "markdown", the ontology IRI on the path "ontology_graph_iri", and, - if enabled, the valid OWL2 profiles on the path "valid_profiles".""", + format on the path "markdown", the ontology IRI on the path "ontology_graph_iri", the + reasoner option on the path "reasoner", and, if enabled, the valid OWL2 profiles on the + path "valid_profiles".""", default_value=False, ), + PluginParameter( + param_type=ChoiceParameterType( + OrderedDict( + { + "inconsistency": "inconsistency", + "unsatisfiability": "unsatisfiability", + } + ) + ), + name="mode", + label="Mode", + description="""Mode "inconsistency" generates an explanation for an inconsistent + ontology. Mode "unsatisfiability" generates explanations for many unsatisfiable classes + at once.""", + default_value="inconsistency", + ), ], ) class ValidatePlugin(WorkflowPlugin): """Validate plugin""" - def __init__( # noqa: PLR0913 C901 + def __init__( # noqa: PLR0912 PLR0913 C901 self, ontology_graph_iri: str, reasoner: str, output_graph_iri: str = "", md_filename: str = "", + mode: str = "inconsistency", validate_profile: bool = False, output_entities: bool = False, stop_at_inconsistencies: bool = False, @@ -121,6 +136,8 @@ def __init__( # noqa: PLR0913 C901 errors += 'Invalid filename for parameter "Output filename". ' if not output_graph_iri and not md_filename and not output_entities: errors += "No output selected. " + if mode not in ("inconsistency", "unsatisfiability"): + errors += 'Invalid value for parameter "Mode". ' if max_ram_percentage not in range(1, 101): errors += 'Invalid value for parameter "Maximum RAM Percentage". ' if errors: @@ -128,6 +145,7 @@ def __init__( # noqa: PLR0913 C901 self.ontology_graph_iri = ontology_graph_iri self.reasoner = reasoner self.output_graph_iri = output_graph_iri + self.mode = mode self.stop_at_inconsistencies = stop_at_inconsistencies if md_filename: self.md_filename = md_filename @@ -148,7 +166,7 @@ def __init__( # noqa: PLR0913 C901 def generate_output_schema(self) -> EntitySchema | None: """Generate output entity schema.""" - paths = [EntityPath("markdown"), EntityPath("ontology_graph_iri")] + paths = [EntityPath("markdown"), EntityPath("ontology_graph_iri"), EntityPath("reasoner")] if self.validate_profile: paths.append(EntityPath("valid_profiles")) return EntitySchema(type_uri="validate", paths=paths) @@ -167,7 +185,7 @@ def explain(self, graphs: dict) -> None: utctime = str(datetime.fromtimestamp(int(time()), tz=UTC))[:-6].replace(" ", "T") + "Z" cmd = ( f'explain --input "{data_location}" ' - f"--reasoner {self.reasoner} -M inconsistency " + f"--reasoner {self.reasoner} -M {self.mode} " f'--explanation "{self.temp}/{self.md_filename}"' ) if self.output_graph_iri: @@ -210,7 +228,7 @@ def add_profiles(self, valid_profiles: list) -> list: def make_entities(self, text: str, valid_profiles: list) -> Entities: """Make entities""" - values = [[text], [self.ontology_graph_iri]] + values = [[text], [self.ontology_graph_iri], [self.reasoner]] if self.validate_profile: values.append([",".join(valid_profiles)]) entities = [ @@ -269,7 +287,7 @@ def _execute(self, context: ExecutionContext) -> Entities | None: return None def execute(self, inputs: None, context: ExecutionContext) -> Entities | None: # noqa: ARG002 - """Remove temp files on error""" + """Execute plugin with temporary directory""" context.report.update( ExecutionReport( operation="validate", diff --git a/cmem_plugin_reason/reason_doc.md b/cmem_plugin_reason/reason_doc.md deleted file mode 100644 index f6a7b09..0000000 --- a/cmem_plugin_reason/reason_doc.md +++ /dev/null @@ -1,79 +0,0 @@ -A task performing OWL reasoning. With an OWL ontology and a data graph as input the reasoning result is written to a specified graph. - -## Options - -### Data graph IRI - -The IRI of the input data graph. The graph IRI is selected from a list of graphs of types `di:Dataset`, `void:Dataset` -and `owl:Ontology`. - -### Ontology graph IRI - -The IRI of the input ontology graph. The graph IRI is selected from a list of graphs of type`owl:Ontology`. - -### Result graph IRI - -The IRI of the output graph for the reasoning result. - -⚠️ Existing graphs will be overwritten. - -### Reasoner - -The following reasoner options are supported: -- [ELK](https://code.google.com/p/elk-reasoner/) (elk) -- [Expression Materializing Reasoner](http://static.javadoc.io/org.geneontology/expression-materializing-reasoner/0.1.3/org/geneontology/reasoner/ExpressionMaterializingReasoner.html) (emr) -- [HermiT](http://www.hermit-reasoner.com/) (hermit) -- [JFact](http://jfact.sourceforge.net/) (jfact) -- [Structural Reasoner](http://owlcs.github.io/owlapi/apidocs_4/org/semanticweb/owlapi/reasoner/structural/StructuralReasoner.html) (structural) -- [Whelk](https://github.com/balhoff/whelk) (whelk) - -### Generated Axioms - -By default, the reason operation will only assert inferred subclass axioms. The plugin provides the following -parameters to include inferred axiom generators: - -#### Class axiom generators -- SubClass -- EquivalentClass -- DisjointClasses - -#### Data property axiom generators -- DataPropertyCharacteristic -- EquivalentDataProperties -- SubDataProperty - -#### Individual axiom generators -- ClassAssertion -- PropertyAssertion - -#### Object property axiom generators -- EquivalentObjectProperty -- InverseObjectProperties -- ObjectPropertyCharacteristic -- SubObjectProperty -- ObjectPropertyRange -- ObjectPropertyDomain - -### Validate OWL2 profiles - -Validate the input ontology against OWL profiles (DL, EL, QL, RL, and Full) and annotate the result graph. - -### Process valid OWL profiles from input - -If enabled along with the "Validate OWL2 profiles" parameter, the valid profiles and ontology IRI is taken from the -config port input (parameters "valid_profiles" and "ontology_graph_iri") instead of from running the validation in the -plugin. The valid profiles input is a comma-separated string (e.g. "Full,DL"). - -### Add ontology graph import to result graph - -Add the triple ` owl:imports ` to the output graph. - -### Add result graph import to ontology graph - -Add the triple ` owl:imports ` to the ontology graph - -### Maximum RAM Percentage - -Maximum heap size for the Java virtual machine in the DI container running the reasoning process. - -⚠️ Setting the percentage too high may result in an out of memory error. diff --git a/cmem_plugin_reason/bin/robot.jar b/cmem_plugin_reason/robot.jar similarity index 100% rename from cmem_plugin_reason/bin/robot.jar rename to cmem_plugin_reason/robot.jar diff --git a/cmem_plugin_reason/utils.py b/cmem_plugin_reason/utils.py index 9eeafa8..1e12d5f 100644 --- a/cmem_plugin_reason/utils.py +++ b/cmem_plugin_reason/utils.py @@ -13,6 +13,7 @@ from cmem.cmempy.dp.proxy.update import post as post_update from cmem_plugin_base.dataintegration.context import ExecutionContext from cmem_plugin_base.dataintegration.description import PluginParameter +from cmem_plugin_base.dataintegration.parameter.choice import ChoiceParameterType from cmem_plugin_base.dataintegration.parameter.graph import GraphParameterType from cmem_plugin_base.dataintegration.plugins import WorkflowPlugin from cmem_plugin_base.dataintegration.types import BoolParameterType, IntParameterType @@ -20,11 +21,7 @@ from . import __path__ -with (Path(__path__[0]) / "reason_doc.md").open("r") as f: - REASON_DOC = f.read() - -with (Path(__path__[0]) / "validate_doc.md").open("r") as f: - VALIDATE_DOC = f.read() +ROBOT = Path(__path__[0]) / "robot.jar" REASONERS = OrderedDict( { @@ -39,6 +36,14 @@ MAX_RAM_PERCENTAGE_DEFAULT = 20 + +REASONER_PARAMETER = PluginParameter( + param_type=ChoiceParameterType(REASONERS), + name="reasoner", + label="Reasoner", + description="Reasoner option.", +) + ONTOLOGY_GRAPH_IRI_PARAMETER = PluginParameter( param_type=GraphParameterType(classes=["http://www.w3.org/2002/07/owl#Ontology"]), name="ontology_graph_iri", @@ -84,7 +89,7 @@ def create_xml_catalog_file(dir_: str, graphs: dict) -> None: def get_graphs_tree(graph_iris: tuple) -> dict: - """Get graph import tree. Last item in tuple is output_graph_iri which is excluded""" + """Get graph import tree. Last item in graph_iris is output_graph_iris which is excluded""" graphs = {} for graph_iri in graph_iris[:-1]: if graph_iri not in graphs: @@ -194,8 +199,7 @@ def get_provenance(plugin: WorkflowPlugin, context: ExecutionContext) -> dict | def robot(cmd: str, max_ram_percentage: int) -> CompletedProcess: """Run robot.jar""" - jar = Path(__path__[0]) / "bin" / "robot.jar" - cmd = f"java -XX:MaxRAMPercentage={max_ram_percentage} -jar {jar} {cmd}" + cmd = f"java -XX:MaxRAMPercentage={max_ram_percentage} -jar {ROBOT} {cmd}" return run(shlex.split(cmd), check=False, capture_output=True) # noqa: S603 @@ -205,7 +209,7 @@ def validate_profiles(plugin: WorkflowPlugin, graphs: dict) -> list: valid_profiles = [] for profile in ("Full", "DL", "EL", "QL", "RL"): plugin.log.info(f"Validating {profile} profile.") - cmd = f"validate-profile --profile {profile} --input {ontology_location}" + cmd = f"merge --input {ontology_location} validate-profile --profile {profile}" response = robot(cmd, plugin.max_ram_percentage) if response.stdout.endswith(b"[Ontology and imports closure in profile]\n\n"): valid_profiles.append(profile) diff --git a/poetry.lock b/poetry.lock index 4669b19..7b89598 100644 --- a/poetry.lock +++ b/poetry.lock @@ -307,13 +307,13 @@ tests = ["defusedxml"] [[package]] name = "idna" -version = "3.7" +version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, ] [[package]] @@ -574,38 +574,38 @@ test = ["Cython", "greenlet", "ipython", "pytest", "pytest-cov", "pytest-textual [[package]] name = "mypy" -version = "1.11.1" +version = "1.11.2" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, - {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, - {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, - {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, - {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, - {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, - {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, - {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, - {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, - {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, - {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, - {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, - {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, - {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, - {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, - {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, - {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, - {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, - {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, - {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, - {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, - {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, - {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d42a6dd818ffce7be66cce644f1dff482f1d97c53ca70908dff0b9ddc120b77a"}, + {file = "mypy-1.11.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:801780c56d1cdb896eacd5619a83e427ce436d86a3bdf9112527f24a66618fef"}, + {file = "mypy-1.11.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41ea707d036a5307ac674ea172875f40c9d55c5394f888b168033177fce47383"}, + {file = "mypy-1.11.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6e658bd2d20565ea86da7d91331b0eed6d2eee22dc031579e6297f3e12c758c8"}, + {file = "mypy-1.11.2-cp310-cp310-win_amd64.whl", hash = "sha256:478db5f5036817fe45adb7332d927daa62417159d49783041338921dcf646fc7"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75746e06d5fa1e91bfd5432448d00d34593b52e7e91a187d981d08d1f33d4385"}, + {file = "mypy-1.11.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a976775ab2256aadc6add633d44f100a2517d2388906ec4f13231fafbb0eccca"}, + {file = "mypy-1.11.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd953f221ac1379050a8a646585a29574488974f79d8082cedef62744f0a0104"}, + {file = "mypy-1.11.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:57555a7715c0a34421013144a33d280e73c08df70f3a18a552938587ce9274f4"}, + {file = "mypy-1.11.2-cp311-cp311-win_amd64.whl", hash = "sha256:36383a4fcbad95f2657642a07ba22ff797de26277158f1cc7bd234821468b1b6"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8960dbbbf36906c5c0b7f4fbf2f0c7ffb20f4898e6a879fcf56a41a08b0d318"}, + {file = "mypy-1.11.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:06d26c277962f3fb50e13044674aa10553981ae514288cb7d0a738f495550b36"}, + {file = "mypy-1.11.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e7184632d89d677973a14d00ae4d03214c8bc301ceefcdaf5c474866814c987"}, + {file = "mypy-1.11.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3a66169b92452f72117e2da3a576087025449018afc2d8e9bfe5ffab865709ca"}, + {file = "mypy-1.11.2-cp312-cp312-win_amd64.whl", hash = "sha256:969ea3ef09617aff826885a22ece0ddef69d95852cdad2f60c8bb06bf1f71f70"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37c7fa6121c1cdfcaac97ce3d3b5588e847aa79b580c1e922bb5d5d2902df19b"}, + {file = "mypy-1.11.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a8a53bc3ffbd161b5b2a4fff2f0f1e23a33b0168f1c0778ec70e1a3d66deb86"}, + {file = "mypy-1.11.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ff93107f01968ed834f4256bc1fc4475e2fecf6c661260066a985b52741ddce"}, + {file = "mypy-1.11.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:edb91dded4df17eae4537668b23f0ff6baf3707683734b6a818d5b9d0c0c31a1"}, + {file = "mypy-1.11.2-cp38-cp38-win_amd64.whl", hash = "sha256:ee23de8530d99b6db0573c4ef4bd8f39a2a6f9b60655bf7a1357e585a3486f2b"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:801ca29f43d5acce85f8e999b1e431fb479cb02d0e11deb7d2abb56bdaf24fd6"}, + {file = "mypy-1.11.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af8d155170fcf87a2afb55b35dc1a0ac21df4431e7d96717621962e4b9192e70"}, + {file = "mypy-1.11.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7821776e5c4286b6a13138cc935e2e9b6fde05e081bdebf5cdb2bb97c9df81d"}, + {file = "mypy-1.11.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539c570477a96a4e6fb718b8d5c3e0c0eba1f485df13f86d2970c91f0673148d"}, + {file = "mypy-1.11.2-cp39-cp39-win_amd64.whl", hash = "sha256:3f14cd3d386ac4d05c5a39a51b84387403dadbd936e17cb35882134d4f8f0d24"}, + {file = "mypy-1.11.2-py3-none-any.whl", hash = "sha256:b499bc07dbdcd3de92b0a8b29fdf592c111276f6a12fe29c30f6c417dd546d12"}, + {file = "mypy-1.11.2.tar.gz", hash = "sha256:7f9993ad3e0ffdc95c2a14b66dee63729f021968bff8ad911867579c65d13a79"}, ] [package.dependencies] @@ -642,18 +642,19 @@ files = [ [[package]] name = "pathvalidate" -version = "3.2.0" +version = "3.2.1" description = "pathvalidate is a Python library to sanitize/validate a string such as filenames/file-paths/etc." optional = false python-versions = ">=3.7" files = [ - {file = "pathvalidate-3.2.0-py3-none-any.whl", hash = "sha256:cc593caa6299b22b37f228148257997e2fa850eea2daf7e4cc9205cef6908dee"}, - {file = "pathvalidate-3.2.0.tar.gz", hash = "sha256:5e8378cf6712bff67fbe7a8307d99fa8c1a0cb28aa477056f8fc374f0dff24ad"}, + {file = "pathvalidate-3.2.1-py3-none-any.whl", hash = "sha256:9a6255eb8f63c9e2135b9be97a5ce08f10230128c4ae7b3e935378b82b22c4c9"}, + {file = "pathvalidate-3.2.1.tar.gz", hash = "sha256:f5d07b1e2374187040612a1fcd2bcb2919f8db180df254c9581bb90bf903377d"}, ] [package.extras] docs = ["Sphinx (>=2.4)", "sphinx-rtd-theme (>=1.2.2)", "urllib3 (<2)"] -test = ["Faker (>=1.0.8)", "allpairspy (>=2)", "click (>=6.2)", "pytest (>=6.0.1)", "pytest-discord (>=0.1.4)", "pytest-md-report (>=0.4.1)"] +readme = ["path (>=13,<17)", "readmemaker (>=1.1.0)"] +test = ["Faker (>=1.0.8)", "allpairspy (>=2)", "click (>=6.2)", "pytest (>=6.0.1)", "pytest-md-report (>=0.6.2)"] [[package]] name = "pillow" @@ -794,13 +795,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyparsing" -version = "3.1.2" +version = "3.1.4" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.6.8" files = [ - {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, - {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, + {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, + {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, ] [package.extras] @@ -1002,13 +1003,13 @@ requests = ">=2.0.1,<3.0.0" [[package]] name = "rich" -version = "13.7.1" +version = "13.8.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, + {file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"}, + {file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"}, ] [package.dependencies] @@ -1064,19 +1065,23 @@ setuptools = "*" [[package]] name = "setuptools" -version = "72.2.0" +version = "74.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-72.2.0-py3-none-any.whl", hash = "sha256:f11dd94b7bae3a156a95ec151f24e4637fb4fa19c878e4d191bfb8b2d82728c4"}, - {file = "setuptools-72.2.0.tar.gz", hash = "sha256:80aacbf633704e9c8bfa1d99fa5dd4dc59573efcf9e4042c13d3bcef91ac2ef9"}, + {file = "setuptools-74.0.0-py3-none-any.whl", hash = "sha256:0274581a0037b638b9fc1c6883cc71c0210865aaa76073f7882376b641b84e8f"}, + {file = "setuptools-74.0.0.tar.gz", hash = "sha256:a85e96b8be2b906f3e3e789adec6a9323abf79758ecfa3065bd740d81158b11e"}, ] [package.extras] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] [[package]] name = "six" @@ -1091,13 +1096,13 @@ files = [ [[package]] name = "textual" -version = "0.76.0" +version = "0.78.0" description = "Modern Text User Interface framework" optional = false python-versions = "<4.0.0,>=3.8.1" files = [ - {file = "textual-0.76.0-py3-none-any.whl", hash = "sha256:e2035609c889dba507d34a5d7b333f1c8c53a29fb170962cb92101507663517a"}, - {file = "textual-0.76.0.tar.gz", hash = "sha256:b12e8879d591090c0901b5cb8121d086e28e677353b368292d3865ec99b83b70"}, + {file = "textual-0.78.0-py3-none-any.whl", hash = "sha256:c9d3c7dc467c37ee2e54a0283ac2c85dac35e4fc949518ed054a65b8e3e9b822"}, + {file = "textual-0.78.0.tar.gz", hash = "sha256:421f508b0d41ea0b8ecf273bf83f0d19376667eb0a87f70575252395d90ab315"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index 42d9f3b..6f58d99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,5 +96,6 @@ ignore = [ "PD", # opinionated linting for pandas code "S101", # use of assert detected "TRY003", # Avoid specifying long messages outside the exception class + "ERA001", # Found commented-out code ] diff --git a/tests/test_reason.py b/tests/test_reason.py index e6d48de..09f9375 100644 --- a/tests/test_reason.py +++ b/tests/test_reason.py @@ -7,6 +7,7 @@ from cmem.cmempy.dp.proxy.graph import delete, get, post from cmem.cmempy.workspace.projects.project import delete_project, make_new_project from cmem.cmempy.workspace.projects.resources.resource import get_resource +from cmem_plugin_base.dataintegration.entity import Entities from rdflib import DCTERMS, OWL, RDF, RDFS, Graph, URIRef from rdflib.compare import to_isomorphic @@ -19,39 +20,60 @@ UID = "e02aaed014c94e0c91bf960fed127750" REASON_DATA_GRAPH_IRI = f"https://ns.eccenca.com/reasoning/{UID}/data/" -REASON_ONTOLOGY_GRAPH_IRI = f"https://ns.eccenca.com/reasoning/{UID}/vocab/" +REASON_ONTOLOGY_GRAPH_IRI_1 = f"https://ns.eccenca.com/reasoning/{UID}/vocab/" +REASON_ONTOLOGY_GRAPH_IRI_2 = f"https://ns.eccenca.com/reasoning/{UID}/vocab2/" +REASON_ONTOLOGY_GRAPH_IRI_3 = f"https://ns.eccenca.com/reasoning/{UID}/vocab3/" REASON_RESULT_GRAPH_IRI = f"https://ns.eccenca.com/reasoning/{UID}/result/" -VALIDATE_ONTOLOGY_GRAPH_IRI = f"https://ns.eccenca.com/validateontology/{UID}/vocab/" +VALIDATE_ONTOLOGY_GRAPH_IRI_1 = f"https://ns.eccenca.com/validateontology/{UID}/vocab/" +VALIDATE_ONTOLOGY_GRAPH_IRI_2 = f"https://ns.eccenca.com/validateontology/{UID}/vocab2/" +VALIDATE_ONTOLOGY_GRAPH_IRI_3 = f"https://ns.eccenca.com/validateontology/{UID}/vocab3/" OUTPUT_GRAPH_IRI = f"https://ns.eccenca.com/validateontology/{UID}/output/" MD_FILENAME = f"{UID}.md" PROJECT_ID = f"validate_plugin_test_project_{UID}" +def get_value_dict(entities: Entities) -> dict: + """Make result path to value map""" + value_dict = {} + paths = [p.path for p in entities.schema.paths] + for p in paths: + value_dict[p] = next(iter(entities.entities)).values[paths.index(p)][0] # type: ignore[union-attr] + return value_dict + + +def import_graph(iri: str, filename: str) -> None: + """Import graph to CMEM""" + res = post(iri, Path(__path__[0]) / filename, replace=True) + if res.status_code != 204: # noqa: PLR2004 + raise ValueError(f"Response {res.status_code}: {res.url}") + + @pytest.fixture() def _setup(request: pytest.FixtureRequest) -> None: """Set up""" - res = post(REASON_DATA_GRAPH_IRI, Path(__path__[0]) / "dataset_owl.ttl", replace=True) - if res.status_code != 204: # noqa: PLR2004 - raise ValueError(f"Response {res.status_code}: {res.url}") - res = post(REASON_ONTOLOGY_GRAPH_IRI, Path(__path__[0]) / "vocab.ttl", replace=True) - if res.status_code != 204: # noqa: PLR2004 - raise ValueError(f"Response {res.status_code}: {res.url}") + import_graph(REASON_DATA_GRAPH_IRI, "dataset_owl.ttl") + import_graph(REASON_ONTOLOGY_GRAPH_IRI_1, "test_reason_ontology_1.ttl") + import_graph(REASON_ONTOLOGY_GRAPH_IRI_2, "test_reason_ontology_2.ttl") + import_graph(REASON_ONTOLOGY_GRAPH_IRI_3, "test_reason_ontology_3.ttl") with suppress(Exception): delete_project(PROJECT_ID) make_new_project(PROJECT_ID) - res = post( - VALIDATE_ONTOLOGY_GRAPH_IRI, Path(__path__[0]) / "test_validate_ontology.ttl", replace=True - ) - if res.status_code != 204: # noqa: PLR2004 - raise ValueError(f"Response {res.status_code}: {res.url}") + + import_graph(VALIDATE_ONTOLOGY_GRAPH_IRI_1, "test_validate_ontology_1.ttl") + import_graph(VALIDATE_ONTOLOGY_GRAPH_IRI_2, "test_validate_ontology_2.ttl") + import_graph(VALIDATE_ONTOLOGY_GRAPH_IRI_3, "test_validate_ontology_3.ttl") request.addfinalizer(lambda: delete(REASON_DATA_GRAPH_IRI)) - request.addfinalizer(lambda: delete(REASON_ONTOLOGY_GRAPH_IRI)) + request.addfinalizer(lambda: delete(REASON_ONTOLOGY_GRAPH_IRI_1)) + request.addfinalizer(lambda: delete(REASON_ONTOLOGY_GRAPH_IRI_2)) + request.addfinalizer(lambda: delete(REASON_ONTOLOGY_GRAPH_IRI_3)) request.addfinalizer(lambda: delete(REASON_RESULT_GRAPH_IRI)) request.addfinalizer(lambda: delete_project(PROJECT_ID)) request.addfinalizer(lambda: delete(OUTPUT_GRAPH_IRI)) - request.addfinalizer(lambda: delete(VALIDATE_ONTOLOGY_GRAPH_IRI)) # noqa: PT021 + request.addfinalizer(lambda: delete(VALIDATE_ONTOLOGY_GRAPH_IRI_1)) + request.addfinalizer(lambda: delete(VALIDATE_ONTOLOGY_GRAPH_IRI_2)) + request.addfinalizer(lambda: delete(VALIDATE_ONTOLOGY_GRAPH_IRI_3)) # noqa: PT021 @needs_cmem @@ -71,7 +93,7 @@ def get_remote_graph(iri: str) -> Graph: def test_reasoner(reasoner: str, err_list: list) -> list: ReasonPlugin( data_graph_iri=REASON_DATA_GRAPH_IRI, - ontology_graph_iri=REASON_ONTOLOGY_GRAPH_IRI, + ontology_graph_iri=REASON_ONTOLOGY_GRAPH_IRI_1, output_graph_iri=REASON_RESULT_GRAPH_IRI, reasoner=reasoner, sub_class=False, @@ -89,38 +111,33 @@ def test_reasoner(reasoner: str, err_list: list) -> list: def test_validate(errors: str) -> str: result = ValidatePlugin( - ontology_graph_iri=VALIDATE_ONTOLOGY_GRAPH_IRI, + ontology_graph_iri=VALIDATE_ONTOLOGY_GRAPH_IRI_1, output_graph_iri=OUTPUT_GRAPH_IRI, reasoner="elk", validate_profile=True, md_filename=MD_FILENAME, output_entities=True, + mode="inconsistency", ).execute(None, context=TestExecutionContext(PROJECT_ID)) md_test = (Path(__path__[0]) / "test_validate.md").read_text() - paths = [p.path for p in result.schema.paths] # type: ignore[union-attr] + value_dict = get_value_dict(result) + output_graph = get_remote_graph(OUTPUT_GRAPH_IRI) + test = Graph().parse(Path(__path__[0]) / "test_validate_output.ttl", format="turtle") val_errors = "" - if next(iter(result.entities)).values[paths.index("markdown")][0] != md_test: # type: ignore[union-attr] + if value_dict["markdown"] != md_test: val_errors += 'EntityPath "markdown" output error. ' - if ( - next(iter(result.entities)).values[paths.index("ontology_graph_iri")][0] # type: ignore[union-attr] - != VALIDATE_ONTOLOGY_GRAPH_IRI - ): + if value_dict["ontology_graph_iri"] != VALIDATE_ONTOLOGY_GRAPH_IRI_1: val_errors += 'EntityPath "ontology_graph_iri" output error. ' - if ( - next(iter(result.entities)).values[paths.index("valid_profiles")][0] # type: ignore[union-attr] - != "Full,DL,EL,QL,RL" - ): + if value_dict["reasoner"] != "elk": + val_errors += 'EntityPath "reasoner" output error. ' + if value_dict["valid_profiles"] != "Full,DL,EL,QL,RL": val_errors += 'EntityPath "valid_profiles" output error. ' if md_test != get_resource(PROJECT_ID, MD_FILENAME).decode(): val_errors += "Markdown file error. " - - output_graph = get_remote_graph(OUTPUT_GRAPH_IRI) - test = Graph().parse(Path(__path__[0]) / "test_validate_output.ttl", format="turtle") if to_isomorphic(output_graph) != to_isomorphic(test): val_errors += "Output graph error. " - if val_errors: errors += "Validate: " + val_errors return errors diff --git a/tests/vocab.ttl b/tests/test_reason_ontology_1.ttl similarity index 96% rename from tests/vocab.ttl rename to tests/test_reason_ontology_1.ttl index f2e778e..407257d 100644 --- a/tests/vocab.ttl +++ b/tests/test_reason_ontology_1.ttl @@ -9,7 +9,8 @@ rdf:type owl:Ontology ; rdfs:comment "Vocabulary for a reasoning use case"@en ; rdfs:label "Eccenca Reasoning Vocabulary"@en ; - rdfs:seeAlso . + rdfs:seeAlso ; + owl:imports . ################################################################# # Object Properties @@ -141,16 +142,6 @@ owl:Thing rdf:type owl:Class . rdfs:label "adult"^^xsd:string . -### https://ns.eccenca.com/reasoning/e02aaed014c94e0c91bf960fed127750/vocab/animal -:animal rdf:type owl:Class ; - rdfs:subClassOf [ rdf:type owl:Restriction ; - owl:onProperty :eats ; - owl:someValuesFrom owl:Thing - ] ; - rdfs:comment ""^^xsd:string ; - rdfs:isDefinedBy ; - rdfs:label "animal"^^xsd:string . - ### https://ns.eccenca.com/reasoning/e02aaed014c94e0c91bf960fed127750/vocab/animal_lover :animal_lover rdf:type owl:Class ; @@ -261,19 +252,6 @@ owl:Thing rdf:type owl:Class . rdfs:label "cat liker"^^xsd:string . -### https://ns.eccenca.com/reasoning/e02aaed014c94e0c91bf960fed127750/vocab/cat_owner -:cat_owner rdf:type owl:Class ; - owl:equivalentClass [ owl:intersectionOf ( :person - [ rdf:type owl:Restriction ; - owl:onProperty :has_pet ; - owl:someValuesFrom :cat - ] - ) ; - rdf:type owl:Class - ] ; - rdfs:comment ""^^xsd:string ; - rdfs:isDefinedBy ; - rdfs:label "cat owner"^^xsd:string . ### https://ns.eccenca.com/reasoning/e02aaed014c94e0c91bf960fed127750/vocab/company diff --git a/tests/test_reason_ontology_2.ttl b/tests/test_reason_ontology_2.ttl new file mode 100644 index 0000000..fde26ff --- /dev/null +++ b/tests/test_reason_ontology_2.ttl @@ -0,0 +1,24 @@ +@prefix : . +@prefix owl: . +@prefix rdf: . +@prefix xml: . +@prefix xsd: . +@prefix rdfs: . +@base . + + rdf:type owl:Ontology ; + rdfs:comment "Vocabulary for a reasoning use case"@en ; + rdfs:label "Eccenca Reasoning Vocabulary"@en ; + rdfs:seeAlso ; + owl:imports . + + +### https://ns.eccenca.com/reasoning/e02aaed014c94e0c91bf960fed127750/vocab/animal +:animal rdf:type owl:Class ; + rdfs:subClassOf [ rdf:type owl:Restriction ; + owl:onProperty :eats ; + owl:someValuesFrom owl:Thing + ] ; + rdfs:comment ""^^xsd:string ; + rdfs:isDefinedBy ; + rdfs:label "animal"^^xsd:string . diff --git a/tests/test_reason_ontology_3.ttl b/tests/test_reason_ontology_3.ttl new file mode 100644 index 0000000..0e7e0ae --- /dev/null +++ b/tests/test_reason_ontology_3.ttl @@ -0,0 +1,27 @@ +@prefix : . +@prefix owl: . +@prefix rdf: . +@prefix xml: . +@prefix xsd: . +@prefix rdfs: . +@base . + + rdf:type owl:Ontology ; + rdfs:comment "Vocabulary for a reasoning use case"@en ; + rdfs:label "Eccenca Reasoning Vocabulary"@en ; + rdfs:seeAlso . + + +### https://ns.eccenca.com/reasoning/e02aaed014c94e0c91bf960fed127750/vocab/cat_owner +:cat_owner rdf:type owl:Class ; + owl:equivalentClass [ owl:intersectionOf ( :person + [ rdf:type owl:Restriction ; + owl:onProperty :has_pet ; + owl:someValuesFrom :cat + ] + ) ; + rdf:type owl:Class + ] ; + rdfs:comment ""^^xsd:string ; + rdfs:isDefinedBy ; + rdfs:label "cat owner"^^xsd:string . \ No newline at end of file diff --git a/tests/test_validate.md b/tests/test_validate.md index f0ee2ce..f7a6f74 100644 --- a/tests/test_validate.md +++ b/tests/test_validate.md @@ -6,14 +6,14 @@ # Axiom Impact ## Axioms used 1 times -- [A](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/A) SubClassOf [B](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/B) [vocab] -- [A](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/A) DisjointWith [B](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/B) [vocab] -- [D_6](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/D_6) Type [A](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/A) [vocab] +- [A](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/A) SubClassOf [B](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/B) [] +- [A](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/A) DisjointWith [B](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/B) [] +- [D_6](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/D_6) Type [A](https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/A) [] # Ontologies used: -- vocab (https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab) +- (https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab3/) diff --git a/tests/test_validate_ontology_1.ttl b/tests/test_validate_ontology_1.ttl new file mode 100644 index 0000000..f27b0c2 --- /dev/null +++ b/tests/test_validate_ontology_1.ttl @@ -0,0 +1,9 @@ +@prefix : . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@base . + + rdf:type owl:Ontology ; + owl:imports . + diff --git a/tests/test_validate_ontology_2.ttl b/tests/test_validate_ontology_2.ttl new file mode 100644 index 0000000..008cd76 --- /dev/null +++ b/tests/test_validate_ontology_2.ttl @@ -0,0 +1,9 @@ +@prefix : . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@base . + + rdf:type owl:Ontology ; + owl:imports . + diff --git a/tests/test_validate_ontology.ttl b/tests/test_validate_ontology_3.ttl similarity index 94% rename from tests/test_validate_ontology.ttl rename to tests/test_validate_ontology_3.ttl index e6fa516..2f5424e 100644 --- a/tests/test_validate_ontology.ttl +++ b/tests/test_validate_ontology_3.ttl @@ -2,9 +2,9 @@ @prefix owl: . @prefix rdf: . @prefix rdfs: . -@base . +@base . - rdf:type owl:Ontology . + rdf:type owl:Ontology . ################################################################# # Classes