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

Add --copy-annotation option to ontoconvert #732

Merged
merged 7 commits into from
Mar 16, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions ontopy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -835,3 +835,34 @@
)

return layout


def copy_annotation(onto, src, dst):
"""In all classes and properties in `onto`, copy annotation `src` to `dst`.

The `src` and `dst` can either be provided as a label string or a full IRI.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should document with "Parameters" of "Arguments", as this renders nicely in the docementaion

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

"""
if onto.world[src]:
src = onto.world[src]
Comment on lines +849 to +850
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why check in world?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we want to support src of the form http://www.w3.org/2004/02/skos/core#prefLabel

else:
src = onto[src]

Check warning on line 848 in ontopy/utils.py

View check run for this annotation

Codecov / codecov/patch

ontopy/utils.py#L848

Added line #L848 was not covered by tests

if onto.world[dst]:
dst = onto.world[dst]
elif dst in onto:
dst = onto[dst]

Check warning on line 853 in ontopy/utils.py

View check run for this annotation

Codecov / codecov/patch

ontopy/utils.py#L852-L853

Added lines #L852 - L853 were not covered by tests
else:
if "://" not in dst:
raise ValueError(

Check warning on line 856 in ontopy/utils.py

View check run for this annotation

Codecov / codecov/patch

ontopy/utils.py#L855-L856

Added lines #L855 - L856 were not covered by tests
Comment on lines +859 to +860
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have tests for errors that we expect may happen.

Copy link
Collaborator Author

@jesper-friis jesper-friis Mar 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this such a test? True, there might exists invalid IRIs that contain "://", but this easy and covers most of all error cases. The rest will raise an exception from owlready2 when trying to create a new annotation property.

What other errors would you expect?

"new destination annotation property must be provided as "
"a full IRI"
)
name = min(dst.rsplit("#")[-1], dst.rsplit("/")[-1])
iri = dst
dst = onto.new_annotation_property(name, owlready2.AnnotationProperty)
dst.iri = iri

Check warning on line 863 in ontopy/utils.py

View check run for this annotation

Codecov / codecov/patch

ontopy/utils.py#L860-L863

Added lines #L860 - L863 were not covered by tests
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs test

Copy link
Collaborator Author

@jesper-friis jesper-friis Mar 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already tested in tests/tools/test_ontoconvert.py

Just codecov that doesn't recognise tests done in subprocesses...


for e in onto.get_entities():
new = getattr(e, src.name).first()
if new and new not in getattr(e, dst.name):
getattr(e, dst.name).append(new)
15 changes: 15 additions & 0 deletions tests/tools/test_ontoconvert.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,18 @@ def test_run() -> None:
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)

# Test 3 - copy-annotation
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are checking copy_preflabel. I agree that this is an alias for annotation, but I think it would be good to check that other annotations also are added. Most particularly, that we can copy to an annotation that is originally not in the ontology.

Also, I think we should have a test where preflabel is not converted, as prefLabel is copied by default (if I understand store_true correctly).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rdfs:label is not in the ontology, so we are actually creating a new annotation.

Ok, added new test with src="prefLabel" .

ontoconvert.main(
[
"-p",
"--iri=https://w3id.org/ex/testonto",
"--base-iri=https://w3id.org/ex/testonto#",
str(ontodir / "testonto.ttl"),
str(outdir / "test_ontoconvert3.ttl"),
]
)
input3 = (ontodir / "testonto.ttl").read_text()
output3 = (outdir / "test_ontoconvert3.ttl").read_text()
assert not re.search('rdfs:label "hasAnnotationProperty"@en', input3)
assert re.search('rdfs:label "hasAnnotationProperty"@en', output3)
36 changes: 34 additions & 2 deletions tools/ontoconvert
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import warnings
from rdflib.util import guess_format

from ontopy import get_ontology
from ontopy.utils import annotate_source, rename_iris
from ontopy.utils import annotate_source, rename_iris, copy_annotation


def main(argv: list = None):
Expand Down Expand Up @@ -59,6 +59,26 @@ def main(argv: list = None):
"The default is to append to it."
),
)
parser.add_argument(
"--copy-annotation",
"-c",
action="append",
default=[],
metavar="FROM-->TO",
help=(
"Copy annotation FROM to annotation TO in each class and "
"property in the ontology. May be provided multiple times."
),
)
parser.add_argument(
"--copy-preflabel",
"-p",
action="store_true",
help=(
"Alias for: `--copy-annotation=http://www.w3.org/2004/02/skos/"
"core#prefLabel-->http://www.w3.org/2000/01/rdf-schema#label`"
),
)
parser.add_argument(
"--no-catalog",
"-n",
Expand All @@ -72,7 +92,7 @@ def main(argv: list = None):
"--infer",
"-i",
nargs="?",
const="FaCT++",
const="HermiT",
choices=["HermiT", "Pellet", "FaCT++"],
metavar="NAME",
help=(
Expand Down Expand Up @@ -188,6 +208,14 @@ def main(argv: list = None):
if not output_format:
output_format = "xml"

if args.copy_annotation is None:
args.copy_annotation = []
if args.copy_preflabel:
args.copy_annotation.append(
"http://www.w3.org/2004/02/skos/core#prefLabel-->"
"http://www.w3.org/2000/01/rdf-schema#label"
)

# Perform conversion
with warnings.catch_warnings(record=True) as warnings_handle:
warnings.simplefilter("always")
Expand Down Expand Up @@ -218,6 +246,10 @@ def main(argv: list = None):
debug=verbose,
)

for cpy in args.copy_annotation:
src, dst = cpy.split("-->", 1)
copy_annotation(onto, src, dst)

onto.save(
args.output,
format=output_format,
Expand Down
Loading