Skip to content

Commit

Permalink
rename main plugin py file, add icon
Browse files Browse the repository at this point in the history
  • Loading branch information
muddymudskipper committed Jun 21, 2024
1 parent 9efe656 commit 4d7542a
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 217 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ include = ["cmem_plugin_robotreason/bin/*"]
```


:bulb: Prior to the build process, the Java library _robot.jar_ (v1.9.4) and the _robot.sh_ script are automatically
:bulb: Prior to the build process, the Java library _robot.jar_ (v1.9.6) and the _robot.sh_ script are automatically
downloaded from the [ROBOT GitHub repository](https://github.com/ontodev/robot). The _jar_ file is not downloaded if it
already exists in the same version. The ROBOT files are downloaded to the directory
_cmem_plugin_robotreason/workflow/bin_ and are not removed automatically when running `task clean`. The files can be
Expand Down
216 changes: 1 addition & 215 deletions cmem_plugin_robotreason/__init__.py
Original file line number Diff line number Diff line change
@@ -1,215 +1 @@
"""Reasoning with robot plugin module"""

import re
import shlex
import unicodedata
from collections import OrderedDict
from collections.abc import Sequence
from datetime import UTC, datetime
from pathlib import Path
from subprocess import run # nosec
from time import time
from uuid import uuid4
from xml.etree.ElementTree import (
Element,
SubElement,
tostring,
)

from cmem.cmempy.dp.proxy.graph import get, get_graph_import_tree, post_streamed
from cmem_plugin_base.dataintegration.context import ExecutionContext
from cmem_plugin_base.dataintegration.description import Plugin, PluginParameter
from cmem_plugin_base.dataintegration.entity import Entities
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 StringParameterType
from cmem_plugin_base.dataintegration.utils import setup_cmempy_user_access
from defusedxml import minidom

ROBOT = Path(__path__[0]) / "bin" / "robot"


def convert_iri_to_filename(value: str) -> str:
"""Convert IRI to filename"""
value = unicodedata.normalize("NFKD", value).encode("ascii", "ignore").decode("ascii")
value = re.sub(r"\.", "_", value.lower())
value = re.sub(r"/", "_", value.lower())
value = re.sub(r"[^\w\s-]", "", value.lower())
value = re.sub(r"[-\s]+", "-", value).strip("-_")
return value + ".nt"


@Plugin(
label="Reasoning with ROBOT",
description="Given a data and an ontology grpah, this task performs reasoning " "using ROBOT.",
documentation="""A task performing reasoning using ROBOT (ROBOT is an OBO Tool).
It takes an OWL ontology and a data graph as inputs and writes the reasoning result
to a specified graph. The following reasoner options are supported: ELK, Expression
Materializing Reasoner, HermiT, JFact, Structural Reasoner and Whelk.""",
parameters=[
PluginParameter(
param_type=GraphParameterType(
classes=[
"http://www.w3.org/2002/07/owl#Ontology",
"https://vocab.eccenca.com/di/Dataset",
"http://rdfs.org/ns/void#Dataset",
]
),
name="data_graph_iri",
label="Data graph IRI",
description="The IRI of the input data graph.",
),
PluginParameter(
param_type=GraphParameterType(classes=["http://www.w3.org/2002/07/owl#Ontology"]),
name="ontology_graph_iri",
label="Ontology_graph_IRI",
description="The IRI of the input ontology graph.",
),
PluginParameter(
param_type=StringParameterType(),
name="result_iri",
label="Result graph IRI",
description="The IRI of the output graph for the reasoning result. "
"WARNING: existing graph will be overwritten!",
),
PluginParameter(
param_type=ChoiceParameterType(
OrderedDict(
{
"elk": "ELK",
"emr": "Expression Materializing Reasoner",
"hermit": "HermiT",
"jfact": "JFact",
"structural": "Structural Reasoner",
"whelk": "Whelk",
}
)
),
name="reasoner",
label="Reasoner",
description="Reasoner option.",
default_value="elk",
),
],
)
class RobotReasonPlugin(WorkflowPlugin):
"""Robot reasoning plugin"""

def __init__(
self, data_graph_iri: str, ontology_graph_iri: str, result_iri: str, reasoner: str
) -> None:
"""Init"""
self.data_graph_iri = data_graph_iri
self.ontology_graph_iri = ontology_graph_iri
self.result_iri = result_iri
self.reasoner = reasoner
self.temp = f"robot_{uuid4().hex}"
if not Path(self.temp).exists():
Path(self.temp).mkdir(parents=True)

def create_xml_catalog_file(self, graphs: dict) -> None:
"""Create XML catalog file"""
file_name = Path(self.temp) / "catalog-v001.xml"
catalog = Element("catalog")
catalog.set("prefer", "public")
catalog.set("xmlns", "urn:oasis:names:tc:entity:xmlns:xml:catalog")
for graph in graphs:
uri = SubElement(catalog, "uri")
uri.set("id", "Auto-generated import resolution by cmem-plugin-robotreason")
uri.set("name", graph)
uri.set("uri", graphs[graph])
reparsed = minidom.parseString(tostring(catalog, "utf-8")).toxml() # nosec
with Path(file_name).open("w", encoding="utf-8") as file:
file.truncate(0)
file.write(reparsed)

def get_graphs(self, graphs: dict) -> None:
"""Get graphs from CMEM"""
for graph in graphs:
with (Path(self.temp) / graphs[graph]).open("w", encoding="utf-8") as file:
file.write(get(graph).text)
file.write(
f"<{graph}> "
f"<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> "
f"<http://www.w3.org/2002/07/owl#Ontology> .\n"
)

def get_graphs_tree(self) -> dict:
"""Get graph import tree"""
graphs = {}
for graph_iri in (self.data_graph_iri, self.ontology_graph_iri):
if graph_iri not in graphs:
graphs[graph_iri] = convert_iri_to_filename(graph_iri)
tree = get_graph_import_tree(graph_iri)
for value in tree["tree"].values():
for iri in value:
if iri not in graphs:
graphs[iri] = convert_iri_to_filename(iri)
return graphs

def reason(self, graphs: dict) -> None:
"""Reason"""
inputs = ""
for value in graphs.values():
inputs += f' --input "{self.temp}/{value}"'
utctime = str(datetime.fromtimestamp(int(time()), tz=UTC))[:-6].replace(" ", "T") + "Z"
cmd = (
f"{ROBOT} merge{inputs} --collapse-import-closure false "
f"reason --reasoner {self.reasoner} "
f'--axiom-generators "ClassAssertion PropertyAssertion" '
f"--include-indirect true "
f"--exclude-duplicate-axioms true "
f"--exclude-owl-thing true "
f"--exclude-tautologies all "
f"--exclude-external-entities "
f"reduce --reasoner {self.reasoner} "
f"unmerge{inputs} "
f'annotate --ontology-iri "{self.result_iri}" '
f"--remove-annotations "
f'--language-annotation rdfs:label "Eccenca Reasoning Result {utctime}" en '
f"--language-annotation rdfs:comment "
f'"Reasoning result set of <{self.data_graph_iri}> and '
f'<{self.ontology_graph_iri}>" en '
f"--language-annotation prov:wasGeneratedBy "
f'"cmem-plugin-robotreason ({self.reasoner})" en '
f'--link-annotation prov:wasDerivedFrom "{self.data_graph_iri}" '
f"--link-annotation prov:wasDerivedFrom "
f'"{self.ontology_graph_iri}" '
f'--typed-annotation dc:created "{utctime}" xsd:dateTime '
f'--output "{self.temp}/result.ttl"'
)
run(shlex.split(cmd), check=False) # noqa: S603

def send_result(self) -> None:
"""Send result"""
post_streamed(
self.result_iri,
str(Path(self.temp) / "result.ttl"),
replace=True,
content_type="text/turtle",
)

def clean_up(self, graphs: dict) -> None:
"""Remove temporary files"""
files = ["catalog-v001.xml", "result.ttl"]
files += list(graphs.values())
for file in files:
try:
(Path(self.temp) / file).unlink()
except (OSError, FileNotFoundError) as err:
self.log.warning(f"Cannot remove file {file} ({err})")
try:
Path(self.temp).rmdir()
except (OSError, FileNotFoundError) as err:
self.log.warning(f"Cannot remove directory {self.temp} ({err})")

def execute(self, inputs: Sequence[Entities], context: ExecutionContext) -> None: # noqa: ARG002
"""Execute plugin"""
setup_cmempy_user_access(context.user)
graphs = self.get_graphs_tree()
self.get_graphs(graphs)
self.create_xml_catalog_file(graphs)
self.reason(graphs)
self.send_result()
self.clean_up(graphs)
"""robotreason - main package"""
Binary file added cmem_plugin_robotreason/obofoundry.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 4d7542a

Please sign in to comment.