/* * Date: February 10, 2011 Template: PluginScreenJavaTemplateGen.java.ftl * generator: org.molgenis.generators.ui.PluginScreenJavaTemplateGen 3.3.3 * * THIS FILE IS A TEMPLATE. PLEASE EDIT :-) */ package plugins.xgapwizard; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import matrix.DataMatrixInstance; import matrix.general.DataMatrixHandler; import matrix.implementations.binary.BinaryDataMatrixWriter; import org.apache.commons.io.FileUtils; import org.molgenis.cluster.DataName; import org.molgenis.cluster.DataSet; import org.molgenis.cluster.DataValue; import org.molgenis.core.OntologyTerm; import org.molgenis.data.Data; import org.molgenis.framework.db.Database; import org.molgenis.framework.db.DatabaseException; import org.molgenis.framework.db.Query; import org.molgenis.framework.db.QueryRule; import org.molgenis.framework.db.QueryRule.Operator; import org.molgenis.framework.ui.PluginModel; import org.molgenis.framework.ui.ScreenController; import org.molgenis.framework.ui.ScreenMessage; import org.molgenis.organization.Investigation; import org.molgenis.pheno.Individual; import org.molgenis.pheno.ObservableFeature; import org.molgenis.util.CsvFileReader; import org.molgenis.util.Tuple; import org.molgenis.xgap.Chromosome; import org.molgenis.xgap.Marker; public class QTLDataSetWizard extends PluginModel { private static final long serialVersionUID = -1810993111211947419L; // if db rollbacks, delete this matrix file! private File dataFileRollback = null; private QTLDataSetWizardModel model = new QTLDataSetWizardModel(); public QTLDataSetWizardModel getMyModel() { return model; } @Override public String getViewName() { return "plugins_xgapwizard_QTLDataSetWizard"; } @Override public String getViewTemplate() { return "plugins/xgapwizard/QTLDataSetWizard.ftl"; } public QTLDataSetWizard(String name, ScreenController<?> parent) { super(name, parent); } /** * Handlerequest. Handles 4 inputs: select investigation, upload geno, * upload pheno, and upload map. Calls helper functions accordingly. */ @Override public void handleRequest(Database db, Tuple request) { try { if (request.getInt("invSelect") != null) { this.model.setSelectedInv(request.getInt("invSelect")); } String action = request.getString("__action"); if (action.equals("uploadGeno")) { uploadData("Geno", request, db); } else if (action.equals("uploadPheno")) { uploadData("Pheno", request, db); } else if (action.equals("uploadMap")) { File mapFile = request.getFile("mapFile"); if (mapFile == null) { throw new FileNotFoundException("No file selected"); } parseMapAndAddToDb(mapFile, db, request.getInt("invSelect")); this.setMessages(new ScreenMessage("Map file parsed and markers/chromosomes added to database", true)); } else { throw new Exception("Unknown request action: " + action); } } catch (Exception e) { if (db.inTx()) { try { // should never be null if (dataFileRollback != null) { // should exist if (dataFileRollback.exists()) { FileUtils.forceDelete(dataFileRollback); } } db.rollbackTx(); } catch (Exception e1) { e1.printStackTrace(); this.setMessages(new ScreenMessage(e.getMessage() != null ? e1.getMessage() : "null", false)); } } e.printStackTrace(); this.setMessages(new ScreenMessage(e.getMessage() != null ? e.getMessage() : "null", false)); } } /** * Reload. Get a fresh list of investigation and print a message if this * fails */ @Override public void reload(Database db) { try { List<Investigation> invList = db.find(Investigation.class); this.model.setInvestigations(invList); // FIXME: hardcoded for now, must be replaced with combination of // Metadb and enum in Data.. (pick out the ObservableFeatures) // if possible? may not have other required fields than Name.. List<String> xof = new ArrayList<String>(); // xof.add("Chromosome"); needs other required xof.add("Measurement"); xof.add("DerivedTrait"); xof.add("EnvironmentalFactor"); xof.add("Gene"); xof.add("Marker"); xof.add("MassPeak"); xof.add("Metabolite"); xof.add("Probe"); // xof.add("Spot"); needs other required this.model.setXqtlObservableFeatureTypes(xof); List<OntologyTerm> crosses = db.find(OntologyTerm.class, new QueryRule(OntologyTerm.NAME, Operator.LIKE, "xgap_rqtl_straintype_")); this.model.setCrosses(crosses); } catch (Exception e) { e.printStackTrace(); this.setMessages(new ScreenMessage(e.getMessage() != null ? e.getMessage() : "null", false)); } } /** * Helper funtion to upload either Genotypes or Phenotypes ('type' = "Geno" * or "Pheno" to the database) from a file into a datamatrix * * @param type * @param request * @param db * @throws Exception */ private void uploadData(String type, Tuple request, Database db) throws Exception { File file = request.getFile(type + "File"); if (file == null) { throw new FileNotFoundException("No file selected"); } // make 'Data' set for this genotype matrix String originalFileName = request.getString(type + "FileOriginalfilename").substring(0, request.getString(type + "FileOriginalfilename").indexOf(".")); int invSelect = request.getInt("invSelect"); Data data = null; if (type.equals("Geno")) { data = makeGenoData(db, originalFileName, invSelect); } if (type.equals("Pheno")) { data = makePhenoData(db, originalFileName, invSelect); } db.beginTx(); // add data definition db.add(data); // make binary matrix and upload into db new BinaryDataMatrixWriter(data, file, db); // tag matrix as Rqtl_data -> genotypes tagMatrix("Rqtl_data", type.toLowerCase() + "types", data, db); // create an instance of this matrix DataMatrixHandler handler = new DataMatrixHandler(db); DataMatrixInstance instance = handler.createInstance(data, db); dataFileRollback = handler.findSourceFile(data, db); if (type.equals("Geno")) { // add missing individuals with this cross type addMissingIndividuals(instance, db, request.getString("cross"), invSelect); } if (type.equals("Pheno")) { // add missing traits of this type String traitType = request.getString("trait"); boolean traitsAdded = addMissingTraits(instance, db, traitType, invSelect); if (traitsAdded) { data.setTargetType(traitType); db.update(data); } } db.commitTx(); dataFileRollback = null; this.setMessages(new ScreenMessage(type + "types succesfully uploaded as '" + data.getName() + "' and tagged for QTL analysis", true)); } /** * Add missing traits of this type if needed. Naive and straight * implementation, similar to addMissingIndividuals * * @param instance * @param db * @param type * @param invId * @throws DatabaseException * @throws IllegalAccessException * @throws InstantiationException */ private boolean addMissingTraits(DataMatrixInstance instance, Database db, String type, int invId) throws DatabaseException, InstantiationException, IllegalAccessException { List<String> traitsNeeded = instance.getRowNames(); Class traitClass = db.getClassForName(type); List<ObservableFeature> traitsInDb = db.find(ObservableFeature.class, new QueryRule(ObservableFeature.NAME, Operator.IN, traitsNeeded)); for (ObservableFeature traitInDb : traitsInDb) { traitsNeeded.remove(traitsNeeded.indexOf(traitInDb.getName())); } List<ObservableFeature> addThese = new ArrayList<ObservableFeature>(); for (String traitNeeded : traitsNeeded) { ObservableFeature missingTrait = (ObservableFeature) traitClass.newInstance(); missingTrait.setInvestigation(invId); missingTrait.setName(traitNeeded); addThese.add(missingTrait); } db.add(addThese); if (addThese.size() > 0) { return true; } else { return false; } } /** * Add missing individuals of this cross type if needed. Naive: only looks * for Individuals, not other ObservationTargets. Assumes the individuals * are on the columns. Assumes your list of input Individuals has no * duplicates, good names, etc. Assumes the cross exists as a uniquely named * ontologyterm. * * @param instance * @param db * @param cross * @throws DatabaseException */ private void addMissingIndividuals(DataMatrixInstance instance, Database db, String cross, int invId) throws DatabaseException { List<String> indvNamesNeeded = instance.getColNames(); List<Individual> indInDb = db.find(Individual.class, new QueryRule(Individual.NAME, Operator.IN, indvNamesNeeded)); OntologyTerm crossType = db.find(OntologyTerm.class, new QueryRule(OntologyTerm.ID, Operator.EQUALS, cross)) .get(0); for (Individual inDb : indInDb) { indvNamesNeeded.remove(indvNamesNeeded.indexOf(inDb.getName())); } List<Individual> addThese = new ArrayList<Individual>(); for (String indName : indvNamesNeeded) { Individual addMe = new Individual(); addMe.setName(indName); addMe.setInvestigation(invId); addMe.setOntologyReference(crossType); addThese.add(addMe); } db.add(addThese); } /** * Helper function to parse a 'map' file and add the needed * markers/chromosomes to the database * * @param mapFile * @param db * @param invId * @throws Exception */ private void parseMapAndAddToDb(File mapFile, Database db, int invId) throws Exception { // required columns that we expect in the file String[] required = new String[] { "name", "chr", "cm" }; // instantiate CsvFileReader to handle the file CsvFileReader csvFile = new CsvFileReader(mapFile); List<String> colNames = csvFile.colnames(); // do checks if all column names are in order if (colNames.size() != required.length) { throw new Exception("Your files has " + colNames.size() + " columns, this needs to be " + required.length); } for (String req : required) { if (!colNames.contains(req)) { throw new Exception("Missing column: " + req); } } // get the values from the CSV final List<Tuple> values = new ArrayList<Tuple>(); for (Tuple tuple : csvFile) { values.add(tuple); } // get the selected investigation Investigation inv = db.find(Investigation.class, new QueryRule("id", Operator.EQUALS, invId)).get(0); List<String> markersInDb = getMarkerNamesFromDb(db, inv.getId()); HashMap<String, Chromosome> chromoInDb = getChromosomesFromDb(db, inv.getId()); // iterate over tuples then convert to markers and chromosomes // needed for the import don't add markers that already exist! // (=cm info) but do map to existing chromosomes (=no further // info) List<Marker> markers = new ArrayList<Marker>(); List<String> markerNames = new ArrayList<String>(); // List<Chromosome> chromo = new ArrayList<Chromosome>(); // List<String> chromoNames = new ArrayList<String>(); HashMap<String, Chromosome> chromoToBeAdded = new HashMap<String, Chromosome>(); for (Tuple t : values) { // get the values in their correct type String name = t.getString("name"); int chr = t.getInt("chr"); double cm = t.getDouble("cm"); // CHROMOSOMES // get or make chromosome object Chromosome chromosome = null; String chromoName = "chr" + chr; if (chromoInDb.keySet().contains(chromoName)) { // get existing chromosome, but check if the ordernumber // matches! chromosome = chromoInDb.get(chromoName); if (chromosome.getOrderNr().intValue() != chr) { throw new Exception("Trying use existing chromosome 'chr" + chr + "' for input '" + chr + "', but the ordernumbers (" + chromosome.getOrderNr().intValue() + " vs. " + chr + ") are different! Change the annotation or upload an updated file."); } } else { // if not exist, make new one and add to list of names to // 'remember' and only add once to list of chromo needed in db if (!chromoToBeAdded.keySet().contains(chromoName)) { chromosome = new Chromosome(); chromosome.setName(chromoName); chromosome.setOrderNr(chr); chromosome.setIsAutosomal(true); chromosome.setInvestigation(inv); chromoToBeAdded.put(chromoName, chromosome); // add chromo to db right now, so the ID of the object is // set! db.add(chromosome); } else { // if we made one already, refer to this one chromosome = chromoToBeAdded.get(chromoName); } } // MARKERS // check for duplicates in the list if (markerNames.contains(name)) { throw new Exception("Duplicate marker name '" + name + "' in your map"); } else { markerNames.add(name); } // check for duplicates in db if (markersInDb.contains(name)) { throw new Exception("There is already a marker named '" + name + "' present in this investigation"); } // create new marker and add to list Marker m = new Marker(); m.setInvestigation(inv); m.setName(name); m.setChromosome(chromosome); m.setCM(cm); // add to list markers.add(m); } // add markers to database db.add(markers); } /** * Helper funtion to get a map of name-object for all Chromosomes for this * investigation * * @param db * @param investigationId * @return * @throws DatabaseException */ private HashMap<String, Chromosome> getChromosomesFromDb(Database db, int investigationId) throws DatabaseException { QueryRule q = new QueryRule("investigation", Operator.EQUALS, investigationId); List<Chromosome> chromo = db.find(Chromosome.class, q); HashMap<String, Chromosome> chromoHash = new HashMap<String, Chromosome>(); for (Chromosome c : chromo) { chromoHash.put(c.getName(), c); } return chromoHash; } /** * Helper function to get the marker names from the database * * @param db * @param investigationId * @return * @throws DatabaseException */ private List<String> getMarkerNamesFromDb(Database db, int investigationId) throws DatabaseException { QueryRule q = new QueryRule("investigation", Operator.EQUALS, investigationId); List<Marker> markers = db.find(Marker.class, q); List<String> names = new ArrayList<String>(); for (Marker m : markers) { names.add(m.getName()); } return names; } /** * Helper function to create a phenotype 'Data' set object * * @param db * @param originalFileName * @param invSelect * @return * @throws DatabaseException * @throws IOException * @throws ParseException */ private Data makePhenoData(Database db, String originalFileName, int invSelect) throws DatabaseException, IOException, ParseException { Data phenoData = new Data(); OntologyTerm onto = db.find(OntologyTerm.class, new QueryRule(OntologyTerm.NAME, Operator.EQUALS, "phenotype_matrix")).get(0); if (onto == null) { onto = new OntologyTerm(); onto.setName("phenotype_matrix"); onto.setDefinition("Phenotypes"); db.add(onto); } // phenoData.setFeature(feature); // phenoData.setTarget(feature); phenoData.setOntologyReference(onto); phenoData.setName(originalFileName); phenoData.setFeatureType("Individual"); phenoData.setTargetType("ClassicalPhenotype"); phenoData.setValueType("Decimal"); phenoData.setStorage("Binary"); Investigation inv = db.find(Investigation.class, new QueryRule("id", Operator.EQUALS, invSelect)).get(0); phenoData.setInvestigation(inv); phenoData.setInvestigation_Name(inv.getName()); return phenoData; } /** * Helper function to create a genotype 'Data' set object * * @param db * @param originalFileName * @param invSelect * @return * @throws DatabaseException * @throws IOException * @throws ParseException */ private Data makeGenoData(Database db, String originalFileName, int invSelect) throws DatabaseException, IOException, ParseException { Data genoData = new Data(); OntologyTerm onto = db.find(OntologyTerm.class, new QueryRule(OntologyTerm.NAME, Operator.EQUALS, "genotype_matrix")).get(0); if (onto == null) { onto = new OntologyTerm(); onto.setName("genotype_matrix"); onto.setDefinition("Genotypes"); db.add(onto); } // genoData.setFeature(feature); // genoData.setTarget(feature); genoData.setOntologyReference(onto); genoData.setName(originalFileName); genoData.setFeatureType("Individual"); genoData.setTargetType("Marker"); genoData.setValueType("Text"); genoData.setStorage("Binary"); Investigation inv = Investigation.findById(db, invSelect); genoData.setInvestigation(inv); genoData.setInvestigation_Name(inv.getName()); return genoData; } /** * Helper funtion to tag an existing datamatrix with a specific dataValue, * eg Rqtl genotypes * * @param dataSet * @param dataName * @param dataValue * @param db * @throws DatabaseException * @throws ParseException * @throws IOException */ private void tagMatrix(String dataSet, String dataName, Data dataValue, Database db) throws DatabaseException, ParseException, IOException { List<DataSet> dsRefList = db.find(DataSet.class, new QueryRule("name", Operator.EQUALS, dataSet)); DataSet dsRef = null; if (dsRefList.size() == 0) { dsRef = new DataSet(); dsRef.setName(dataSet); db.add(dsRef); } else { dsRef = dsRefList.get(0); } Query<DataName> q = db.query(DataName.class); q.addRules(new QueryRule("name", Operator.EQUALS, dataName)); q.addRules(new QueryRule("dataset", Operator.EQUALS, dsRef.getId())); List<DataName> dnRefList = q.find(); DataName dnRef = null; if (dnRefList.size() == 0) { dnRef = new DataName(); dnRef.setName(dataName); dnRef.setDataSet(dsRef); db.add(dnRef); } else { dnRef = dnRefList.get(0); } DataValue dv = new DataValue(); dv.setDataName(dnRef); dv.setValue(dataValue); dv.setName(dataValue.getInvestigation_Name() + "_" + dataValue.getName()); db.add(dv); } }