package org.molgenis.protocol;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Vector;
import org.apache.log4j.Logger;
import org.molgenis.MolgenisFieldTypes;
import org.molgenis.batch.MolgenisBatch;
import org.molgenis.fieldtypes.UnknownField;
import org.molgenis.framework.db.Database;
import org.molgenis.framework.db.DatabaseException;
import org.molgenis.framework.db.QueryRule.Operator;
import org.molgenis.framework.ui.ScreenController;
import org.molgenis.framework.ui.html.ActionInput;
import org.molgenis.framework.ui.html.CheckboxInput;
import org.molgenis.framework.ui.html.Container;
import org.molgenis.framework.ui.html.DatetimeInput;
import org.molgenis.framework.ui.html.DivPanel;
import org.molgenis.framework.ui.html.HorizontalRuler;
import org.molgenis.framework.ui.html.HtmlInput;
import org.molgenis.framework.ui.html.HtmlInputException;
import org.molgenis.framework.ui.html.MolgenisForm;
import org.molgenis.framework.ui.html.Paragraph;
import org.molgenis.framework.ui.html.RadioInput;
import org.molgenis.framework.ui.html.SelectInput;
import org.molgenis.framework.ui.html.SelectMultipleInput;
import org.molgenis.framework.ui.html.StringInput;
import org.molgenis.framework.ui.html.Table;
import org.molgenis.framework.ui.html.XrefInput;
import org.molgenis.matrix.component.MatrixViewer;
import org.molgenis.matrix.component.SliceablePhenoMatrix;
import org.molgenis.matrix.component.general.MatrixQueryRule;
import org.molgenis.pheno.Individual;
import org.molgenis.pheno.Measurement;
import org.molgenis.pheno.ObservationTarget;
import org.molgenis.pheno.ObservedValue;
import org.molgenis.util.ValueLabel;
public class ApplyProtocolUI
{
private Container protocolApplicationContainer = null;
private DivPanel protocolDiv;
private DivPanel tableDiv;
private Table valueTable;
private SelectInput protocols;
MatrixViewer targetMatrixViewer = null;
static String TARGETMATRIX = "targetmatrix";
private SelectMultipleInput batches;
private RadioInput newOrEditButtons;
private CheckboxInput timeBox;
private CheckboxInput allValuesBox;
private ApplyProtocolPluginModel model;
private ApplyProtocolService service;
private static transient Logger logger = Logger.getLogger(ApplyProtocolUI.class);
public ApplyProtocolUI(ApplyProtocolPluginModel model)
{
this.model = model;
}
public void setService(ApplyProtocolService service)
{
this.service = service;
}
public void initScreen(Database db, ScreenController<?> plugin, int userId, String userName) throws Exception
{
model.setNewProtocolApplication(false);
model.setTimeInfo(false);
protocolApplicationContainer = new Container();
protocolDiv = new DivPanel("ProtocolPanel", null);
tableDiv = new DivPanel("TablePanel", null);
tableDiv.setStyle("width:0");
makeProtocolSelect(db);
makeTargetsMatrix(db, plugin, userId);
makeBatchSelect(db);
makeNewOrEditButtons(userName);
makeTimeSelectbox();
makeAllValuesSelectbox();
makeSelectButton();
makeClearButton();
fillContainer();
}
public Container getProtocolApplicationContainer()
{
return this.protocolApplicationContainer;
}
/**
* Puts an input box with value 'value' into the table at position col, row
*
* @param col
* column to insert the input in
* @param row
* row to insert the input in
* @param order
* in case of multiple HtmlInputs for one target-feature
* combination, append this number to keep the input name unique
* @param value
* the value to insert
* @throws Exception
*/
@SuppressWarnings(
{ "rawtypes", "unchecked" })
private HtmlInput makeInput(Database db, int featureNr, int col, int row, int order, ObservedValue value)
throws Exception
{
HtmlInput valueInput;
Measurement feature = model.getFeaturesList().get(featureNr);
// Get the metadata to create the input
// Data type
String dataType = feature.getDataType();
if (MolgenisFieldTypes.getType(dataType) instanceof UnknownField)
{
throw new Exception("Fieldtype " + dataType + "' is unknown in MOLGENIS");
}
// Target type allowed for relation
String observationTargetType = model.getTargettypeAllowedForRelation(feature);
// Panel label for relation
String panelLabel = feature.getPanelLabelAllowedForRelation();
// Make the appropriate input
List<String> catList = model.getAllCategoriesForFeatureAsStrings(feature.getName());
if (dataType.equals("string") && catList != null && catList.size() > 0)
{
// If there are categories for this Measurement, show a selectbox
// with those
valueInput = new SelectInput(col + "_" + row + "_" + order);
((SelectInput) valueInput).setOptionsFromStringList(catList);
if (value != null && value.getValue() != null)
{
valueInput.setValue(value.getValue());
}
}
else
{
if (panelLabel != null)
{
// If there's only a subset of labeled Panels allowed for this
// Measurement, show a selectbox with those
valueInput = new SelectInput(col + "_" + row + "_" + order);
List<ObservationTarget> panelList = model.getAllPanelsForFeature(feature);
for (ObservationTarget p : panelList)
{
((SelectInput) valueInput).addOption(p.getId(), p.getName());
}
if (value != null && value.getRelation_Id() != null)
{
valueInput.setValue(value.getRelation_Id());
}
}
else
{
// Normally, show the input belonging to the data type
valueInput = MolgenisFieldTypes.createInput(dataType, col + "_" + row + "_" + order,
observationTargetType);
if (dataType.equals("string"))
{
((StringInput) valueInput).setWidth(20);
}
if (value != null)
{
if (value.getValue() != null)
{
valueInput.setValue(value.getValue());
}
else
{
if (dataType.equals("xref"))
{
ObservationTarget relation = db.findById(ObservationTarget.class, value.getRelation_Id());
((XrefInput) valueInput).setValue(relation);
}
else
{
valueInput.setValue(value.getRelation_Id());
}
}
}
}
}
return valueInput;
}
/**
* Makes the appropriate input for the cell at col, row and adds it to the
* valueTable. At the moment, supports only cells which contain one input.
*
* @param featureNr
* @param col
* @param row
* @param order
* @param value
* @throws Exception
*/
@SuppressWarnings("rawtypes")
public void makeInputAndSetCell(Database db, int featureNr, int col, int row, int order, ObservedValue value)
throws Exception
{
HtmlInput input = makeInput(db, featureNr, col, row, order, value);
valueTable.setCell(col, row, input);
}
/**
* Makes the appropriate date input for the cell at col, row and adds it to
* the valueTable. At the moment, supports only cells which contain one
* input.
*
* @param col
* @param row
* @param order
* @param theTime
*/
public void makeDateInputAndSetCell(int col, int row, int order, Date theTime)
{
DatetimeInput input = new DatetimeInput(col + "_" + row + "_" + order, theTime);
valueTable.setCell(col, row, input);
}
/**
* Fill the container
*
*/
public void fillContainer()
{
protocolApplicationContainer.add(protocolDiv);
}
/**
*
*/
public void makeProtocolSelect(Database db)
{
try
{
protocols = new SelectInput("Protocols");
protocols.setLabel("Choose protocol:");
protocols.setOptions(service.getAllProtocolsSorted(db, Protocol.NAME, "ASC", model.getInvestigationIds()),
Protocol.NAME, Protocol.NAME);
protocolDiv.add(protocols);
protocolDiv.add(new HorizontalRuler());
}
catch (Exception e)
{
e.printStackTrace();
logger.error("An error occurred while retrieving protocols from the database", e);
}
}
/**
* Create a select box with ObservationTargets grabbed from the database
*
*/
// public void makeTargetsSelect() {
// try {
// targets = new SelectMultipleInput("Targets", null);
// targets.setLabel("Choose Targets:");
// List<Integer> investigationIds =
// service.getWritableUserInvestigationIds(model.getUserId());
// for (ObservationTarget o :
// service.getAllObservationTargets(investigationIds)) {
// targets.addOption(o.getId(),
// service.getObservationTargetById(o.getId()).getName());
// }
// protocolDiv.add(targets);
//
// } catch(Exception e) {
// e.printStackTrace();
// logger.error("An error occurred while retrieving targets from the database",
// e);
// }
// }
public void makeTargetsMatrix(Database db, ScreenController<?> plugin, int userId) throws Exception
{
List<String> investigationNames = service.getAllUserInvestigationNames(db, userId);
List<String> measurementsToShow = new ArrayList<String>();
measurementsToShow.add("Species");
measurementsToShow.add("Sex");
measurementsToShow.add("Active");
List<MatrixQueryRule> filterRules = new ArrayList<MatrixQueryRule>();
filterRules.add(new MatrixQueryRule(MatrixQueryRule.Type.rowHeader, Individual.INVESTIGATION_NAME, Operator.IN,
investigationNames));
targetMatrixViewer = new MatrixViewer(plugin, TARGETMATRIX, new SliceablePhenoMatrix<Individual, Measurement>(
Individual.class, Measurement.class), true, 2, false, false, filterRules, new MatrixQueryRule(
MatrixQueryRule.Type.colHeader, Measurement.NAME, Operator.IN, measurementsToShow));
targetMatrixViewer.setDatabase(db);
targetMatrixViewer.setLabel("Choose animals:");
protocolDiv.add(targetMatrixViewer);
protocolDiv.add(new HorizontalRuler());
}
/**
* Create a select box with Batches grabbed from the database
*/
public void makeBatchSelect(Database db)
{
try
{
batches = new SelectMultipleInput("Batches", null);
batches.setLabel("Choose batches:");
for (MolgenisBatch o : service.getAllBatches(db))
{
batches.addOption(o.getId(), o.getName());
}
protocolDiv.add(batches);
protocolDiv.add(new HorizontalRuler());
}
catch (Exception e)
{
e.printStackTrace();
logger.error("An error occurred while retrieving batches from the database", e);
}
}
/**
* Create radio buttons to select the way to apply the protocol.
*
* @throws HtmlInputException
*/
private void makeNewOrEditButtons(String userName) throws HtmlInputException
{
List<String> options = new ArrayList<String>();
options.add("New");
options.add("Edit");
List<String> optionLabels = new ArrayList<String>();
optionLabels.add("Make new values");
optionLabels.add("Edit existing values");
newOrEditButtons = new RadioInput("NewOrEdit", "", "New", false, false,
"Indicate whether you want to fill in new values (default) or edit existing ones.", options,
optionLabels);
protocolDiv.add(newOrEditButtons);
}
/**
* Create a checkbox to toggle time fields with the values.
*/
private void makeTimeSelectbox()
{
Vector<ValueLabel> options = new Vector<ValueLabel>();
options.add(new ValueLabel("Time", "Show date-time fields with values"));
timeBox = new CheckboxInput("TimeBox", "", "Indicate whether you want date-time fields next to the values",
options, null);
protocolDiv.add(new Paragraph("", "")); // gives empty <p></p>
protocolDiv.add(timeBox);
}
/**
* Create a checkbox to toggle showing all values, not only the most recent
* one(s).
*/
private void makeAllValuesSelectbox()
{
Vector<ValueLabel> options = new Vector<ValueLabel>();
options.add(new ValueLabel("AllValues",
"Show not only most recent but all values (works only with 'Edit existing values' and "
+ "disables defaults)"));
allValuesBox = new CheckboxInput("AllValuesBox", "",
"Indicate whether you want to see all values for every target-measurement combination", options, null);
protocolDiv.add(new Paragraph("", "")); // gives empty <p></p>
protocolDiv.add(allValuesBox);
}
/**
*
*/
public void makeSelectButton()
{
ActionInput selectButton = new ActionInput("Select", "", "Select");
protocolDiv.add(selectButton);
}
/**
* Create a button to clear selections
*
*/
public void makeClearButton()
{
ActionInput clearButton = new ActionInput("Clear", "", "Reset");
protocolDiv.add(clearButton);
}
/**
* Create an 'apply' button
*
*/
public void makeApplyButton()
{
ActionInput applyButton = new ActionInput("Apply", "", "Apply Protocol");
tableDiv.add(applyButton);
}
/**
* Create a button to apply defaults to the table
*
*/
public void makeApplyAllDefaultsButton()
{
ActionInput applyDefaultsButton = new ActionInput("ApplyAllDefaults", "", "Set All Defaults");
tableDiv.add(applyDefaultsButton);
}
/**
* Make a table given a protocolId (whose feature to use) and a targetlist
*
* @param protocolId
* @param targetList
*/
public void makeTable(Database db)
{
try
{
valueTable = new Table("ValueTable", "");
makeColumns();
makeRows(db);
fillTableCells(db);
tableDiv.add(valueTable);
}
catch (Exception e)
{
logger.error(e.getMessage());
e.printStackTrace();
}
}
/**
* Create columns for a given protocol id, with the columns being the
* features for that protocol
*
* @param protocolId
* the protocol whose features we want to use
* @throws DatabaseException
* @throws ParseException
*/
public void makeColumns() throws DatabaseException, ParseException
{
for (Measurement m : model.getFeaturesList())
{
String measurementName = m.getName();
valueTable.addColumn(measurementName);
if (model.isTimeInfo())
{
valueTable.addColumn(measurementName + " start");
valueTable.addColumn(measurementName + " end");
}
}
}
/**
* Create rows for the selected targets
*
* @throws DatabaseException
* @throws ParseException
*/
public void makeRows(Database db) throws DatabaseException, ParseException
{
valueTable.addRow("Defaults:");
model.getTargetsIdList().clear();
for (String o : model.getFullTargetList())
{
Integer targetId = Integer.parseInt(o);
model.getTargetsIdList().add(targetId);
valueTable.addRow(service.getObservationTargetById(db, targetId).getName());
}
}
/**
* Fill all table cells for the number of columns and rows
*
* @param valueTable
* @throws DatabaseException
* @throws ParseException
*/
@SuppressWarnings("rawtypes")
public void fillTableCells(Database db) throws DatabaseException, ParseException
{
try
{
int sizeFeatures = model.getFeaturesList().size();
if (model.isAllValues() == false)
{
DivPanel div;
// First row contains default input boxes
for (int col = 0; col < sizeFeatures; col++)
{
int colNrInTable = col;
if (model.isTimeInfo())
{
colNrInTable *= 3;
}
div = new DivPanel();
HtmlInput input = makeInput(db, col, colNrInTable, 0, 0, null);
input.setLabel("");
div.add(input);
ActionInput applyButton2 = new ActionInput("ApplyDefault_" + colNrInTable, "", "Set");
div.add(applyButton2);
// Put the input in the right place in the table
if (model.isTimeInfo())
{
Date now = Calendar.getInstance().getTime();
// Make div with default start date-time
DivPanel div2 = new DivPanel();
DatetimeInput datetimeInputStart = new DatetimeInput((colNrInTable + 1) + "_0_0", now);
datetimeInputStart.setLabel("");
div2.add(datetimeInputStart);
ActionInput applyButtonStartTime = new ActionInput("ApplyStartTime_" + (colNrInTable + 1), "",
"Set");
div2.add(applyButtonStartTime);
valueTable.setCell(colNrInTable + 1, 0, div2);
// Make div with default end date-time
DivPanel div3 = new DivPanel();
DatetimeInput datetimeInputEnd = new DatetimeInput((colNrInTable + 2) + "_0_0", now);
datetimeInputEnd.setLabel("");
div3.add(datetimeInputEnd);
ActionInput applyButtonEndTime = new ActionInput("ApplyEndTime_" + (colNrInTable + 2), "",
"Set");
div3.add(applyButtonEndTime);
valueTable.setCell(colNrInTable + 2, 0, div3);
}
valueTable.setCell(colNrInTable, 0, div);
}
}
int userId = model.getUserId();
int ownInvId = service.getOwnUserInvestigationId(db, userId);
List<Integer> investigationIds = service.getWritableUserInvestigationIds(db, userId);
// Rest of the rows contain inputs for each target-feature
// combination
for (int row = 1; row <= model.getFullTargetList().size(); row++)
{
for (int col = 0; col < sizeFeatures; col++)
{
int colNrInTable = col;
if (model.isTimeInfo())
{
colNrInTable *= 3;
}
if (!model.isNewProtocolApplication())
{
// Show existing values, or new ones if none can be
// found
DivPanel valueDiv = new DivPanel();
DivPanel starttimeDiv = new DivPanel();
DivPanel endtimeDiv = new DivPanel();
int valueCounter = 0;
List<ObservedValue> values = service.getObservedValuesByTargetAndFeature(db, model
.getTargetsIdList().get(row - 1), model.getFeaturesList().get(col), investigationIds,
ownInvId);
for (ObservedValue value : values)
{
HtmlInput input = makeInput(db, col, colNrInTable, row, valueCounter, value);
input.setLabel("");
valueDiv.add(input);
if (model.isTimeInfo())
{
DatetimeInput datetimeInputStart = new DatetimeInput((colNrInTable + 1) + "_" + row
+ "_" + valueCounter);
datetimeInputStart.setLabel("");
if (value != null && value.getTime() != null)
{
datetimeInputStart.setValue(value.getTime());
}
starttimeDiv.add(datetimeInputStart);
DatetimeInput datetimeInputEnd = new DatetimeInput((colNrInTable + 2) + "_" + row + "_"
+ valueCounter);
datetimeInputEnd.setLabel("");
if (value != null && value.getEndtime() != null)
{
datetimeInputEnd.setValue(value.getEndtime());
}
endtimeDiv.add(datetimeInputEnd);
}
if (!model.isAllValues())
{
// If user wants only the first value, jump out
// now:
break;
}
valueCounter++;
}
valueTable.setCell(colNrInTable, row, valueDiv);
valueTable.setCell(colNrInTable + 1, row, starttimeDiv);
valueTable.setCell(colNrInTable + 2, row, endtimeDiv);
}
else
{
// Show only new values
HtmlInput input = makeInput(db, col, colNrInTable, row, 0, null);
valueTable.setCell(colNrInTable, row, input);
if (model.isTimeInfo())
{
DatetimeInput datetimeInputStart = new DatetimeInput((colNrInTable + 1) + "_" + row + "_0");
valueTable.setCell(colNrInTable + 1, row, datetimeInputStart);
DatetimeInput datetimeInputEnd = new DatetimeInput((colNrInTable + 2) + "_" + row + "_0");
valueTable.setCell(colNrInTable + 2, row, datetimeInputEnd);
}
}
}
}
}
catch (Exception e)
{
e.printStackTrace();
logger.error("Filling table cells failed", e);
}
}
public void addTableDiv()
{
this.protocolApplicationContainer.add(tableDiv);
}
public void setValues()
{
protocols.setValue(model.getProtocolName());
batches.setValue(model.getBatchesList());
if (model.isNewProtocolApplication())
{
newOrEditButtons.setValue("New");
}
else
{
newOrEditButtons.setValue("Edit");
}
if (model.isTimeInfo())
{
Vector<String> valueVector = new Vector<String>();
valueVector.add("Time");
timeBox.setValue(valueVector); // checkboxInput's setValue() expects
// a vector of String (undocumented
// behavior)
}
else
{
timeBox.setValue(null);
}
if (model.isAllValues())
{
Vector<String> valueVector = new Vector<String>();
valueVector.add("AllValues");
allValuesBox.setValue(valueVector); // checkboxInput's setValue()
// expects a vector of String
// (undocumented behavior)
}
else
{
allValuesBox.setValue(null);
}
}
/**
* Set the value of the input in the table cell at col, row
*
* @param col
* @param row
* @param value
*/
@SuppressWarnings(
{ "rawtypes", "unchecked" })
public void fixCellValue(Database db, int col, int row, Object value)
{
HtmlInput input = (HtmlInput) valueTable.getCell(col, row);
input.setValue(value);
// And, if this involves an xref box, set the value and label of the
// selected option
if (valueTable.getCell(col, row) instanceof XrefInput)
{
try
{
int targetId = Integer.parseInt(value.toString());
((XrefInput) input).setValue(service.getObservationTargetById(db, targetId));
}
catch (Exception e)
{
// Do nothing, no value will be set
}
}
valueTable.setCell(col, row, input);
}
}