/* Date: February 2, 2010 * Template: PluginScreenJavaTemplateGen.java.ftl * generator: org.molgenis.generators.ui.PluginScreenJavaTemplateGen 3.3.2-testing * * THIS FILE IS A TEMPLATE. PLEASE EDIT :-) */ package plugins.qtlfinder; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.TreeMap; import matrix.DataMatrixInstance; import matrix.general.DataMatrixHandler; import org.molgenis.data.Data; import org.molgenis.framework.db.Database; import org.molgenis.framework.db.DatabaseException; 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.model.elements.Field; import org.molgenis.pheno.ObservableFeature; import org.molgenis.util.Entity; import org.molgenis.util.Tuple; import org.molgenis.xgap.Gene; import org.molgenis.xgap.Locus; import org.molgenis.xgap.Marker; import org.molgenis.xgap.Probe; import plugins.reportbuilder.Statistics; import plugins.rplot.MakeRPlot; public class QtlFinder extends PluginModel<Entity> { private static final long serialVersionUID = 1L; private QtlFinderModel model = new QtlFinderModel(); // private DataMatrixHandler dmh = null; private int plotWidth = 1024; private int plotHeight = 768; public QtlFinderModel getMyModel() { return model; } public QtlFinder(String name, ScreenController<?> parent) { super(name, parent); } @Override public String getViewName() { return "QtlFinder"; } @Override public String getViewTemplate() { return "plugins/qtlfinder/QtlFinder.ftl"; } private List<String> getInput(String query) throws Exception { // refresh results this.model.setResultSet(new HashMap<String, Result>()); this.model.setQmpr(null); // works for unix and windows // (http://stackoverflow.com/questions/454908/split-java-string-by-new-line) String lines[] = query.split("\\r?\\n"); List<String> uniqueInputs = new ArrayList<String>(); // checks for (String findMe : lines) { findMe = findMe.trim(); if (findMe.equals("")) { throw new Exception("Empty input line is not allowed"); } if (!uniqueInputs.contains(findMe)) { uniqueInputs.add(findMe); } else { throw new Exception("Non unique input string '" + findMe + "' not allowed"); } } return uniqueInputs; } public void handleRequest(Database db, Tuple request) { if (request.getString("__action") != null) { String action = request.getString("__action"); try { // on all actions: set threshold, query and data filter Double threshold = request.getDouble("threshold"); this.model.setThreshold(threshold); String query = request.getString("findme"); this.model.setQuery(query); QueryRule dataFilter = makeDataFilter(request); // also sets // tickboxes // NEW: multiple QTL's in a single plot if (action.equals("findQtlMulti")) { // get inputs from box List<String> uniqueInputs = getInput(query); // match all inputs to database and create 1 plot with // everything List<Entity> entities = new ArrayList<Entity>(); for (String findMe : uniqueInputs) { entities.addAll(findInGenesAndProbes(findMe, db)); } createQTLMultiPlotReportFor(entities, threshold, dataFilter, query, db); } // old style: 1 QTL per plot if (action.equals("findQtl")) { // get inputs from box List<String> uniqueInputs = getInput(query); // for each input, create a QTL report or disambiguation // view for (String findMe : uniqueInputs) { Result r = new Result(); r.setSelectedName(findMe); List<Entity> result = findInGenesAndProbes(findMe, db); if (result.size() == 0) { r.setNoResultsFound(true); } else if (result.size() == 1) { r.setResult(result.get(0)); List<QTLInfo> qtls = createQTLReportFor(result.get(0), threshold, dataFilter, db); r.setQtlsFound(qtls); } else { r.setDisambiguate(result); } this.model.getResultSet().put(findMe, r); } } else if (action.equals("disambig")) { // disambiguate one resultset, but with multi input String key = request.getString("__key"); // which resultset // name and type in a map // obtained by splitting input for '@', e.g. // 'disambig_option_Y37E31@Gene' // OK because '@' are not allowed in names HashMap<String, String> chosenItems = new HashMap<String, String>(); List<String> reqFields = request.getFields(); for (String s : reqFields) { if (s.startsWith("disambig_option_")) { String[] split = s.split("@"); chosenItems.put(split[0].substring("disambig_option_".length()), split[1]); } } // disambiguate terms based on name and type, create all // reports and results for (String name : chosenItems.keySet()) { Result rs = new Result(); rs.setSelectedName(name); Class<? extends Entity> entityClass = db.getClassForName(chosenItems.get(name)); List<? extends Entity> result = db.find(entityClass, new QueryRule(ObservableFeature.NAME, Operator.EQUALS, name)); rs.setResult(result.get(0)); List<QTLInfo> qtls = createQTLReportFor(result.get(0), threshold, dataFilter, db); rs.setQtlsFound(qtls); this.model.getResultSet().put(name, rs); } // remove the old ambiguous set this.model.getResultSet().remove(key); } } catch (Exception e) { e.printStackTrace(); this.setMessages(new ScreenMessage(e.getMessage() != null ? e.getMessage() : "null", false)); } } } private QueryRule makeDataFilter(Tuple request) { ArrayList<Integer> ids = new ArrayList<Integer>(); ArrayList<String> names = new ArrayList<String>(); for (Data d : this.model.getDataSets()) { if (request.getString("dataset_filter_" + d.getId()) != null) { ids.add(d.getId()); names.add(d.getName()); } } if (ids.size() > 0) { this.model.setTickedDataSets(names); return new QueryRule(Data.ID, Operator.IN, ids); } else { return null; } } private List<QTLInfo> createQTLReportFor(Entity entity, Double threshold, QueryRule dataFilter, Database db) throws Exception { List<QTLInfo> result = new ArrayList<QTLInfo>(); List<Data> allData; if (dataFilter == null) { allData = db.find(Data.class); } else { allData = db.find(Data.class, dataFilter); } DataMatrixHandler dmh = new DataMatrixHandler(db); // List<MatrixLocation> matrixLocations = new // ArrayList<MatrixLocation>(); for (Data d : allData) { // if something fails with this matrix, don't break the loop // e.g. backend file is missing try { // loop over Text data (can't be QTL) if (d.getValueType().equals("Text")) { continue; } // match row/col type of the matrix to the type of entity // queried // and one of the dimensions is Marker if ((d.getTargetType().equals(entity.get(Field.TYPE_FIELD)) || d.getFeatureType().equals( entity.get(Field.TYPE_FIELD))) && (d.getTargetType().equals("Marker") || d.getFeatureType().equals("Marker"))) { // create instance and get name of the row/col we want DataMatrixInstance instance = dmh.createInstance(d, db); String name = entity.get(ObservableFeature.NAME).toString(); long locus; if (entity instanceof Locus) { locus = ((Locus) entity).getBpStart(); } else { locus = 0; } // find out if the name is in the row or col names List<String> rowNames = instance.getRowNames(); List<String> colNames = instance.getColNames(); int rowIndex = rowNames.indexOf(name); int colIndex = colNames.indexOf(name); // if its in row, do row stuff if (rowIndex != -1) { Double[] Dvalues = Statistics.getAsDoubles(instance.getRow(rowIndex)); int maxIndex = Statistics.getIndexOfMax(Dvalues); double peakDouble = Dvalues[maxIndex]; if (threshold != null && peakDouble < threshold.doubleValue()) { continue; } String peakMarker = colNames.get(maxIndex); List<Double> DvaluesList = Arrays.asList(Dvalues); QTLInfo qtl = new QTLInfo(d, peakMarker, peakDouble, colNames, DvaluesList); HashMap<String, Marker> markerInfo = getMarkerInfo(colNames, db); qtl.setMarkerAnnotations(markerInfo); try { File img; // find out if we can do QTL plot // RIGHT NOW THIS IS ALWAYS TRUE // MISSING VALUES ARE 'HANDLED' BY THE QTL PLOT if (qtlInformationIsComplete(markerInfo, colNames)) { TreeMap<Long, QtlPlotDataPoint> data = sortQtlPlotData(colNames, DvaluesList, markerInfo); // img = MakeRPlot.qtlPlot(name, data, locus, // plotWidth, plotHeight, "LOD score", "qtl"); } else { img = MakeRPlot.plot(d, instance, name, null, "row", "o", plotWidth, plotHeight); } // qtl.setPlot(img.getName()); } catch (Exception e) { e.printStackTrace(); // too bad, image failed } result.add(qtl); } // if its in col, and not in row, do col stuff // we assume its not in row and col at the same time, then // its not QTL data but correlations or so if (rowIndex == -1 && colIndex != -1) { Double[] Dvalues = Statistics.getAsDoubles(instance.getCol(colIndex)); int maxIndex = Statistics.getIndexOfMax(Dvalues); double peakDouble = Dvalues[maxIndex]; if (threshold != null && peakDouble < threshold.doubleValue()) { continue; } String peakMarker = rowNames.get(maxIndex); List<Double> DvaluesList = Arrays.asList(Dvalues); QTLInfo qtl = new QTLInfo(d, peakMarker, peakDouble, rowNames, DvaluesList); HashMap<String, Marker> markerInfo = getMarkerInfo(rowNames, db); qtl.setMarkerAnnotations(markerInfo); try { File img; // find out if we can do QTL plot // RIGHT NOW THIS IS ALWAYS TRUE // MISSING VALUES ARE 'HANDLED' BY THE QTL PLOT if (qtlInformationIsComplete(markerInfo, rowNames)) { TreeMap<Long, QtlPlotDataPoint> data = sortQtlPlotData(rowNames, DvaluesList, markerInfo); // img = MakeRPlot.qtlPlot(name, data, locus, // plotWidth, plotHeight, "LOD score", "qtl"); } else { img = MakeRPlot.plot(d, instance, null, name, "col", "o", plotWidth, plotHeight); } // qtl.setPlot(img.getName()); } catch (Exception e) { e.printStackTrace(); // too bad, image failed } result.add(qtl); } } } catch (Exception e) { e.printStackTrace(); // too bad, data matrix failed } } return result; } private QTLMultiPlotResult createQTLMultiPlotReportFor(List<Entity> entities, Double threshold, QueryRule dataFilter, String query, Database db) throws Exception { HashMap<String, Entity> matches = new HashMap<String, Entity>(); // valnr - markername - markerbp - traitname - traitloc - value // 1 PVV4 3422 Y34G3 3456 5.424 List<String> valueListForR = new ArrayList<String>(); int overallIndex = 1; List<Data> allData; if (dataFilter == null) { allData = db.find(Data.class); } else { allData = db.find(Data.class, dataFilter); } List<String> entityTypes = new ArrayList<String>(); for (Entity e : entities) { entityTypes.add(e.get(Field.TYPE_FIELD).toString()); } DataMatrixHandler dmh = new DataMatrixHandler(db); // writer for data table File tmpData = new File(System.getProperty("java.io.tmpdir") + File.separator + "rplot_data_table_" + System.nanoTime() + ".txt"); BufferedWriter bw = new BufferedWriter(new FileWriter(tmpData)); for (Data d : allData) { // if something fails with this matrix, don't break the loop // e.g. backend file is missing try { // loop over Text data (can't be QTL) if (d.getValueType().equals("Text")) { continue; } // check if datamatrix target/features matches any entity type // and one of the dimensions is Marker if (entityTypes.contains(d.getTargetType()) || entityTypes.contains(d.getFeatureType()) && (d.getTargetType().equals("Marker") || d.getFeatureType().equals("Marker"))) { // create instance and get name of the row/col we want DataMatrixInstance instance = dmh.createInstance(d, db); List<String> rowNames = instance.getRowNames(); List<String> colNames = instance.getColNames(); // get the markers List<Marker> markers = db.find(Marker.class, new QueryRule(Marker.NAME, Operator.IN, d .getFeatureType().equals("Marker") ? colNames : rowNames)); HashMap<String, Marker> nameToMarker = new HashMap<String, Marker>(); for (Marker m : markers) { nameToMarker.put(m.getName(), m); } // for each entity, see if the types match to the matrix for (Entity e : entities) { if (d.getTargetType().equals(e.get(Field.TYPE_FIELD)) || d.getFeatureType().equals(e.get(Field.TYPE_FIELD))) { // if so, use this entity to 'query' the matrix and // store the values String name = e.get(ObservableFeature.NAME).toString(); // find out if the name is in the row or col names int rowIndex = rowNames.indexOf(name); int colIndex = colNames.indexOf(name); // get trait bp loc long locus; if (e instanceof Locus) { locus = ((Locus) e).getBpStart(); } else { locus = 0; } // if its in row, do row stuff if (rowIndex != -1) { Double[] Dvalues = Statistics.getAsDoubles(instance.getRow(rowIndex)); int maxIndex = Statistics.getIndexOfMax(Dvalues); double peakDouble = Dvalues[maxIndex]; if (threshold != null && peakDouble < threshold.doubleValue()) { continue; } matches.put(name, e); for (int markerIndex = 0; markerIndex < colNames.size(); markerIndex++) { if (nameToMarker.containsKey(colNames.get(markerIndex))) { // String line = // "plotMe <- rbind(plotMe, c(" + // overallIndex + ", \"" + // colNames.get(markerIndex) + "\", " + // nameToMarker.get(colNames.get(markerIndex)).getBpStart() // + ", \"" + name + "\", " + locus + // ", " + Dvalues[markerIndex] + "))"; // valueListForR.add(line); String chrId = nameToMarker.get(colNames.get(markerIndex)).getChromosome_Id() != null ? nameToMarker .get(colNames.get(markerIndex)).getChromosome_Id().toString() : "-"; bw.write(overallIndex + " \"" + colNames.get(markerIndex) + "\" " + chrId + " " + nameToMarker.get(colNames.get(markerIndex)).getBpStart() + " \"" + name + "\" " + locus + " " + Dvalues[markerIndex]); bw.newLine(); overallIndex++; } } } // if its in col, and not in row, do col stuff // we assume its not in row and col at the same // time, then its not QTL data but correlations or // so if (rowIndex == -1 && colIndex != -1) { Double[] Dvalues = Statistics.getAsDoubles(instance.getCol(colIndex)); int maxIndex = Statistics.getIndexOfMax(Dvalues); double peakDouble = Dvalues[maxIndex]; if (threshold != null && peakDouble < threshold.doubleValue()) { continue; } matches.put(name, e); for (int markerIndex = 0; markerIndex < rowNames.size(); markerIndex++) { if (nameToMarker.containsKey(rowNames.get(markerIndex))) { // String line = // "plotMe <- rbind(plotMe, c(" + // overallIndex + ", \"" + // rowNames.get(markerIndex) + "\", " + // nameToMarker.get(rowNames.get(markerIndex)).getBpStart() // + ", \"" + name + "\", " + locus + // ", " + Dvalues[markerIndex] + "))"; // valueListForR.add(line); String chrId = nameToMarker.get(rowNames.get(markerIndex)).getChromosome_Id() != null ? nameToMarker .get(rowNames.get(markerIndex)).getChromosome_Id().toString() : "-"; bw.write(overallIndex + " \"" + rowNames.get(markerIndex) + "\" " + chrId + " " + nameToMarker.get(rowNames.get(markerIndex)).getBpStart() + " \"" + name + "\" " + locus + " " + Dvalues[markerIndex]); bw.newLine(); overallIndex++; } } } if (matches.size() > 1000) { break; } } } } } catch (Exception e) { e.printStackTrace(); // too bad, data matrix failed } if (matches.size() > 1000) { throw new Exception("More than 1000 matches to your search query. Please be more specific."); } } bw.close(); // TODO: clean up? // File plot = MakeRPlot.qtlMultiPlot(tmpData, plotWidth, // (matches.size() * 10), query); // File cisTransplot = MakeRPlot.qtlCisTransPlot(tmpData, plotWidth, // plotHeight, query); QTLMultiPlotResult result = new QTLMultiPlotResult(); // result.setPlot(plot.getName()); // result.setCisTransPlot(cisTransplot.getName()); // result.setMatches(matches); this.model.setQmpr(result); return result; } // sort the datapoints to bp position to be plottable // use bppos as index to get automatic natural sorting! public static TreeMap<Long, QtlPlotDataPoint> sortQtlPlotData(List<String> markers, List<Double> lodscores, HashMap<String, Marker> markerInfo) { TreeMap<Long, QtlPlotDataPoint> res = new TreeMap<Long, QtlPlotDataPoint>(); for (int i = 0; i < markers.size(); i++) { long bpPos = markerInfo.get(markers.get(i)).getBpStart(); String chr = markerInfo.get(markers.get(i)).getChromosome_Name(); QtlPlotDataPoint qd = new QtlPlotDataPoint(lodscores.get(i).doubleValue(), bpPos, chr); res.put(new Long(bpPos), qd); } return res; } private boolean qtlInformationIsComplete(HashMap<String, Marker> markerInfo, List<String> colNames) { // TODO Auto-generated method stub return true; } public static HashMap<String, Marker> getMarkerInfo(List<String> colNames, Database db) { HashMap<String, Marker> result = new HashMap<String, Marker>(); try { List<Marker> dbFind = db.find(Marker.class, new QueryRule(Marker.NAME, Operator.IN, colNames)); for (Marker m : dbFind) { result.put(m.getName(), m); } } catch (Exception e) { // too bad, no marker annotations e.printStackTrace(); } return result; } private List<Entity> findInGenesAndProbes(String findMe, Database db) throws DatabaseException { List<Entity> result = new ArrayList<Entity>(); for (Gene entity : db.find(Gene.class)) { for (String field : entity.getFields()) { if (entity.get(field) != null && entity.get(field).toString().toLowerCase().contains(findMe.toLowerCase())) { result.add(entity); break; } } } for (Probe entity : db.find(Probe.class)) { for (String field : entity.getFields()) { if (entity.get(field) != null && entity.get(field).toString().toLowerCase().contains(findMe.toLowerCase())) { result.add(entity); break; } } } return result; } @Override public void reload(Database db) { try { List<Data> dataSets = db.find(Data.class); this.model.setDataSets(dataSets); if (this.model.getResultSet() == null) { this.model.setResultSet(new HashMap<String, Result>()); } } catch (Exception e) { e.printStackTrace(); this.setMessages(new ScreenMessage(e.getMessage() != null ? e.getMessage() : "null", false)); } } }