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 validation framework as package an made accessible via minerva cmdline client #251

Merged
merged 6 commits into from
Oct 21, 2019
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
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jena.rdf.model.Model;
import org.apache.log4j.Logger;
import org.geneontology.minerva.BlazegraphMolecularModelManager;
import org.geneontology.minerva.GafToLegoIndividualTranslator;
Expand All @@ -25,6 +26,9 @@
import org.geneontology.minerva.legacy.sparql.GPADSPARQLExport;
import org.geneontology.minerva.lookup.ExternalLookupService;
import org.geneontology.minerva.util.BlazegraphMutationCounter;
import org.geneontology.minerva.validation.Enricher;
import org.geneontology.minerva.validation.ShexValidationReport;
import org.geneontology.minerva.validation.ShexValidator;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.UpdateExecutionException;
Expand All @@ -36,6 +40,10 @@
import org.semanticweb.owlapi.formats.TurtleDocumentFormat;
import org.semanticweb.owlapi.model.*;
import org.semanticweb.owlapi.reasoner.InconsistentOntologyException;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
import org.semanticweb.owlapi.reasoner.structural.StructuralReasonerFactory;

import owltools.cli.JsCommandRunner;
import owltools.cli.Opts;
import owltools.cli.tools.CLIMethod;
Expand All @@ -50,14 +58,15 @@
import uk.ac.manchester.cs.owlapi.modularity.SyntacticLocalityModuleExtractor;

import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.*;

public class MinervaCommandRunner extends JsCommandRunner {

private static final Logger LOGGER = Logger.getLogger(MinervaCommandRunner.class);

@CLIMethod("--dump-owl-models")
public void modelsToOWL(Opts opts) throws Exception {
opts.info("[-j|--journal JOURNALFILE] [-f|--folder OWLFILESFOLDER] [-p|--prefix MODELIDPREFIX]",
Expand Down Expand Up @@ -85,7 +94,7 @@ else if (opts.nextEq("-p|--prefix")) {
break;
}
}

// minimal inputs
if (journalFilePath == null) {
System.err.println("No journal file was configured.");
Expand All @@ -97,14 +106,14 @@ else if (opts.nextEq("-p|--prefix")) {
exit(-1);
return;
}

OWLOntology dummy = OWLManager.createOWLOntologyManager().createOntology(IRI.create("http://example.org/dummy"));
CurieHandler curieHandler = new MappedCurieHandler();
BlazegraphMolecularModelManager<Void> m3 = new BlazegraphMolecularModelManager<>(dummy, curieHandler, modelIdPrefix, journalFilePath, outputFolder);
m3.dumpAllStoredModels();
m3.dispose();
}

@CLIMethod("--import-owl-models")
public void importOWLModels(Opts opts) throws Exception {
opts.info("[-j|--journal JOURNALFILE] [-f|--folder OWLFILESFOLDER]",
Expand All @@ -127,7 +136,7 @@ else if (opts.nextEq("-f|--folder")) {
break;
}
}

// minimal inputs
if (journalFilePath == null) {
System.err.println("No journal file was configured.");
Expand All @@ -139,7 +148,7 @@ else if (opts.nextEq("-f|--folder")) {
exit(-1);
return;
}

OWLOntology dummy = OWLManager.createOWLOntologyManager().createOntology(IRI.create("http://example.org/dummy"));
String modelIdPrefix = "http://model.geneontology.org/"; // this will not be used for anything
CurieHandler curieHandler = new MappedCurieHandler();
Expand All @@ -150,7 +159,7 @@ else if (opts.nextEq("-f|--folder")) {
}
m3.dispose();
}

@CLIMethod("--sparql-update")
public void sparqlUpdate(Opts opts) throws OWLOntologyCreationException, IOException, RepositoryException, MalformedQueryException, UpdateExecutionException {
opts.info("[-j|--journal JOURNALFILE] [-f|--file SPARQL UPDATE FILE]",
Expand Down Expand Up @@ -233,7 +242,7 @@ else if (opts.nextEq("--compact-json")) {
break;
}
}

// minimal inputs
if (input == null) {
System.err.println("No input model was configured.");
Expand All @@ -245,15 +254,15 @@ else if (opts.nextEq("--compact-json")) {
exit(-1);
return;
}

// configuration
CurieHandler curieHandler = DefaultCurieHandler.getDefaultHandler();
GsonBuilder gsonBuilder = new GsonBuilder();
if (usePretty) {
gsonBuilder.setPrettyPrinting();
}
Gson gson = gsonBuilder.create();

// process each model
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Loading model from file: "+input);
Expand Down Expand Up @@ -330,7 +339,7 @@ else if (opts.nextEq("--skip-merge")) {
merge = false;
}
else if (opts.nextEq("-m|--minimize")) {
opts.info("", "use module extraction to include module of ontology");
opts.info("", "use module extraction to include module of ontology");
minimize = true;
}
else {
Expand All @@ -340,22 +349,22 @@ else if (opts.nextEq("-m|--minimize")) {
if (g != null && gafdoc != null && output != null) {
GafToLegoIndividualTranslator tr = new GafToLegoIndividualTranslator(g, curieHandler, addLineNumber);
OWLOntology lego = tr.translate(gafdoc);

if (merge) {
new OWLGraphWrapper(lego).mergeImportClosure(true);
}
if (minimize) {
final OWLOntologyManager m = lego.getOWLOntologyManager();

SyntacticLocalityModuleExtractor sme = new SyntacticLocalityModuleExtractor(m, lego, ModuleType.BOT);
Set<OWLEntity> sig = new HashSet<OWLEntity>(lego.getIndividualsInSignature());
Set<OWLAxiom> moduleAxioms = sme.extract(sig);

OWLOntology module = m.createOntology(IRI.generateDocumentIRI());
m.addAxioms(module, moduleAxioms);
lego = module;
}

OWLOntologyManager manager = lego.getOWLOntologyManager();
OutputStream outputStream = null;
try {
Expand All @@ -380,7 +389,124 @@ else if (opts.nextEq("-m|--minimize")) {
return;
}
}


/**
* Will test a go-cam model or directory of models against a shex schema and shape map to test conformance
* Example invocation: --validate-go-cams -s /Users/bgood/Documents/GitHub/GO_Shapes/shapes/go-cam-shapes.shex -m /Users/bgood/Documents/GitHub/GO_Shapes/shapes/go-cam-shapes.shapeMap -f /Users/bgood/Documents/GitHub/GO_Shapes/test_ttl/go_cams/should_pass/ -e -r /Users/bgood/Desktop/shapely_report.txt
* @param opts
* @throws Exception
*/
@CLIMethod("--validate-go-cams")
public void validateGoCams(Opts opts) throws Exception {
String url_for_tbox = "http://purl.obolibrary.org/obo/go/extensions/go-lego.owl";
ShexValidator validator = null;
String shexpath = null;//"../shapes/go-cam-shapes.shex";
String shapemappath = null;
String model_file = null;//"../test_ttl/go_cams/should_pass/typed_reactome-homosapiens-Acetylation.ttl";
String report_file = null;
boolean addSuperClasses = false;
boolean addSuperClassesLocal = false;
boolean travisMode = false;
boolean shouldPass = true;
String extra_endpoint = null;
Map<String, Model> name_model = new HashMap<String, Model>();

while (opts.hasOpts()) {
if(opts.nextEq("-travis")) {
travisMode = true;
}
else if (opts.nextEq("-f|--file")) {
model_file = opts.nextOpt();
name_model = Enricher.loadRDF(model_file);
}
else if (opts.nextEq("-shouldfail")) {
shouldPass = false;
}
else if (opts.nextEq("-s|--shexpath")) {
shexpath = opts.nextOpt();
}
else if(opts.nextEq("-m|--shapemap")) {
shapemappath = opts.nextOpt();
}
else if(opts.nextEq("-r|--report")) {
report_file = opts.nextOpt();
}
else if(opts.nextEq("-expand")) {
addSuperClasses = true;
}
else if(opts.nextEq("-el|--elocal")) {
addSuperClassesLocal = true;
}
else if(opts.nextEq("-extra_endpoint")) {
extra_endpoint = opts.nextOpt();
}else {
break;
}
}
//requirements
if(model_file==null) {
System.err.println("-f .No go-cam file or directory provided to validate.");
exit(-1);
}
else if(shexpath==null) {
System.err.println("-s .No shex schema provided.");
exit(-1);
}else if(shapemappath==null) {
System.err.println("-m .No shape map file provided.");
exit(-1);
}else if(report_file==null) {
System.err.println("-r .No report file specified");
exit(-1);
}
else {
validator = new ShexValidator(shexpath, shapemappath);
FileWriter w = new FileWriter(report_file);
int good = 0; int bad = 0;
Enricher enrich = new Enricher(extra_endpoint, null);
if(addSuperClassesLocal) {
URL tbox_location = new URL(url_for_tbox);
File tbox_file = new File("./target/go-lego.owl");
System.out.println("downloading tbox ontology from "+url_for_tbox);
org.apache.commons.io.FileUtils.copyURLToFile(tbox_location, tbox_file);
System.out.println("loading tbox ontology from "+tbox_file.getAbsolutePath());
OWLOntologyManager ontman = OWLManager.createOWLOntologyManager();
OWLOntology tbox = ontman.loadOntologyFromOntologyDocument(tbox_file);
System.out.println("done loading, building structural reasoner");
OWLReasonerFactory reasonerFactory = new StructuralReasonerFactory();
OWLReasoner tbox_reasoner = reasonerFactory.createReasoner(tbox);
enrich = new Enricher(null, tbox_reasoner);
}
for(String name : name_model.keySet()) {
Model test_model = name_model.get(name);
if(addSuperClasses||addSuperClassesLocal) {
test_model = enrich.enrichSuperClasses(test_model);
}
if(validator.GoQueryMap!=null){
boolean stream_output = false;
ShexValidationReport r = validator.runShapeMapValidation(test_model, stream_output);
System.out.println(name+" conformant:"+r.isConformant());
w.write(name+"\t");
if(!r.isConformant()) {
w.write("invalid\n");
bad++;
if(travisMode&&(shouldPass)) {
System.out.println(name+" should have validated but did not "+r.getAsText());
System.exit(-1);
}
}else {
good++;
w.write("valid\n");
if(travisMode&&(!shouldPass)) {
System.out.println(name+" should NOT have validated but did ");
System.exit(-1);
}
}
}
}
w.close();
System.out.println("input: "+model_file+" total:"+name_model.size()+" Good:"+good+" Bad:"+bad);
}
}
/**
* Output GPAD files via inference+SPARQL, for testing only
* @param opts
Expand Down Expand Up @@ -430,7 +556,7 @@ else if (opts.nextEq("--ontology")) {
}
m3.dispose();
}

@CLIMethod("--lego-to-gpad")
public void legoToAnnotations(Opts opts) throws Exception {
String modelIdPrefix = "http://model.geneontology.org/";
Expand Down Expand Up @@ -462,10 +588,10 @@ else if (opts.nextEq("--remove-lego-model-ids")) {
else if (opts.nextEq("--model-id-prefix")) {
modelIdPrefix = opts.nextOpt();
}
else if (opts.nextEq("-s|--input-file-suffix")) {
opts.info("SUFFIX", "if a directory is specified, use only files with this suffix. Default is 'ttl'");
fileSuffix = opts.nextOpt();
}
else if (opts.nextEq("-s|--input-file-suffix")) {
opts.info("SUFFIX", "if a directory is specified, use only files with this suffix. Default is 'ttl'");
fileSuffix = opts.nextOpt();
}
else if (opts.nextEq("--model-id-curie")) {
modelIdcurie = opts.nextOpt();
}
Expand All @@ -490,7 +616,7 @@ else if (opts.nextEq("--group-by-model-organisms")) {
}
}
}

}
}
else if (opts.nextEq("--add-model-organism-group")) {
Expand All @@ -511,7 +637,7 @@ else if (opts.nextEq("--set-default-model-group")) {
// create curie handler
CurieMappings localMappings = new CurieMappings.SimpleCurieMappings(Collections.singletonMap(modelIdcurie, modelIdPrefix));
CurieHandler curieHandler = new MappedCurieHandler(DefaultCurieHandler.loadDefaultMappings(), localMappings);

ExternalLookupService lookup= null;

SimpleEcoMapper mapper = EcoMapperFactory.createSimple();
Expand All @@ -525,17 +651,17 @@ else if (opts.nextEq("--set-default-model-group")) {
groupingTranslator.translate(model);
}
else if (inputFolder != null) {
final String fileSuffixFinal = fileSuffix;
File inputFile = new File(inputFolder).getCanonicalFile();
final String fileSuffixFinal = fileSuffix;
File inputFile = new File(inputFolder).getCanonicalFile();
if (inputFile.isDirectory()) {
File[] files = inputFile.listFiles(new FilenameFilter() {

@Override
public boolean accept(File dir, String name) {
if (fileSuffixFinal != null && fileSuffixFinal.length() > 0)
return name.endsWith("."+fileSuffixFinal);
else
return StringUtils.isAlphanumeric(name);
if (fileSuffixFinal != null && fileSuffixFinal.length() > 0)
return name.endsWith("."+fileSuffixFinal);
else
return StringUtils.isAlphanumeric(name);
}
});
for (File file : files) {
Expand All @@ -558,7 +684,7 @@ public boolean accept(File dir, String name) {
}
}
}

// by organism
if (taxonGroups != null) {
for(String group : groupingTranslator.getTaxonGroups()) {
Expand All @@ -572,7 +698,7 @@ public boolean accept(File dir, String name) {
}
}
}

// production only by organism
if (taxonGroups != null) {
for(String group : groupingTranslator.getProductionTaxonGroups()) {
Expand Down
17 changes: 17 additions & 0 deletions minerva-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,22 @@
<groupId>net.sf.trove4j</groupId>
<artifactId>trove4j</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
<version>2.28</version>
</dependency>
<dependency>
<groupId>fr.inria.lille.shexjava</groupId>
<artifactId>shexjava-core</artifactId>
<version>1.2.3c</version>
<!-- avoiding an incompatibility with the shexjava jena version 3.0.0 and the arachne version 3.11.0 -->
<exclusions>
<exclusion> <!-- declare the exclusion here -->
<groupId>org.apache.jena</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
Loading