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 all 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
35 changes: 35 additions & 0 deletions ontopy/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -835,3 +835,38 @@
)

return layout


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

Arguments:
onto: Ontology to work on.
src: Name of source annotation.
dst: Name or IRI of destination annotation. Use IRI if the
destination annotation is not already in the ontology.
"""
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]

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

Check warning on line 857 in ontopy/utils.py

View check run for this annotation

Codecov / codecov/patch

ontopy/utils.py#L857

Added line #L857 was not covered by tests
else:
if "://" not in dst:
raise ValueError(

Check warning on line 860 in ontopy/utils.py

View check run for this annotation

Codecov / codecov/patch

ontopy/utils.py#L860

Added line #L860 was 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], key=len)
iri = dst
dst = onto.new_annotation_property(name, owlready2.AnnotationProperty)
dst.iri = iri

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)
30 changes: 30 additions & 0 deletions tests/tools/test_ontoconvert.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,33 @@ 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)

# Test 4 - copy-annotation with source as annotation label
ontoconvert.main(
[
"-c prefLabel-->http://www.w3.org/2004/02/skos/core#hiddenLabel",
"--iri=https://w3id.org/ex/testonto",
"--base-iri=https://w3id.org/ex/testonto#",
str(ontodir / "testonto.ttl"),
str(outdir / "test_ontoconvert4.ttl"),
]
)
input4 = (ontodir / "testonto.ttl").read_text()
output4 = (outdir / "test_ontoconvert4.ttl").read_text()
assert not re.search('skos:hiddenLabel "hasAnnotationProperty"@en', input4)
assert re.search('skos:hiddenLabel "hasAnnotationProperty"@en', output4)
39 changes: 37 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,29 @@ 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. FROM and TO may be given as "
"full IRIs or (if they already exists as annotations in the "
"ontology) as entity names. "
"This option be given 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 +95,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 +211,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 +249,10 @@ def main(argv: list = None):
debug=verbose,
)

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

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