package org.molgenis.ngs.ui;
import java.io.File;
import java.io.IOException;
import java.sql.Date;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.zip.DataFormatException;
import org.molgenis.contribution.Person;
import org.molgenis.framework.db.Database;
import org.molgenis.framework.db.Database.DatabaseAction;
import org.molgenis.framework.db.DatabaseException;
import org.molgenis.framework.ui.EasyPluginController;
import org.molgenis.framework.ui.ScreenController;
import org.molgenis.framework.ui.ScreenView;
import org.molgenis.framework.ui.html.ActionInput;
import org.molgenis.framework.ui.html.EntityTable;
import org.molgenis.framework.ui.html.FileInput;
import org.molgenis.framework.ui.html.MolgenisForm;
import org.molgenis.framework.ui.html.Newline;
import org.molgenis.framework.ui.html.Paragraph;
import org.molgenis.framework.ui.html.TextInput;
import org.molgenis.ngs.Array;
import org.molgenis.ngs.Flowcell;
import org.molgenis.ngs.LibraryLane;
import org.molgenis.ngs.Machine;
import org.molgenis.ngs.NgsBarcode;
import org.molgenis.ngs.NgsCapturingKit;
import org.molgenis.ngs.NgsPrepKit;
import org.molgenis.ngs.NgsSample;
import org.molgenis.ngs.NgsStudy;
import org.molgenis.util.CsvFileReader;
import org.molgenis.util.CsvReader;
import org.molgenis.util.CsvStringReader;
import org.molgenis.util.Tuple;
import org.molgenis.util.ValueLabel;
/**
* ImportWorksheetController takes care of all user requests and application
* logic.
*
* <li>Each user request is handled by its own method based action=methodName.
* <li>MOLGENIS takes care of db.commits and catches exceptions to show to the
* user <li>ImportWorksheetModel holds application state and business logic on
* top of domain model. Get it via this.getModel()/setModel(..) <li>
* ImportWorksheetView holds the template to show the layout. Get/set it via
* this.getView()/setView(..).
*/
public class ImportWorksheet extends EasyPluginController<ImportWorksheet>
{
Map<String, Person> persons = new LinkedHashMap<String, Person>();
Map<String, NgsStudy> studies = new LinkedHashMap<String, NgsStudy>();
Map<String, NgsSample> samples = new LinkedHashMap<String, NgsSample>();
Map<String, Flowcell> flowcells = new LinkedHashMap<String, Flowcell>();
Map<String, LibraryLane> liblanes = new LinkedHashMap<String, LibraryLane>();
Map<String, NgsBarcode> barcodes = new LinkedHashMap<String, NgsBarcode>();
Map<String, NgsCapturingKit> capturingKits = new LinkedHashMap<String, NgsCapturingKit>();
Map<String, NgsPrepKit> prepKits = new LinkedHashMap<String, NgsPrepKit>();
Map<String, Machine> machines = new LinkedHashMap<String, Machine>();
Map<String, Array> arrays = new LinkedHashMap<String, Array>();
public enum State
{
UPLOAD, REVIEW
};
private State state = State.UPLOAD;
private static final long serialVersionUID = 6075389492409205677L;
public ImportWorksheet(String name, ScreenController<?> parent)
{
super(name, parent);
this.setModel(this); // the default model
}
@Override
public void reload(Database db) throws Exception
{
// nothing todo
}
// handle the upload
public void uploadWorksheet(Database db, Tuple request) throws IOException, DataFormatException, ParseException
{
CsvReader reader = null;
if (!request.isNull("filefor_worksheetFile"))
{
reader = new CsvFileReader(new File(request.getString("filefor_worksheetFile")));
}
else
{
reader = new CsvStringReader(request.getString("worksheetCsv"));
}
persons = new LinkedHashMap<String, Person>();
studies = new LinkedHashMap<String, NgsStudy>();
samples = new LinkedHashMap<String, NgsSample>();
flowcells = new LinkedHashMap<String, Flowcell>();
liblanes = new LinkedHashMap<String, LibraryLane>();
barcodes = new LinkedHashMap<String, NgsBarcode>();
capturingKits = new LinkedHashMap<String, NgsCapturingKit>();
prepKits = new LinkedHashMap<String, NgsPrepKit>();
machines = new LinkedHashMap<String, Machine>();
arrays = new LinkedHashMap<String, Array>();
int counter_total = 0;
int counter_ignored = 0;
// iterate
for (Tuple row : reader)
{
counter_total++;
// parsing validation
String study_identifier = capitalizeFirstOnly(row.getString("project"));
String study_contact = nameCasing(row.getString("contact"));
String seqType = row.getString("seqType");
String flowcell_identifier = row.getString("flowcell");
String flowcell_run = row.getString("run");
String flowcell_machine = row.getString("sequencer");
String sample_identifier = row.getString("internalSampleId");
String sample_externalIdentifier = row.getString("externalSampleID");
String lib_lanes = row.getString("lane");
String lib_barcode = row.getString("barcode");
String lib_capturingKit = capitalizeFirstOnly(row.getString("capturingKit"));
String lib_prepKit = capitalizeFirstOnly(row.getString("prepKit"));
String barcodeType = row.getString("barcodeType");
String arrayFile = row.getString("arrayFile");
String arrayID = row.getString("arrayID");
if (lib_lanes == null)
{
counter_ignored++;
// System.out.println(">> Row ignored because Lane was null: " +
// row);
// Do same here as when we did find a lane..
// 1. create function calls to do work below
// 2. also call functions here
// Now, flowcell-lane-barcode-SAMPLE is unique, so we can also
// store a new sample without specifying its Flowcell (may be
// NA),
// its lane (then also NA), its barcode (may be NA)...!
// which makes sense because two different samples may be on
// same FLCLL-LANE-BARCODE!
}
else
{
for (String lib_lane : lib_lanes.split(","))// laneList)
{
lib_lane = lib_lane.trim();
String lib_identifier = flowcell_identifier + "_L" + lib_lane + "_" + lib_barcode + "_"
+ sample_identifier;
// if Google-docs-validation script produced error, then
// insert null in barcode
if (barcodeType != null)
{
boolean validBarcodeType = false;
for (ValueLabel vl : (new NgsBarcode().getBarcodeTypeOptions()))
if (barcodeType.equalsIgnoreCase((String) vl.getValue())) validBarcodeType = true;
if (!validBarcodeType)
{
lib_barcode = null;
barcodeType = null;
}
}
// parsing dates sucks
Date flowcell_startDate = null;
DateFormat formatter = new SimpleDateFormat("yyMMdd", Locale.US);
if (!row.isNull("sequencingStartDate")) flowcell_startDate = new java.sql.Date(formatter.parse(
row.getString("sequencingStartDate")).getTime());
// validation
if (study_identifier == null) throw new DataFormatException("project is required for row: " + row);
if (study_contact == null) throw new DataFormatException("contact is required for row: " + row);
if (sample_identifier == null) throw new DataFormatException(
"internalSampleId is required for row: " + row);
// object creation, based on identifiers
if (persons.get(study_contact) == null)
{
Person person = new Person();
person.setName(study_contact);
persons.put(study_contact, person);
}
// required: study
if (studies.get(study_identifier) == null)
{
NgsStudy study = new NgsStudy();
study.setIdentifier(study_identifier);
// FIXME? Why is seqType a propertie of study?! MD
// thinks seqType should be a property of lane-barcode.
if (seqType == null)
{
if (flowcell_startDate == null)
{
study.setSeqType("PE");
}
else
{
// guesstimate: (date < 10 Nov 2010 ? SR : PE)
if (flowcell_startDate.before(new java.sql.Date(formatter.parse("101110").getTime())))
{
study.setSeqType("SR");
}
else
{
study.setSeqType("PE");
}
}
}
else
{
study.setSeqType(seqType);
}
// check seq type
if (study.getSeqType().equalsIgnoreCase("PE") && flowcell_machine != null
&& flowcell_machine.equalsIgnoreCase("HWUSI-EAS536"))
{
throw new DataFormatException(">>> Paired end was never run on HWUSI-EAS536 >>> " + row);
}
study.setContact_Name(study_contact);
studies.put(study_identifier, study);
}
// required: sample
if (studies.get(sample_identifier) == null)
{
NgsSample sample = new NgsSample();
sample.setIdentifier(sample_identifier);
sample.setExternalIdentifier(sample_externalIdentifier);
sample.setStudy_Identifier(study_identifier);
samples.put(sample_identifier, sample);
}
// optional: flowcell
if (flowcell_identifier != null && flowcells.get(flowcell_identifier) == null)
{
Flowcell flowcell = new Flowcell();
flowcell.setIdentifier(flowcell_identifier);
flowcell.setRun(flowcell_run);
flowcell.setMachine_Name(flowcell_machine);
flowcell.setStartDate(flowcell_startDate);
flowcells.put(flowcell_identifier, flowcell);
}
// optional: libraryLane
if (lib_identifier != null && liblanes.get(lib_identifier) == null)
{
LibraryLane lib = new LibraryLane();
lib.setIdentifier(lib_identifier);
lib.setFlowcell_Identifier(flowcell_identifier);
lib.setLane(lib_lane);
lib.setBarcode_Name(lib_barcode);
lib.setCapturingKit_Name(lib_capturingKit);
lib.setPrepKit_Name(lib_prepKit);
lib.setSample_Identifier(sample_identifier);
liblanes.put(lib_identifier, lib);
}
// System.out.println(">> " + barcodeType);
if (lib_barcode != null && barcodes.get(lib_barcode) == null)
{
NgsBarcode bc = new NgsBarcode();
bc.setName(lib_barcode);
// if (barcodeType != null) {
// // bc.getBarcodeTypeOptions();
// }
bc.setBarcodeType(barcodeType);
barcodes.put(lib_barcode, bc);
}
if (lib_capturingKit != null && capturingKits.get(lib_capturingKit) == null)
{
NgsCapturingKit c = new NgsCapturingKit();
c.setName(lib_capturingKit);
capturingKits.put(lib_capturingKit, c);
}
if (lib_prepKit != null && prepKits.get(lib_prepKit) == null)
{
NgsPrepKit c = new NgsPrepKit();
c.setName(lib_prepKit);
prepKits.put(lib_prepKit, c);
}
if (flowcell_machine != null && machines.get(flowcell_machine) == null)
{
Machine c = new Machine();
c.setName(flowcell_machine);
machines.put(flowcell_machine, c);
}
System.out.println(">> Array: " + arrayFile);
if (arrayFile != null && arrays.get(sample_identifier) == null)
{
Array a = new Array();
a.setSample_Identifier(sample_identifier);
a.setArrayFile(arrayFile);
a.setArrayID(arrayID);
a.setIdentifier(sample_identifier);
arrays.put(sample_identifier, a);
}
}
}
}
System.out.println(">> REPORT: total number of lines: " + counter_total);
System.out.println(">> REPORT: number of lines IGNORED: " + counter_ignored);
state = State.REVIEW;
}
/**
* Capitalize only the first character of name
*/
private String capitalizeFirstOnly(String name)
{
String result = "";
if (name != null) for (int i = 0; i < name.length(); i++)
{
char c = name.charAt(i);
result += (i == 0 ? Character.toUpperCase(c) : Character.toLowerCase(c));
}
return result;
}
/**
* Capitalize first letter and each letter followed by a whitespace
*/
private String nameCasing(String name)
{
String result = "";
boolean capitalize = true;
for (int i = 0; i < name.length(); i++)
{
char c = name.charAt(i);
result += (capitalize ? Character.toUpperCase(c) : Character.toLowerCase(c));
capitalize = c == ' ';
}
return result;
}
public void saveUpload(Database db, Tuple request) throws DatabaseException
{
try
{
int count = 0;
count += db.update(new ArrayList<Person>(persons.values()), DatabaseAction.ADD_UPDATE_EXISTING, "name");
count += db.update(new ArrayList<NgsStudy>(studies.values()), DatabaseAction.ADD_UPDATE_EXISTING,
"identifier");
// System.out.println(">> " + barcodes.values());
count += db
.update(new ArrayList<NgsBarcode>(barcodes.values()), DatabaseAction.ADD_UPDATE_EXISTING, "name");
count += db
.update(new ArrayList<NgsPrepKit>(prepKits.values()), DatabaseAction.ADD_UPDATE_EXISTING, "name");
count += db.update(new ArrayList<Machine>(machines.values()), DatabaseAction.ADD_UPDATE_EXISTING, "name");
// System.out.println(">> " + capturingKits.values());
count += db.update(new ArrayList<NgsCapturingKit>(capturingKits.values()),
DatabaseAction.ADD_UPDATE_EXISTING, "name");
count += db.update(new ArrayList<NgsSample>(samples.values()), DatabaseAction.ADD_UPDATE_EXISTING,
"identifier");
count += db.update(new ArrayList<Flowcell>(flowcells.values()), DatabaseAction.ADD_UPDATE_EXISTING,
"identifier");
// System.out.println(">> " + liblanes.values());
count += db.update(new ArrayList<LibraryLane>(liblanes.values()), DatabaseAction.ADD_UPDATE_EXISTING,
"identifier");
// count += db.update(new ArrayList<Array>(arrays.values()),
// DatabaseAction.ADD_UPDATE_EXISTING, "identifier");
for (Array a : arrays.values())
{
System.out.println(a);
db.add(a);
}
state = State.UPLOAD;
this.setSuccess("Added or updated " + count + " records succesfully");
}
catch (DatabaseException e)
{
state = State.UPLOAD;
throw e;
}
}
public void resetUpload(Database db, Tuple request)
{
state = State.UPLOAD;
}
@Override
public ScreenView getView()
{
MolgenisForm view = new MolgenisForm(this);
if (state == State.UPLOAD)
{
view.add(new TextInput("worksheetCsv"));
view.add(new Newline());
view.add(new FileInput("worksheetFile"));
view.add(new Newline());
view.add(new ActionInput("uploadWorksheet"));
}
if (state == State.REVIEW)
{
view.add(new Paragraph("<h2>Please review uploaded data:</h2>"));
view.add(new ActionInput("resetUpload", "reset"));
view.add(new ActionInput("saveUpload", "save"));
EntityTable s = new EntityTable("studies", studies.values(), false, "identifier", "contact_name");
s.setLabel("<h3>Studies:</h3>");
view.add(s);
EntityTable p = new EntityTable("persons", persons.values(), false, "name");
p.setLabel("<h3>Persons:</h3>");
view.add(p);
EntityTable sa = new EntityTable("samples", samples.values(), false, "study_identifier", "identifier",
"externalIdentifier", "sampletype");
sa.setLabel("<h3>Samples:</h3>");
view.add(sa);
EntityTable fc = new EntityTable("flowcells", flowcells.values(), false, "identifier", "machine_name",
"run", "startDate");
fc.setLabel("<h3>Flowcells:</h3>");
view.add(fc);
EntityTable l = new EntityTable("libraryLanes", liblanes.values(), false, "flowcell_identifier", "lane",
"barcode_name", "sample_identifier", "prepKit_name", "capturingKit_name");
l.setLabel("<h3>Libraries loaded on lanes</h3>");
view.add(l);
EntityTable m = new EntityTable("machines", machines.values(), false, "name");
m.setLabel("<h3>Machines used:</h3>");
view.add(m);
EntityTable b = new EntityTable("barcodes", barcodes.values(), false, "name", "barcodeType");
b.setLabel("<h3>Barcodes used:</h3>");
view.add(b);
EntityTable c = new EntityTable("capturingKits", capturingKits.values(), false, "name");
c.setLabel("<h3>Capturing kits used:</h3>");
view.add(c);
EntityTable k = new EntityTable("prepKits", prepKits.values(), false, "name");
k.setLabel("<h3>Prep kits used:</h3>");
view.add(k);
EntityTable a = new EntityTable("Arrays", arrays.values(), false, "identifier");
a.setLabel("<h3>ArrayFiles used:</h3>");
view.add(a);
}
return view;
}
}