Skip to content

Commit

Permalink
add '[processed ontology] a owl:Ontology' to output graph, use config…
Browse files Browse the repository at this point in the history
… port input for valid OWL2 profiles in Reasin plugin
  • Loading branch information
muddymudskipper committed Aug 12, 2024
1 parent bc6cf5e commit 7ff5ff9
Show file tree
Hide file tree
Showing 13 changed files with 53 additions and 66 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
The ontology IRI on the input overrides the plugin setting.
- update execution report
- Output graph IRI selectable from existing graphs
- When "input_profiles" is enabled the ontology IRI and list of valid OWL2 profiles is now taken from the config port.
The list of valid profiles is a comma-separated string (e.g. "Full,DL")

## [1.0.0beta4] 2024-07-12

Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,8 @@ Validate the input ontology against OWL profiles (DL, EL, QL, RL, and Full). The

### Process valid OWL profiles from input

If enabled along with the "Validate OWL2 profiles" parameter, the list of valid profiles is taken from the plugin input,
without validating the ontology against the profiles in the plugin. The inputs need to include the entity paths "profile"
for the valid profiles, and "ontology" for the ontology IRI, the latter overriding the setting in the "Reason" plugin.
If the "Validate OWL2 profiles" parameter is enabled in the "Validate" plugin, it can be directly connected to the 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.


Expand Down
71 changes: 25 additions & 46 deletions cmem_plugin_reason/plugin_reason.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Reasoning workflow plugin module"""

from collections.abc import Sequence
from datetime import UTC, datetime
from os import environ
from pathlib import Path
Expand All @@ -12,12 +11,12 @@
from cmem.cmempy.dp.proxy.graph import get
from cmem_plugin_base.dataintegration.context import ExecutionContext, ExecutionReport
from cmem_plugin_base.dataintegration.description import Icon, Plugin, PluginParameter
from cmem_plugin_base.dataintegration.entity import Entities, EntityPath, EntitySchema
from cmem_plugin_base.dataintegration.entity import EntityPath, EntitySchema
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, FixedSchemaPort
from cmem_plugin_base.dataintegration.types import BoolParameterType
from cmem_plugin_base.dataintegration.ports import FixedNumberOfInputs
from cmem_plugin_base.dataintegration.types import BoolParameterType, StringParameterType
from cmem_plugin_base.dataintegration.utils import setup_cmempy_user_access
from inflection import underscore
from urllib3.exceptions import InsecureRequestWarning
Expand Down Expand Up @@ -177,11 +176,20 @@
name="input_profiles",
label="Process valid OWL profiles from input",
description="""If the "validate OWL profiles" parameter is enabled, the valid profiles
and ontology IRI is taken from the input (paths "profile" and "ontology") instead of
running the validation in the plugin.""",
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").""",
default_value=False,
advanced=True,
),
PluginParameter(
param_type=StringParameterType(),
name="valid_profiles",
label="Valid OWL2 profiles",
description="Valid OWL2 profiles for the processed ontology.",
default_value="",
visible=False,
),
],
)
class ReasonPlugin(WorkflowPlugin):
Expand Down Expand Up @@ -210,6 +218,7 @@ def __init__( # noqa: PLR0913, C901
validate_profile: bool = False,
input_profiles: bool = False,
max_ram_percentage: int = MAX_RAM_PERCENTAGE_DEFAULT,
valid_profiles: str = "",
) -> None:
self.axioms = {
"SubClass": sub_class,
Expand Down Expand Up @@ -254,14 +263,12 @@ def __init__( # noqa: PLR0913, C901
self.validate_profile = validate_profile
self.input_profiles = input_profiles
self.max_ram_percentage = max_ram_percentage
self.valid_profiles = valid_profiles

for k, v in self.axioms.items():
self.__dict__[underscore(k)] = v

if validate_profile and input_profiles:
self.input_ports = FixedNumberOfInputs([FixedSchemaPort(self.generate_input_schema())])
else:
self.input_ports = FixedNumberOfInputs([])
self.input_ports = FixedNumberOfInputs([])
self.output_port = None

def generate_input_schema(self) -> EntitySchema:
Expand Down Expand Up @@ -324,17 +331,7 @@ def reason(self, graphs: dict) -> None:
raise OSError(response.stderr.decode())
raise OSError("ROBOT error")

def post_valid_profiles(self, inputs: Sequence[Entities], graphs: dict) -> None:
"""Post valid profiles. Optionally get valid profiles from input."""
if self.input_profiles:
values = next(inputs[0].entities).values
paths = [p.path for p in inputs[0].schema.paths]
valid_profiles = values[paths.index("profile")]
else:
valid_profiles = validate_profiles(self, graphs)
post_profiles(self, valid_profiles)

def _execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> None:
def _execute(self, context: ExecutionContext) -> None:
"""`Execute plugin"""
setup_cmempy_user_access(context.user)
graphs = get_graphs_tree(
Expand All @@ -346,7 +343,11 @@ def _execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> Non
setup_cmempy_user_access(context.user)
send_result(self.output_graph_iri, Path(self.temp) / "result.ttl")
if self.validate_profile:
self.post_valid_profiles(inputs, graphs)
if self.input_profiles:
valid_profiles = self.valid_profiles.split(",")
else:
valid_profiles = validate_profiles(self, graphs)
post_profiles(self, valid_profiles)
post_provenance(self, get_provenance(self, context))

context.report.update(
Expand All @@ -357,35 +358,13 @@ def _execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> Non
)
)

def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> None:
def execute(self, inputs: None, context: ExecutionContext) -> None: # noqa: ARG002
"""Validate input, execute plugin with temporary directory"""
context.report.update(
ExecutionReport(
operation="reason",
operation_desc="ontologies and data graphs processed.",
)
)
if self.input_profiles:
errors = ""
values = next(inputs[0].entities).values
paths = [p.path for p in inputs[0].schema.paths]
if not inputs:
raise OSError(
'Input entities needed if "Process valid OWL profiles from input" is enabled'
)
if "profile" not in paths:
errors += 'No value for "profile" given on input. '
if "ontology" not in paths:
errors += 'No value for "ontology" given on input. '
self.ontology_graph_iri = values[paths.index("ontology")][0]
if not validators.url(self.ontology_graph_iri):
errors += 'Invalid IRI for parameter "Ontology graph IRI". '
if self.ontology_graph_iri == self.data_graph_iri:
errors += "Ontology graph IRI cannot be the same as the data graph IRI. "
if self.ontology_graph_iri == self.output_graph_iri:
errors += "Ontology graph IRI cannot be the same as the output graph IRI. "
if errors:
raise ValueError(errors[:-1])

with TemporaryDirectory() as self.temp:
self._execute(inputs, context)
self._execute(context)
8 changes: 4 additions & 4 deletions cmem_plugin_reason/plugin_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ def __init__( # noqa: PLR0913

def generate_output_schema(self) -> EntitySchema:
"""Generate the output schema."""
paths = [EntityPath("markdown"), EntityPath("ontology")]
paths = [EntityPath("markdown"), EntityPath("ontology_graph_iri")]
if self.validate_profile:
paths.append(EntityPath("profile"))
paths.append(EntityPath("valid_profiles"))
return EntitySchema(type_uri="validate", paths=paths)

def get_graphs(self, graphs: dict, context: ExecutionContext) -> None:
Expand Down Expand Up @@ -191,7 +191,7 @@ def make_entities(self, text: str, valid_profiles: list) -> Entities:
"""Make entities"""
values = [[text], [self.ontology_graph_iri]]
if self.validate_profile:
values.append(valid_profiles)
values.append([",".join(valid_profiles)])
entities = [
Entity(
uri="https://eccenca.com/plugin_validateontology/result",
Expand Down Expand Up @@ -246,7 +246,7 @@ def _execute(self, context: ExecutionContext) -> Entities:

return self.make_entities(text, valid_profiles)

def execute(self, inputs: tuple, context: ExecutionContext) -> Entities: # noqa: ARG002
def execute(self, inputs: None, context: ExecutionContext) -> Entities: # noqa: ARG002
"""Remove temp files on error"""
context.report.update(
ExecutionReport(
Expand Down
2 changes: 1 addition & 1 deletion cmem_plugin_reason/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def post_profiles(plugin: WorkflowPlugin, valid_profiles: list) -> None:
query = f"""
INSERT DATA {{
GRAPH <{plugin.output_graph_iri}> {{
<{plugin.ontology_graph_iri}>
<{plugin.ontology_graph_iri}> a <http://www.w3.org/2002/07/owl#Ontology> ;
<https://vocab.eccenca.com/plugin/reason/profile> "{profiles}" .
}}
}}
Expand Down
3 changes: 2 additions & 1 deletion tests/test_elk.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,6 @@
vocab:pet_owner .


vocab: <https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .
vocab: a owl:Ontology ;
<https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .

3 changes: 2 additions & 1 deletion tests/test_emr.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,5 @@
vocab:pet_owner .


vocab: <https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .
vocab: a owl:Ontology ;
<https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .
3 changes: 2 additions & 1 deletion tests/test_hermit.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,5 @@
<https://ns.eccenca.com/reasoning/e02aaed014c94e0c91bf960fed127750/data/Louie> .


vocab: <https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .
vocab: a owl:Ontology ;
<https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .
3 changes: 2 additions & 1 deletion tests/test_jfact.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,5 @@
<https://ns.eccenca.com/reasoning/e02aaed014c94e0c91bf960fed127750/data/Louie> .


vocab: <https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .
vocab: a owl:Ontology ;
<https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .
9 changes: 5 additions & 4 deletions tests/test_reason.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,10 @@ def test_reasoner(reasoner: str, err_list: list) -> list:
class_assertion=True,
property_assertion=True,
validate_profile=True,
).execute((), context=TestExecutionContext())
).execute(None, context=TestExecutionContext())

result = get_remote_graph(REASON_RESULT_GRAPH_IRI)
result.serialize(f"temp/{reasoner}.ttl", format="turtle")
test = Graph().parse(Path(__path__[0]) / f"test_{reasoner}.ttl", format="turtle")
if to_isomorphic(result) != to_isomorphic(test):
err_list.append(reasoner)
Expand All @@ -91,16 +92,16 @@ def test_validate(errors: str) -> str:
output_graph_iri=OUTPUT_GRAPH_IRI,
validate_profile=True,
md_filename=MD_FILENAME,
).execute((), context=TestExecutionContext(PROJECT_ID))
).execute(None, context=TestExecutionContext(PROJECT_ID))

val_errors = ""
md_test = (Path(__path__[0]) / "test_validate.md").read_text()

if next(iter(result.entities)).values[0][0] != md_test: # type: ignore[union-attr]
val_errors += 'EntityPath "markdown" output error. '

if next(iter(result.entities)).values[2] != ["Full", "DL", "EL", "QL", "RL"]: # type: ignore[union-attr]
val_errors += 'EntityPath "profile" output error. '
if next(iter(result.entities)).values[2][0] != "Full,DL,EL,QL,RL": # type: ignore[union-attr]
val_errors += 'EntityPath "valid_profile" output error. '

if md_test != get_resource(PROJECT_ID, MD_FILENAME).decode():
val_errors += "Markdown file error. "
Expand Down
3 changes: 2 additions & 1 deletion tests/test_structural.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,5 @@
vocab:animal .


vocab: <https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .
vocab: a owl:Ontology ;
<https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .
3 changes: 2 additions & 1 deletion tests/test_validate_output.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@

<https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/B> a owl:Class .

<https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/> <https://vocab.eccenca.com/plugin/reason/profile> "Full", "DL", "EL", "QL", "RL" .
<https://ns.eccenca.com/validateontology/e02aaed014c94e0c91bf960fed127750/vocab/> a owl:Ontology ;
<https://vocab.eccenca.com/plugin/reason/profile> "Full", "DL", "EL", "QL", "RL" .

3 changes: 2 additions & 1 deletion tests/test_whelk.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,5 @@
<https://ns.eccenca.com/reasoning/e02aaed014c94e0c91bf960fed127750/data/Louie> .


vocab: <https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .
vocab: a owl:Ontology ;
<https://vocab.eccenca.com/plugin/reason/profile> "DL" , "Full" .

0 comments on commit 7ff5ff9

Please sign in to comment.