Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct saving squashed ontology #719

Merged
merged 11 commits into from
Feb 29, 2024
33 changes: 27 additions & 6 deletions ontopy/ontology.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,21 +1000,42 @@ def save(
)
elif squash:
URIRef, RDF, OWL = rdflib.URIRef, rdflib.RDF, rdflib.OWL
iri = self.iri if self.iri else self.base_iri
graph = self.world.as_rdflib_graph()
graph.namespace_manager.bind("", rdflib.Namespace(iri))

# Make a copy of the owlready2 graph object to not mess with
# owlready2 internals
graph = rdflib.Graph()
graph_owlready2 = self.world.as_rdflib_graph()
for triple in graph_owlready2.triples((None, None, None)):
graph.add(triple)

# Add namespaces
graph.namespace_manager.bind("", rdflib.Namespace(self.base_iri))
graph.namespace_manager.bind(
"swrl", rdflib.Namespace("http://www.w3.org/2003/11/swrl#")
)

# Remove all ontology-declarations in the graph that are
# not the current ontology.
for s, _, _ in graph.triples((None, RDF.type, OWL.Ontology)):
for s, _, _ in graph.triples( # pylint: disable=not-an-iterable
(None, RDF.type, OWL.Ontology)
):
if str(s).rstrip("/#") != self.base_iri.rstrip("/#"):
for _, p, o in graph.triples((s, None, None)):
for (
_,
p,
o,
) in graph.triples( # pylint: disable=not-an-iterable
(s, None, None)
):
graph.remove((s, p, o))
graph.remove((s, OWL.imports, None))

# Insert correct IRI of the ontology
if self.iri:
base_iri = URIRef(self.base_iri)
for s, p, o in graph.triples((base_iri, None, None)):
for s, p, o in graph.triples( # pylint: disable=not-an-iterable
(base_iri, None, None)
):
graph.remove((s, p, o))
graph.add((URIRef(self.iri), p, o))

Expand Down
48 changes: 40 additions & 8 deletions tests/tools/test_ontoconvert.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,53 @@
from typing import Callable


def test_run(get_tool: "Callable[[str], ModuleType]", tmpdir: "Path") -> None:
if True:
# def test_run(get_tool: "Callable[[str], ModuleType]", tmpdir: "Path") -> None:
"""Check that running `ontoconvert` works.

Parameters:
get_tool: Local module fixture to load a named tool as a module.
See the current folder's `conftest.py` file.
tmpdir: A generic pytest fixture to generate a temporary directory, which will
exist only for the lifetime of this test function.

tmpdir: A generic pytest fixture to generate a temporary directory,
which will exist only for the lifetime of this test function.
"""
import re
from pathlib import Path
import sys
import importlib

testdir = Path(__file__).resolve().parent.parent
ontodir = testdir / "testonto"
outdir = testdir / "output"
toolsdir = testdir.parent / "tools"

# ontoconvert = get_tool("ontoconvert")

test_file = (
Path(__file__).resolve().parent.parent / "testonto" / "models.ttl"
sys.path.append(str(toolsdir))
ontoconvert = importlib.machinery.SourceFileLoader(
"ontoconvert", str(toolsdir / "ontoconvert")
).load_module()

# Test 1
ontoconvert.main(
[str(ontodir / "models.ttl"), str(outdir / "test_ontoconvert1.ttl")]
)
ontoconvert = get_tool("ontoconvert")
output1 = (outdir / "test_ontoconvert1.ttl").read_text()
assert re.search("@prefix : <http://emmo.info/models#>", output1)
assert re.search("<http://emmo.info/models> .* owl:Ontology", output1)
assert re.search("testclass .* owl:Class", output1)

ontoconvert.main([str(test_file), str(tmpdir / "test.ttl")])
# Test 2 - squash
ontoconvert.main(
[
"-asw",
"--iri=https://w3id.org/ex/testonto",
"--base-iri=https://w3id.org/ex/testonto#",
str(ontodir / "testonto.ttl"),
str(outdir / "test_ontoconvert2.ttl"),
]
)
output2 = (outdir / "test_ontoconvert2.ttl").read_text()
assert re.search("@prefix : <https://w3id.org/ex/testonto#>", output2)
assert re.search("<https://w3id.org/ex/testonto> .* owl:Ontology", output2)
assert re.search("testclass .* owl:Class", output2)
18 changes: 15 additions & 3 deletions tools/ontoconvert
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,20 @@ def main(argv: list = None):
help="Do not infer imported ontologies.",
)
# To be implemented...
parser.add_argument(
"--iri",
"-I",
help="IRI of converted ontology.",
)
parser.add_argument(
"--base-iri",
"-b",
help=(
"Base iri of output ontology. The default is the base iri of "
"Base IRI of converted ontology. The default is the base iri of "
"the input ontology."
"\n\nNOTE: Currently this option does nothing. Kept to not break "
"existing workflows using it."
"\n\nThis argument can be used to workaround the bug in Owlready2 "
"that changes the base IRI of the ontology to always end with a "
"slash."
),
)
parser.add_argument(
Expand Down Expand Up @@ -191,6 +197,12 @@ def main(argv: list = None):
url_from_catalog=args.url_from_catalog,
)

if args.iri:
onto.iri = args.iri

if args.base_iri:
onto.base_iri = args.base_iri

if args.annotate_source:
annotate_source(onto)

Expand Down
Loading