Skip to content

Commit

Permalink
add parameters for owl:imports and entity output. PLugin doc imported…
Browse files Browse the repository at this point in the history
… from md files
  • Loading branch information
muddymudskipper committed Aug 13, 2024
1 parent d8c3d40 commit 304abf7
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 25 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
### Added

- defined input and output schema
- Reason: parameters to import the result graph in the ontology graph and to import the ontology graph in the result graph
- Validate: parameter to enable/disable entity output

### Fixed

Expand Down
65 changes: 51 additions & 14 deletions cmem_plugin_reason/plugin_reason.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import validators.url
from cmem.cmempy.dp.proxy.graph import get
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
Expand All @@ -25,6 +26,7 @@
MAX_RAM_PERCENTAGE_PARAMETER,
ONTOLOGY_GRAPH_IRI_PARAMETER,
OUTPUT_GRAPH_IRI_PARAMETER,
REASON_DOC,
REASONERS,
VALIDATE_PROFILES_PARAMETER,
create_xml_catalog_file,
Expand All @@ -45,9 +47,7 @@
label="Reason",
icon=Icon(file_name="fluent--brain-circuit-24-regular.svg", package=__package__),
description="Performs OWL reasoning.",
documentation="""A task performing OWL reasoning. With an OWL ontology and a data graph as input
the reasoning result is written to a specified graph. The following reasoners are supported:
ELK, Expression Materializing Reasoner, HermiT, JFact, Structural Reasoner and Whelk.""",
documentation=REASON_DOC,
parameters=[
ONTOLOGY_GRAPH_IRI_PARAMETER,
OUTPUT_GRAPH_IRI_PARAMETER,
Expand Down Expand Up @@ -181,6 +181,22 @@
default_value=False,
advanced=True,
),
PluginParameter(
param_type=BoolParameterType(),
name="import_ontology",
label="Add ontology graph import to result graph.",
description="""Add the triple <output_graph_iri> owl:imports <ontology_graph_iri> to the
output graph.""",
default_value=True,
),
PluginParameter(
param_type=BoolParameterType(),
name="import_result",
label="Add result graph import to ontology graph.",
description="""Add the triple <ontology_graph_iri> owl:imports <output_graph_iri> to the
ontology graph.""",
default_value=False,
),
PluginParameter(
param_type=StringParameterType(),
name="valid_profiles",
Expand Down Expand Up @@ -215,6 +231,8 @@ def __init__( # noqa: PLR0913, C901
sub_data_property: bool = False,
sub_object_property: bool = False,
validate_profile: bool = False,
import_ontology: bool = True,
import_result: bool = False,
input_profiles: bool = False,
max_ram_percentage: int = MAX_RAM_PERCENTAGE_DEFAULT,
valid_profiles: str = "",
Expand Down Expand Up @@ -250,6 +268,11 @@ def __init__( # noqa: PLR0913, C901
errors += 'Invalid value for parameter "Reasoner". '
if True not in self.axioms.values():
errors += "No axiom generator selected. "
if import_result and import_ontology:
errors += (
'Enable only one of "Add result graph import to ontology graph" and "Add '
'ontology graph import to result graph". '
)
if (
input_profiles
and valid_profiles
Expand All @@ -268,6 +291,8 @@ def __init__( # noqa: PLR0913, C901
self.output_graph_iri = output_graph_iri
self.reasoner = reasoner
self.validate_profile = validate_profile
self.import_ontology = import_ontology
self.import_result = import_result
self.input_profiles = input_profiles
self.max_ram_percentage = max_ram_percentage
self.valid_profiles = valid_profiles
Expand All @@ -285,25 +310,20 @@ def get_graphs(self, graphs: dict, context: ExecutionContext) -> None:
with (Path(self.temp) / filename).open("w", encoding="utf-8") as file:
setup_cmempy_user_access(context.user)
for line in get(iri).text.splitlines():
if line != (
f"<{iri}> <http://www.w3.org/2002/07/owl#imports> "
f"<{self.output_graph_iri}> ."
if not line.endswith(
f"<http://www.w3.org/2002/07/owl#imports> <{self.output_graph_iri}> ."
):
file.write(line + "\n")
if iri == self.data_graph_iri:
file.write(
f"<{iri}> <http://www.w3.org/2002/07/owl#imports> "
f"<{self.ontology_graph_iri}> ."
)

def reason(self, graphs: dict) -> None:
"""Reason"""
axioms = " ".join(k for k, v in self.axioms.items() if v)
data_location = f"{self.temp}/{graphs[self.data_graph_iri]}"
ontology_location = f"{self.temp}/{graphs[self.ontology_graph_iri]}"
utctime = str(datetime.fromtimestamp(int(time()), tz=UTC))[:-6].replace(" ", "T") + "Z"
cmd = (
f'reason --input "{data_location}" '
f"--reasoner {self.reasoner} "
f'merge --input "{data_location}" --input "{ontology_location}" '
f"reason --reasoner {self.reasoner} "
f'--axiom-generators "{axioms}" '
f"--include-indirect true "
f"--exclude-duplicate-axioms true "
Expand All @@ -321,8 +341,10 @@ def reason(self, graphs: dict) -> None:
f'--link-annotation dc:source "{self.data_graph_iri}" '
f'--link-annotation dc:source "{self.ontology_graph_iri}" '
f'--typed-annotation dc:created "{utctime}" xsd:dateTime '
f'--output "{self.temp}/result.ttl"'
)
if self.import_ontology:
cmd += f'--link-annotation owl:imports "{self.ontology_graph_iri}" '
cmd += f'--output "{self.temp}/result.ttl"'
response = robot(cmd, self.max_ram_percentage)
if response.returncode != 0:
if response.stdout:
Expand All @@ -331,6 +353,18 @@ def reason(self, graphs: dict) -> None:
raise OSError(response.stderr.decode())
raise OSError("ROBOT error")

def add_result_import(self) -> None:
"""Add result graph import to ontology graph"""
query = f"""
INSERT DATA {{
GRAPH <{self.ontology_graph_iri}> {{
<{self.ontology_graph_iri}> <http://www.w3.org/2002/07/owl#imports>
<{self.output_graph_iri}>
}}
}}
"""
post(query=query)

def _execute(self, context: ExecutionContext) -> None:
"""`Execute plugin"""
setup_cmempy_user_access(context.user)
Expand All @@ -349,6 +383,9 @@ def _execute(self, context: ExecutionContext) -> None:
valid_profiles = validate_profiles(self, graphs)
post_profiles(self, valid_profiles)
post_provenance(self, get_provenance(self, context))
if self.import_result:
setup_cmempy_user_access(context.user)
self.add_result_import()

context.report.update(
ExecutionReport(
Expand Down
35 changes: 24 additions & 11 deletions cmem_plugin_reason/plugin_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
ONTOLOGY_GRAPH_IRI_PARAMETER,
OUTPUT_GRAPH_IRI_PARAMETER,
REASONERS,
VALIDATE_DOC,
VALIDATE_PROFILES_PARAMETER,
create_xml_catalog_file,
get_graphs_tree,
Expand All @@ -45,11 +46,7 @@
@Plugin(
label="Validate OWL consistency",
description="Validates the consistency of an OWL ontology.",
documentation="""A task validating the consistency of an OWL ontology and generating an
explanation if inconsistencies are found. The explanation can be written to the project as a
Markdown file and/or to a specified graph. The Markdown string is also provided as an output
entity using the path "text". The following reasoners are supported: ELK, Expression
Materializing Reasoner, HermiT, JFact, Structural Reasoner and Whelk.""",
documentation=VALIDATE_DOC,
icon=Icon(file_name="file-icons--owl.svg", package=__package__),
parameters=[
ONTOLOGY_GRAPH_IRI_PARAMETER,
Expand Down Expand Up @@ -78,6 +75,15 @@
"not output entities.",
default_value=False,
),
PluginParameter(
param_type=BoolParameterType(),
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".""",
default_value=False,
),
],
)
class ValidatePlugin(WorkflowPlugin):
Expand All @@ -90,6 +96,7 @@ def __init__( # noqa: PLR0913
output_graph_iri: str = "",
md_filename: str = "",
validate_profile: bool = False,
output_entities: bool = False,
stop_at_inconsistencies: bool = False,
max_ram_percentage: int = MAX_RAM_PERCENTAGE_DEFAULT,
) -> None:
Expand All @@ -104,6 +111,8 @@ def __init__( # noqa: PLR0913
errors += 'Invalid value for parameter "Reasoner". '
if md_filename and not is_valid_filename(md_filename):
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 max_ram_percentage not in range(1, 101):
errors += 'Invalid value for parameter "Maximum RAM Percentage". '
if errors:
Expand All @@ -119,14 +128,18 @@ def __init__( # noqa: PLR0913
self.md_filename = "mdfile.md"
self.write_md = False
self.validate_profile = validate_profile
self.output_entities = output_entities
self.max_ram_percentage = max_ram_percentage

self.input_ports = FixedNumberOfInputs([])
self.schema = self.generate_output_schema()
self.output_port = FixedSchemaPort(self.schema)
if self.output_entities:
self.schema = self.generate_output_schema()
self.output_port = FixedSchemaPort(self.schema)
else:
self.output_port = None

def generate_output_schema(self) -> EntitySchema:
"""Generate the output schema."""
def generate_output_schema(self) -> EntitySchema | None:
"""Generate output entity schema."""
paths = [EntityPath("markdown"), EntityPath("ontology_graph_iri")]
if self.validate_profile:
paths.append(EntityPath("valid_profiles"))
Expand Down Expand Up @@ -243,8 +256,8 @@ def _execute(self, context: ExecutionContext) -> Entities:
entity_count=1,
)
)

return self.make_entities(text, valid_profiles)
if self.output_entities:
return self.make_entities(text, valid_profiles)

def execute(self, inputs: None, context: ExecutionContext) -> Entities: # noqa: ARG002
"""Remove temp files on error"""
Expand Down
72 changes: 72 additions & 0 deletions cmem_plugin_reason/reason_doc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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). The ontology is annotated in the output graph.

### Process valid OWL profiles from input

If enabled along with the "Validate OWL2 profiles" parameter, the ontology IRI and list of valid profiles is taken from the config port input,
without validating the ontology against the profiles in the plugin. If the "Validate OWL2 profiles" parameter is enabled in the "Validate" plugin, it can be directly connected to the input
of the "Reason" plugin.


### 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.
6 changes: 6 additions & 0 deletions cmem_plugin_reason/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@

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()

REASONERS = OrderedDict(
{
"elk": "ELK",
Expand Down
54 changes: 54 additions & 0 deletions cmem_plugin_reason/validate_doc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
A task validating the consistency of an OWL ontology and generating an explanation if inconsistencies are found.
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" as a comma-separated string.

## Options

### Ontology graph IRI

The IRI of the input ontology graph. The graph IRI is selected from a list of graphs of type`owl:Ontology`.

### 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)

### Produce output graph

If enabled, an explanation graph is created.

### Output graph IRI

The IRI of the output graph for the reasoning result.

⚠️ Existing graphs will be overwritten.

### Write markdown explanation file

If enabled, an explanation markdown file is written to the project.

### Output filename

The filename of the Markdown file with the explanation of inconsistencies.

⚠️ Existing files will be overwritten.

### Stop at inconsistencies
Raise an error if inconsistencies are found. If enabled, the plugin does not output entities.

### Validate OWL2 profiles

Validate the input ontology against OWL profiles (DL, EL, QL, RL, and Full). The valid profiles are added to the output
Markdown file and the ontology is annotated in the output graph. The plugin outputs the profiles with path "valid_profiles",
and the ontology IRI with path "ontology_graph_iri".

### 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.

0 comments on commit 304abf7

Please sign in to comment.