diff --git a/CHANGELOG.md b/CHANGELOG.md index ab060cf7a..90a7181f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Updated ELK from 0.4.3 to 0.5.0. [#999]. This is an important change as ELK 0.5.0 is more complete than 0.4.3, which means that it will potentially uncover inferences, in particular unsatisfiable classes, which were not recognised by ELK 0.4.3. - Add support for pluggable commands [#1119] +- Add `--drop-axiom-annotations` option to drop axiom annotations in [`remove`] and [`filter`] [#1023] ### Changed - Migrate to OWL API 4.5.26 to deal with [broken turtle serialiser](https://github.com/ontodev/robot/issues/1129). [#1135] @@ -382,7 +383,7 @@ First official release of ROBOT! [`report`]: http://robot.obolibrary.org/report [`template`]: http://robot.obolibrary.org/template [`validate`]: http://robot.obolibrary.org/validate - +; [#1181]: https://github.com/ontodev/robot/pull/1181 [#1171]: https://github.com/ontodev/robot/pull/1171 [#1168]: https://github.com/ontodev/robot/pull/1168 @@ -402,6 +403,7 @@ First official release of ROBOT! [#1061]: https://github.com/ontodev/robot/issues/1061 [#1030]: https://github.com/ontodev/robot/issues/1030 [#1026]: https://github.com/ontodev/robot/issues/1026 +[#1023]: https://github.com/ontodev/robot/pull/1023 [#1017]: https://github.com/ontodev/robot/issues/1017 [#1016]: https://github.com/ontodev/robot/issues/1016 [#1009]: https://github.com/ontodev/robot/issues/1009 diff --git a/docs/examples/template-drop-axiom-filter.owl b/docs/examples/template-drop-axiom-filter.owl new file mode 100644 index 000000000..5c14a517a --- /dev/null +++ b/docs/examples/template-drop-axiom-filter.owl @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight of a mouse or rat in kilograms (kg). + Rebecca C Jackson + weight in kilograms + + + + + + + + + + + + + + + An inbred strain of mouse used in many scientific investigations. + James A. Overton + B6C3F1 + + + + + + + + + + An inbred strain of rat used in many scientific investigations. + James A. Overton + F 344/N + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.2 + rat 1234 + + + + + + + diff --git a/docs/examples/template-drop-axiom-remove.owl b/docs/examples/template-drop-axiom-remove.owl new file mode 100644 index 000000000..5c14a517a --- /dev/null +++ b/docs/examples/template-drop-axiom-remove.owl @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight of a mouse or rat in kilograms (kg). + Rebecca C Jackson + weight in kilograms + + + + + + + + + + + + + + + An inbred strain of mouse used in many scientific investigations. + James A. Overton + B6C3F1 + + + + + + + + + + An inbred strain of rat used in many scientific investigations. + James A. Overton + F 344/N + + + + + + + + + + + + + + + + + + + + + + + + + + + 0.2 + rat 1234 + + + + + + + diff --git a/docs/filter.md b/docs/filter.md index a4ebfc71d..d08dc3467 100644 --- a/docs/filter.md +++ b/docs/filter.md @@ -28,6 +28,8 @@ Then `filter --term A --term R --term C --select "self parents" --axioms all --s - the objects for `ax2` are `{A, R, C}` (with `R some C` excluded), and all of these are in the target set, so `ax2` is matched and copied - the objects for `ax3` are `{D, E}`, and none of these are in the target set, so `ax3` is not matched and is not copied +Finally `--drop-axiom-annotations` option lets you to specify an annotation property to drop all axiom annotations using it. `all` parameter can be used to drop all axiom annotations. + The `remove` and `filter` operations maintains structural integrity by default: lineage is maintained, and gaps will be filled where classes have been removed. If you wish to *not* preserve the hierarchy, include `--preserve-structure false`. ## Annotations @@ -94,3 +96,13 @@ Create a "base" subset that only includes internal axioms (alternatively, use `r --include-term IAO:0000119 \ --output results/template-base-filter.owl +Create a "base" subset in which axiom annotations involving IAO:0000117 and IAO:0000119 are removed: + + robot filter --input template.owl \ + --base-iri http://example.com/ \ + --select "annotations" \ + --axioms internal \ + --drop-axiom-annotations IAO:0000117 \ + --drop-axiom-annotations IAO:0000119 \ + --output results/template-drop-axiom-filter.owl + diff --git a/docs/remove.md b/docs/remove.md index 3b63aefd6..1d17fffcb 100644 --- a/docs/remove.md +++ b/docs/remove.md @@ -49,6 +49,8 @@ Then `remove --term A --term R --term C --select "self parents" --axioms all --s - the objects for `ax1` are `{A, B}`, and at least one of these is in the target set, so `ax1` is matched and removed - the objects for `ax2` are `{A, R, C}` (with `R some C` excluded), and at least one of these is in the target set, so `ax2` is matched and removed - the objects for `ax3` are `{D, E}`, and none of these are in the target set, so `ax3` is not matched and is not removed + +Finally `--drop-axiom-annotations` option lets you to specify an annotation property to drop all axiom annotations using it. `all` parameter can be used to drop all axiom annotations. ## Preserving the Structure @@ -227,3 +229,14 @@ Create a "base" subset by removing external axioms (alternatively, use `filter - --select annotation-properties \ --signature true \ --output results/filter_annotations.owl + +Create a "base" subset in which axiom annotations involving IAO:0000117 and IAO:0000119 are removed: + + robot remove --input template.owl \ + --base-iri http://example.com/ \ + --axioms external \ + --drop-axiom-annotations IAO:0000117 \ + --drop-axiom-annotations IAO:0000119 \ + --output results/template-drop-axiom-remove.owl + + \ No newline at end of file diff --git a/robot-command/src/main/java/org/obolibrary/robot/FilterCommand.java b/robot-command/src/main/java/org/obolibrary/robot/FilterCommand.java index 490ce9f85..3c1b65d37 100644 --- a/robot-command/src/main/java/org/obolibrary/robot/FilterCommand.java +++ b/robot-command/src/main/java/org/obolibrary/robot/FilterCommand.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.semanticweb.owlapi.apibinding.OWLManager; @@ -43,6 +44,11 @@ public FilterCommand() { o.addOption("r", "trim", true, "if true, keep axioms containing only selected objects"); o.addOption( "S", "signature", true, "if true, keep axioms with any selected entity in their signature"); + o.addOption( + "d", + "drop-axiom-annotations", + true, + "drop all axiom annotations involving a particular annotation property"); options = o; } @@ -159,10 +165,22 @@ public CommandState execute(CommandState state, String[] args) throws Exception } } + List dropParameters = + CommandLineHelper.getOptionalValues(line, "drop-axiom-annotations"); + List annotationsToDrop = + dropParameters.stream() + .filter(s -> !s.equalsIgnoreCase("all")) + .map( + curie -> + CommandLineHelper.maybeCreateIRI(ioHelper, curie, "drop-axiom-annotations")) + .collect(Collectors.toList()); + // Use the select statements to get a set of objects to filter Set relatedObjects = RemoveCommand.getObjects(line, ioHelper, inputOntology, selectGroups); if (relatedObjects.isEmpty()) { + // drop specified axiom annotations + RelatedObjectsHelper.dropAxiomAnnotations(outputOntology, annotationsToDrop, dropParameters); // nothing to filter - save and exit CommandLineHelper.maybeSaveOutput(line, outputOntology); state.setOntology(outputOntology); @@ -212,6 +230,9 @@ public CommandState execute(CommandState state, String[] args) throws Exception outputOntology, RelatedObjectsHelper.getAnnotationAxioms(inputOntology, relatedObjects)); } + // drop specified axiom annotations + RelatedObjectsHelper.dropAxiomAnnotations(outputOntology, annotationsToDrop, dropParameters); + // Save the changed ontology and return the state CommandLineHelper.maybeSaveOutput(line, outputOntology); state.setOntology(outputOntology); diff --git a/robot-command/src/main/java/org/obolibrary/robot/RemoveCommand.java b/robot-command/src/main/java/org/obolibrary/robot/RemoveCommand.java index a4d31dc26..81905de60 100644 --- a/robot-command/src/main/java/org/obolibrary/robot/RemoveCommand.java +++ b/robot-command/src/main/java/org/obolibrary/robot/RemoveCommand.java @@ -1,6 +1,7 @@ package org.obolibrary.robot; import java.util.*; +import java.util.stream.Collectors; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.semanticweb.owlapi.apibinding.OWLManager; @@ -46,6 +47,11 @@ public RemoveCommand() { "if true, remove axioms with any selected entity in their signature"); o.addOption( "p", "preserve-structure", true, "if false, do not preserve hierarchical relationships"); + o.addOption( + "d", + "drop-axiom-annotations", + true, + "drop all axiom annotations involving a particular annotation property"); options = o; } @@ -149,9 +155,21 @@ public CommandState execute(CommandState state, String[] args) throws Exception } } + List dropParameters = + CommandLineHelper.getOptionalValues(line, "drop-axiom-annotations"); + List annotationsToDrop = + dropParameters.stream() + .filter(s -> !s.equalsIgnoreCase("all")) + .map( + curie -> + CommandLineHelper.maybeCreateIRI(ioHelper, curie, "drop-axiom-annotations")) + .collect(Collectors.toList()); + // Get the objects to remove Set relatedObjects = getObjects(line, ioHelper, ontology, selectGroups); if (relatedObjects.isEmpty()) { + // drop specified axiom annotations + RelatedObjectsHelper.dropAxiomAnnotations(ontology, annotationsToDrop, dropParameters); // nothing to remove - save and exit CommandLineHelper.maybeSaveOutput(line, ontology); state.setOntology(ontology); @@ -198,6 +216,9 @@ public CommandState execute(CommandState state, String[] args) throws Exception copy, baseNamespaces, complementObjects, anonymous, internal, external)); } + // drop specified axiom annotations + RelatedObjectsHelper.dropAxiomAnnotations(ontology, annotationsToDrop, dropParameters); + // Save the changed ontology and return the state CommandLineHelper.maybeSaveOutput(line, ontology); state.setOntology(ontology); diff --git a/robot-core/src/main/java/org/obolibrary/robot/OntologyHelper.java b/robot-core/src/main/java/org/obolibrary/robot/OntologyHelper.java index 770a02c27..f6d96a716 100644 --- a/robot-core/src/main/java/org/obolibrary/robot/OntologyHelper.java +++ b/robot-core/src/main/java/org/obolibrary/robot/OntologyHelper.java @@ -1653,4 +1653,53 @@ public static void trimOntology(OWLOntology ontology) { Set axioms = RelatedObjectsHelper.getPartialAxioms(ontology, trimObjects, type); manager.removeAxioms(ontology, axioms); } + + /** + * Removes all of the axiom annotations for the given annotation properties. + * + * @param ontology OWLOntology to remove axiom annotations + * @param properties Annotation property IRIs to remove related axiom annotations. + */ + public static void removeAxiomAnnotations(OWLOntology ontology, List properties) { + OWLDataFactory owlDataFactory = ontology.getOWLOntologyManager().getOWLDataFactory(); + properties.stream() + .map(iri -> owlDataFactory.getOWLAnnotationProperty(iri)) + .forEach(p -> OntologyHelper.removeAxiomAnnotations(ontology, p)); + } + + /** + * Removes all of the axiom annotations for the given annotation property. + * + * @param ontology OWLOntology to remove axiom annotations + * @param property Annotation property to remove related axiom annotations. + */ + public static void removeAxiomAnnotations(OWLOntology ontology, OWLAnnotationProperty property) { + OWLOntologyManager manager = ontology.getOWLOntologyManager(); + for (OWLAxiom axiom : ontology.getAxioms(Imports.EXCLUDED)) { + Set annotationsToRemove = axiom.getAnnotations(property); + if (!annotationsToRemove.isEmpty()) { + Set axiomAnnotations = axiom.getAnnotations(); + axiomAnnotations.removeAll(annotationsToRemove); + OWLAxiom cleanedAxiom = + axiom.getAxiomWithoutAnnotations().getAnnotatedAxiom(axiomAnnotations); + + manager.removeAxiom(ontology, axiom); + manager.addAxiom(ontology, cleanedAxiom); + } + } + } + + /** + * Removes all axiom annotations from the given ontology. + * + * @param ontology OWLOntology to remove axiom annotations + */ + public static void removeAllAxiomAnnotations(OWLOntology ontology) { + OWLOntologyManager manager = ontology.getOWLOntologyManager(); + for (OWLAxiom axiom : ontology.getAxioms(Imports.EXCLUDED)) { + OWLAxiom cleanedAxiom = axiom.getAxiomWithoutAnnotations(); + manager.removeAxiom(ontology, axiom); + manager.addAxiom(ontology, cleanedAxiom); + } + } } diff --git a/robot-core/src/main/java/org/obolibrary/robot/RelatedObjectsHelper.java b/robot-core/src/main/java/org/obolibrary/robot/RelatedObjectsHelper.java index cbed28d43..106dbb472 100644 --- a/robot-core/src/main/java/org/obolibrary/robot/RelatedObjectsHelper.java +++ b/robot-core/src/main/java/org/obolibrary/robot/RelatedObjectsHelper.java @@ -2344,4 +2344,20 @@ private static void spanGapsHelper( } } } + + /** + * Drops axiom annotations from the given ontology which uses the given annotation properties. + * + * @param ontology OWLOntology to drop axiom annotations from + * @param annotationsToDrop list of annotation property IRIs + * @param dropParameters list of drop-axiom-annotations parameters + */ + public static void dropAxiomAnnotations( + OWLOntology ontology, List annotationsToDrop, List dropParameters) { + if (dropParameters.stream().anyMatch(x -> x.equalsIgnoreCase("all"))) { + OntologyHelper.removeAllAxiomAnnotations(ontology); + } else { + OntologyHelper.removeAxiomAnnotations(ontology, annotationsToDrop); + } + } }