package org.molgenis.omx.plugins; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.Charset; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletResponse; 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.server.MolgenisRequest; import org.molgenis.framework.ui.PluginModel; import org.molgenis.framework.ui.ScreenController; import org.molgenis.observ.Category; import org.molgenis.observ.DataSet; import org.molgenis.observ.ObservableFeature; import org.molgenis.observ.Protocol; import org.molgenis.omx.EMeasureFeatureWriter; import org.molgenis.omx.dataset.DataSetViewerPlugin; import org.molgenis.util.Entity; import org.molgenis.util.Tuple; import org.molgenis.util.XlsWriter; import com.google.gson.Gson; /** * Protocol viewer controller */ public class ProtocolViewerController extends PluginModel<Entity> { private static final long serialVersionUID = -6143910771849972946L; /** Protocol viewer model */ private ProtocolViewer protocolViewer; public ProtocolViewerController(String name, ScreenController<?> parent) { super(name, parent); } public ProtocolViewer getMyModel() { return protocolViewer; } @Override public String getViewName() { return ProtocolViewer.class.getSimpleName(); } @Override public String getViewTemplate() { return ProtocolViewer.class.getName().replace('.', '/') + ".ftl"; } @Override public String getCustomHtmlHeaders() { // TODO move to head section in view StringBuilder s = new StringBuilder(); s.append("<link rel=\"stylesheet\" href=\"bootstrap/css/bootstrap.min.css\" type=\"text/css\" />"); s.append("<script type=\"text/javascript\" src=\"bootstrap/js/bootstrap.min.js\"></script>"); s.append("<link rel=\"stylesheet\" href=\"res/jquery-plugins/dynatree/skin-vista/ui.dynatree.css\" type=\"text/css\" />"); s.append("<script type=\"text/javascript\" src=\"res/jquery-plugins/dynatree/jquery.dynatree.min.js\"></script>"); s.append("<script type=\"text/javascript\" src=\"res/scripts/protocolviewer.js\"></script>"); s.append("<link rel=\"stylesheet\" href=\"res/css/protocolviewer.css\" type=\"text/css\" />"); return s.toString(); } @Override public Show handleRequest(Database db, Tuple request, OutputStream out) throws Exception { if (out == null) { this.handleRequest(db, request); return Show.SHOW_MAIN; } Object src = null; if (request.getAction().equals("download_json_getdataset")) { Integer dataSetId = request.getInt("datasetid"); List<DataSet> dataSets = db.find(DataSet.class, new QueryRule(DataSet.ID, Operator.EQUALS, dataSetId)); if (dataSets != null && !dataSets.isEmpty()) src = toJSDataSet(db, dataSets.get(0)); } else if (request.getAction().equals("download_json_getfeature")) { Integer featureId = request.getInt("featureid"); List<ObservableFeature> features = db.find(ObservableFeature.class, new QueryRule(ObservableFeature.ID, Operator.EQUALS, featureId)); if (features != null && !features.isEmpty()) src = toJSFeature(db, features.get(0)); } else { throw new RuntimeException("unknown action: " + request.getAction()); } // serialize object to json if (src != null) { Writer writer = new OutputStreamWriter(out, Charset.forName("UTF-8")); try { new Gson().toJson(src, writer); } finally { writer.close(); } } return Show.SHOW_MAIN; } @Override public void handleRequest(Database db, Tuple request) throws Exception { MolgenisRequest req = (MolgenisRequest) request; HttpServletResponse response = req.getResponse(); // get data set Integer dataSetId = request.getInt("datasetid"); DataSet dataSet = null; List<DataSet> dataSets = db.find(DataSet.class, new QueryRule(DataSet.ID, Operator.EQUALS, dataSetId)); if (dataSets != null && !dataSets.isEmpty()) dataSet = dataSets.get(0); // get features String featuresStr = request.getString("features"); List<ObservableFeature> features = null; if (featuresStr != null && !featuresStr.isEmpty()) { String[] featuresStrArr = request.getString("features").split(","); List<Integer> featureIds = new ArrayList<Integer>(featuresStrArr.length); for (String featureStr : featuresStrArr) featureIds.add(Integer.valueOf(featureStr)); features = findFeatures(db, featureIds); } if (request.getAction().equals("download_emeasure")) { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH.mm"); String fileName = "EMeasure_" + dateFormat.format(new Date()) + ".xml"; // write response headers response.setContentType("application/x-download"); response.setHeader("Content-Disposition", "attachment; filename=" + fileName); // write eMeasure XML file EMeasureFeatureWriter eMeasureWriter = new EMeasureFeatureWriter(response.getOutputStream()); try { eMeasureWriter.writeFeatures(features); } finally { eMeasureWriter.close(); } } else if (request.getAction().equals("download_xls")) { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH.mm"); String fileName = "selectedvariables_" + dateFormat.format(new Date()) + ".xls"; // write response headers response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", "attachment; filename=" + fileName); // write excel file List<String> headers = Arrays.asList("Selected variables", "Descriptions", "Sector/Protocol"); XlsWriter xlsWriter = new XlsWriter(response.getOutputStream(), headers); try { xlsWriter.writeHeader(); int row = 1; for (ObservableFeature feature : features) { xlsWriter.writeCell(0, row, feature.getName()); xlsWriter.writeCell(1, row, feature.getDescription()); // TODO not consistent with eMeasure output xlsWriter.writeCell(2, row, dataSet.getProtocolUsed_Identifier()); row++; } } finally { xlsWriter.close(); } } else if (request.getAction().equals("download_viewer")) { req.getRequest().getSession().setAttribute("selectedObservableFeatures", features); String dataSetViewerName = this.getDataSetViewerName(); if (dataSetViewerName != null) { StringBuilder sb = new StringBuilder(); sb.append(req.getAppLocation()); sb.append("/molgenis.do?__target=").append(dataSetViewerName); sb.append("&select=").append(dataSetViewerName); sb.append("&__action=selectDataSet"); sb.append("&dataSetId=").append(dataSetId); response.sendRedirect(sb.toString()); } } } /* * Find the name of the DataSetViewer for user in a url. For now if there * are multiple DataSetViewers it returns the first Returns null if not * found */ private String getDataSetViewerName() { ScreenController<?> menu = getParent(); for (ScreenController<?> controller : menu.getAllChildren()) { if (controller instanceof DataSetViewerPlugin) { return controller.getName(); } } return null; } // TODO reload should throw DatabaseException @Override public void reload(Database db) { List<DataSet> dataSets; try { dataSets = db.query(DataSet.class).find(); } catch (DatabaseException e) { throw new RuntimeException(e); } // create new model this.protocolViewer = new ProtocolViewer(); List<JSDataSet> jsDataSets; if (dataSets != null && !dataSets.isEmpty()) { jsDataSets = new ArrayList<JSDataSet>(dataSets.size()); for (DataSet dataSet : dataSets) // performance: do not add protocols jsDataSets.add(new JSDataSet(dataSet, null)); } else { jsDataSets = Collections.emptyList(); } this.protocolViewer.setDataSets(jsDataSets); } private List<Category> findCategories(Database db, ObservableFeature feature) throws DatabaseException { // TODO can we get by (internal) id instead of identifier? List<Category> categories = db.find(Category.class, new QueryRule(Category.OBSERVABLEFEATURE_IDENTIFIER, Operator.EQUALS, feature.getIdentifier())); return categories; } private List<Protocol> findSubProtocols(Database db, Protocol protocol) throws DatabaseException { List<Integer> subProtocolIds = protocol.getSubprotocols_Id(); if (subProtocolIds == null || subProtocolIds.isEmpty()) return Collections.emptyList(); List<Protocol> protocols = db.find(Protocol.class, new QueryRule(Protocol.ID, Operator.IN, subProtocolIds)); return protocols; } private List<ObservableFeature> findFeatures(Database db, List<Integer> featureIds) throws DatabaseException { if (featureIds == null || featureIds.isEmpty()) return null; List<ObservableFeature> features = db.find(ObservableFeature.class, new QueryRule(ObservableFeature.ID, Operator.IN, featureIds)); return features; } private JSDataSet toJSDataSet(Database db, DataSet dataSet) throws DatabaseException { Integer protocolId = dataSet.getProtocolUsed_Id(); List<Protocol> protocols = db.find(Protocol.class, new QueryRule(Protocol.ID, Operator.EQUALS, protocolId)); JSProtocol jsProtocol = null; if (protocols != null && !protocols.isEmpty()) jsProtocol = toJSProtocol(db, protocols.get(0)); return new JSDataSet(dataSet, jsProtocol); } private JSProtocol toJSProtocol(Database db, Protocol protocol) throws DatabaseException { // get features List<JSFeature> jsFeatures = null; List<ObservableFeature> features = findFeatures(db, protocol.getFeatures_Id()); if (features != null && !features.isEmpty()) { jsFeatures = new ArrayList<JSFeature>(features.size()); for (ObservableFeature feature : features) jsFeatures.add(toJSFeature(db, feature)); } // get sub protocols (recursive) List<JSProtocol> jsSubProtocols = null; List<Protocol> subProtocols = findSubProtocols(db, protocol); if (subProtocols != null && !subProtocols.isEmpty()) { jsSubProtocols = new ArrayList<JSProtocol>(subProtocols.size()); for (Protocol subProtocol : subProtocols) jsSubProtocols.add(toJSProtocol(db, subProtocol)); } return new JSProtocol(protocol, jsFeatures, jsSubProtocols); } private JSFeature toJSFeature(Database db, ObservableFeature feature) throws DatabaseException { List<JSCategory> jsCategories = null; List<Category> categories = findCategories(db, feature); if (categories != null && !categories.isEmpty()) { jsCategories = new ArrayList<JSCategory>(categories.size()); for (Category category : categories) jsCategories.add(new JSCategory(category)); } return new JSFeature(feature, jsCategories); } public static class JSDataSet { private final int id; private final String name; private final JSProtocol protocol; public JSDataSet(DataSet dataSet, JSProtocol protocol) { this.id = dataSet.getId(); this.name = dataSet.getName(); this.protocol = protocol; } public int getId() { return id; } public String getName() { return name; } public JSProtocol getProtocol() { return protocol; } } @SuppressWarnings("unused") private static class JSProtocol { private final int id; private final String name; private final List<JSFeature> features; private final List<JSProtocol> subProtocols; public JSProtocol(Protocol protocol, List<JSFeature> features, List<JSProtocol> subProtocols) { this.id = protocol.getId(); this.name = protocol.getName(); this.features = features; this.subProtocols = subProtocols; } } @SuppressWarnings("unused") private static class JSFeature { private final int id; private final String name; private final String description; private final String dataType; private final List<JSCategory> categories; public JSFeature(ObservableFeature feature, List<JSCategory> categories) { this.id = feature.getId(); this.name = feature.getName(); this.description = feature.getDescription(); this.dataType = feature.getDataType(); this.categories = categories; } } @SuppressWarnings("unused") private static class JSCategory { private final int id; private final String name; private final String code; private final String description; public JSCategory(Category category) { this.id = category.getId(); this.name = category.getName(); this.code = category.getValueCode(); this.description = category.getDescription(); } } }