diff --git a/docs/mint.md b/docs/mint.md new file mode 100644 index 000000000..804a1a1b0 --- /dev/null +++ b/docs/mint.md @@ -0,0 +1,30 @@ +# Mint + +ROBOT can mint sequential numeric identifiers for temporary IRIS. For example, in a decentralized development workflow, +curators may submit pull requests creating new terms using UUID-based identifiers, such as: + +- `http://purl.obolibrary.org/temp#65969F4B-4050-4AB9-9EE4-6E136A7A335F` +- `http://purl.obolibrary.org/temp#829D9A75-572B-4F79-9A52-F74F53FDD687` + +To replace occurrences of these IRIs with IRIs following a given ontology identifier scheme, you can run a command like this: + + robot mint \ + --input needs-minting.owl \ + --output results/minted.owl + --minted-id-prefix "http://purl.obolibrary.org/obo/EXAMPLE_" + --temp-id-prefix "http://purl.obolibrary.org/temp#" + --minted-from-property "http://purl.obolibrary.org/obo/OMO_mintedfrom" + +If, for example, the ontology already contains a term with IRI `http://purl.obolibrary.org/obo/EXAMPLE_0000004`, +this will result in all occurrences of the above IRIs being replaced with these corresponding IRIs: + +- `http://purl.obolibrary.org/obo/EXAMPLE_0000005` +- `http://purl.obolibrary.org/obo/EXAMPLE_0000006` + +`mint` provides some additional options to modify identifier generation: + +- `--pad-width`: apply leading zeroes to minted identifiers up to this width (default `7`) +- `--min-id`: start minted identifiers from the max of either this number or the highest identifier found which is less than or equal to `max-id` (default `0`) +- `--max-id`: fail the operation if no identifier can be minted less than or equal to this number (default `Integer.MAX_VALUE`) +- `--keep-deprecated `: whether to keep temporary terms in the ontology as deprecated entities (default `false`). + Deprecated temporary entities are linked to the minted replacement term with a [term_replaced_by](http://purl.obolibrary.org/obo/IAO_0100001) annotation. diff --git a/robot-command/src/main/java/org/obolibrary/robot/CommandLineInterface.java b/robot-command/src/main/java/org/obolibrary/robot/CommandLineInterface.java index 1b935c87d..2bb038c41 100644 --- a/robot-command/src/main/java/org/obolibrary/robot/CommandLineInterface.java +++ b/robot-command/src/main/java/org/obolibrary/robot/CommandLineInterface.java @@ -30,6 +30,7 @@ private static CommandManager initManager() { m.addCommand("filter", new FilterCommand()); m.addCommand("materialize", new MaterializeCommand()); m.addCommand("merge", new MergeCommand()); + m.addCommand("mint", new MintCommand()); m.addCommand("mirror", new MirrorCommand()); m.addCommand("python", new PythonCommand()); m.addCommand("query", new QueryCommand()); diff --git a/robot-command/src/main/java/org/obolibrary/robot/MintCommand.java b/robot-command/src/main/java/org/obolibrary/robot/MintCommand.java new file mode 100644 index 000000000..6651eb85d --- /dev/null +++ b/robot-command/src/main/java/org/obolibrary/robot/MintCommand.java @@ -0,0 +1,128 @@ +package org.obolibrary.robot; + +import java.io.IOException; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Options; +import org.semanticweb.owlapi.apibinding.OWLManager; +import org.semanticweb.owlapi.model.IRI; +import org.semanticweb.owlapi.model.OWLOntology; + +public class MintCommand implements Command { + + /** Store the command-line options for the command. */ + private Options options; + + public MintCommand() { + Options o = CommandLineHelper.getCommonOptions(); + o.addOption("i", "input", true, "load ontology from a file"); + o.addOption("I", "input-iri", true, "load ontology from an IRI"); + o.addOption("o", "output", true, "save processed ontology to a file"); + o.addOption(null, "minted-id-prefix", true, "IRI prefix to prepend to minted identifiers"); + o.addOption( + null, + "minted-from-property", + true, + "property IRI used to link minted identifiers to temporary identifiers"); + o.addOption(null, "temp-id-prefix", true, "IRI prefix indicating temporary identifiers"); + o.addOption( + null, + "min-id", + true, + "start minted identifiers from the max of either this number or the highest identifier found which is less than or equal to max-id"); + o.addOption( + null, + "max-id", + true, + "fail if no identifier can be minted less than or equal to this number"); + o.addOption( + null, "pad-width", true, "apply leading zeroes to minted identifiers up to this width"); + o.addOption( + null, + "keep-deprecated", + true, + "keep temporary terms in the ontology as deprecated entities"); + options = o; + } + + @Override + public String getName() { + return "mint"; + } + + @Override + public String getDescription() { + return "replace temporary identifiers with newly minted official identifiers"; + } + + @Override + public String getUsage() { + return "robot mint --input --minted-id-prefix --temp-id-prefix --minted-from-property --min-id --max-id --pad-width --keep-deprecated --output "; + } + + @Override + public Options getOptions() { + return options; + } + + @Override + public void main(String[] args) { + try { + execute(null, args); + } catch (Exception e) { + CommandLineHelper.handleException(e); + } + } + + @Override + public CommandState execute(CommandState state, String[] args) throws Exception { + CommandLine line = CommandLineHelper.getCommandLine(getUsage(), getOptions(), args); + if (line == null) { + return null; + } + IOHelper ioHelper = CommandLineHelper.getIOHelper(line); + if (state == null) { + state = new CommandState(); + } + state = CommandLineHelper.updateInputOntology(ioHelper, state, line); + OWLOntology ontology = state.getOntology(); + + MintOperation.MintConfig config = parseConfig(line); + MintOperation.mintIdentifiers(ontology, config); + + CommandLineHelper.maybeSaveOutput(line, ontology); + state.setOntology(ontology); + return state; + } + + private MintOperation.MintConfig parseConfig(CommandLine line) + throws IOException, NumberFormatException { + IOHelper ioHelper = CommandLineHelper.getIOHelper(line); + MintOperation.MintConfig config = new MintOperation.MintConfig(); + config.setMintedIDPrefix( + CommandLineHelper.getDefaultValue(line, "minted-id-prefix", config.getMintedIDPrefix())); + config.setTempIDPrefix( + CommandLineHelper.getDefaultValue(line, "temp-id-prefix", config.getTempIDPrefix())); + String propertyID = CommandLineHelper.getDefaultValue(line, "minted-from-property", ""); + if (!propertyID.isEmpty()) { + IRI propertyIRI = + CommandLineHelper.maybeCreateIRI(ioHelper, propertyID, "minted-from-property"); + config.setMintedFromProperty( + OWLManager.getOWLDataFactory().getOWLAnnotationProperty(propertyIRI)); + } + String minID = CommandLineHelper.getDefaultValue(line, "min-id", ""); + if (!minID.isEmpty()) { + config.setMinIdentifier(Integer.parseInt(minID)); + } + String maxID = CommandLineHelper.getDefaultValue(line, "max-id", ""); + if (!maxID.isEmpty()) { + config.setMaxIdentifier(Integer.parseInt(maxID)); + } + String padding = CommandLineHelper.getDefaultValue(line, "pad-width", ""); + if (!padding.isEmpty()) { + config.setPadWidth(Integer.parseInt(padding)); + } + config.setKeepDeprecated( + CommandLineHelper.getBooleanValue(line, "keep-deprecated", config.isKeepDeprecated())); + return config; + } +} diff --git a/robot-core/src/main/java/org/obolibrary/robot/MintOperation.java b/robot-core/src/main/java/org/obolibrary/robot/MintOperation.java new file mode 100644 index 000000000..be194f8f5 --- /dev/null +++ b/robot-core/src/main/java/org/obolibrary/robot/MintOperation.java @@ -0,0 +1,82 @@ +package org.obolibrary.robot; + +import org.semanticweb.owlapi.apibinding.OWLManager; +import org.semanticweb.owlapi.model.IRI; +import org.semanticweb.owlapi.model.OWLAnnotationProperty; +import org.semanticweb.owlapi.model.OWLOntology; + +public class MintOperation { + + public static class MintConfig { + + private String mintedIDPrefix = "http://purl.obolibrary.org/obo/EXAMPLE_"; + private String tempIDPrefix = "http://purl.obolibrary.org/temp#"; + private OWLAnnotationProperty mintedFromProperty = + OWLManager.getOWLDataFactory() + .getOWLAnnotationProperty(IRI.create("http://purl.obolibrary.org/obo/OMO_mintedfrom")); + private int minIdentifier = 0; + private int maxIdentifier = Integer.MAX_VALUE; + private int padWidth = 7; + private boolean keepDeprecated = false; + + public String getMintedIDPrefix() { + return mintedIDPrefix; + } + + public void setMintedIDPrefix(String mintedIDPrefix) { + this.mintedIDPrefix = mintedIDPrefix; + } + + public String getTempIDPrefix() { + return tempIDPrefix; + } + + public void setTempIDPrefix(String tempIDPrefix) { + this.tempIDPrefix = tempIDPrefix; + } + + public OWLAnnotationProperty getMintedFromProperty() { + return mintedFromProperty; + } + + public void setMintedFromProperty(OWLAnnotationProperty mintedFromProperty) { + this.mintedFromProperty = mintedFromProperty; + } + + public int getMinIdentifier() { + return minIdentifier; + } + + public void setMinIdentifier(int minIdentifier) { + this.minIdentifier = minIdentifier; + } + + public int getMaxIdentifier() { + return maxIdentifier; + } + + public void setMaxIdentifier(int maxIdentifier) { + this.maxIdentifier = maxIdentifier; + } + + public int getPadWidth() { + return padWidth; + } + + public void setPadWidth(int padWidth) { + this.padWidth = padWidth; + } + + public boolean isKeepDeprecated() { + return keepDeprecated; + } + + public void setKeepDeprecated(boolean keepDeprecated) { + this.keepDeprecated = keepDeprecated; + } + } + + public static void mintIdentifiers(OWLOntology ontology, MintConfig config) { + // TODO + } +}