/*************************************************************************
* *
* This file is part of the 20n/act project. *
* 20n/act enables DNA prediction for synthetic biology/bioengineering. *
* Copyright (C) 2017 20n Labs, Inc. *
* *
* Please direct all queries to act@20n.com. *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
*************************************************************************/
package com.act.lcms.db.io.writer;
import com.act.lcms.db.io.DB;
import com.act.lcms.db.model.DeliveredStrainWell;
import com.act.lcms.db.model.InductionWell;
import com.act.lcms.db.model.LCMSWell;
import com.act.lcms.db.model.Plate;
import com.act.lcms.db.model.PregrowthWell;
import com.act.lcms.db.model.StandardWell;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.List;
public class PlateCompositionWriter {
public static final String PLATE_TYPE_96_WELLS = "plate_96_well";
public static final String PLATE_TYPE_96_WELLS_BLOCK = "block_96_well";
public static final String PLATE_TYPE_24_WELLS = "plate_24_well";
public static final String PLATE_TYPE_24_WELLS_BLOCK = "block_24_well";
public static final Integer PLATE_DIM_96_WELL_X = 12;
public static final Integer PLATE_DIM_96_WELL_Y = 8;
public static final Integer PLATE_DIM_24_WELL_X = 6;
public static final Integer PLATE_DIM_24_WELL_Y = 4;
public static final List<String> PLATE_LABELS_SAMPLE_WELLS = Arrays.asList("msid", "composition", "chemical", "note");
public static final List<String> PLATE_LABELS_STANDARD_WELLS = Arrays.asList("chemical", "media");
public static final List<String> PLATE_LABELS_INDUCTION_WELLS =
Arrays.asList("msid", "chemical_source", "composition", "chemical", "strain_source", "note", "growth");
public static final List<String> PLATE_LABELS_PREGROWTH_WELLS =
Arrays.asList("source_plate", "msid", "composition", "source_well", "note", "growth");
public void writeLCMSWellComposition(PrintWriter w, Plate plate, List<LCMSWell> wells) throws IOException {
switch (plate.getPlateType()) {
case PLATE_TYPE_96_WELLS:
case PLATE_TYPE_96_WELLS_BLOCK:
String[][][] tables = new String[4][PLATE_DIM_96_WELL_Y][PLATE_DIM_96_WELL_X];
for (LCMSWell well : wells) {
int y = well.getPlateRow();
int x = well.getPlateColumn();
// TODO: should we use a map for this instead to make the table name -> data correspondence clearer?
tables[0][y][x] = well.getMsid();
tables[1][y][x] = well.getComposition();
tables[2][y][x] = well.getChemical();
tables[3][y][x] = well.getNote();
}
for (int i = 0; i < PLATE_LABELS_SAMPLE_WELLS.size(); i++) {
write96CellTable(w, PLATE_LABELS_SAMPLE_WELLS.get(i), tables[i]);
}
break;
default:
// TODO: use a better exception type here.
throw new RuntimeException(String.format("Found unexpected plate type: %s", plate.getPlateType()));
}
}
public void writeStandardWellComposition(PrintWriter w, Plate plate, List<StandardWell> wells)
throws IOException {
switch (plate.getPlateType()) {
case PLATE_TYPE_96_WELLS:
case PLATE_TYPE_96_WELLS_BLOCK:
String[][][] tables = new String[2][PLATE_DIM_96_WELL_Y][PLATE_DIM_96_WELL_X];
for (StandardWell well : wells) {
int y = well.getPlateRow();
int x = well.getPlateColumn();
// TODO: should we use a map for this instead to make the table name -> data correspondence clearer?
tables[0][y][x] = well.getChemical();
tables[1][y][x] = well.getMedia();
// TODO: add notes when we expect them.
}
for (int i = 0; i < PLATE_LABELS_STANDARD_WELLS.size(); i++) {
write96CellTable(w, PLATE_LABELS_STANDARD_WELLS.get(i), tables[i]);
}
break;
default:
// TODO: use a better exception type here.
throw new RuntimeException(String.format("Found unexpected plate type: %s", plate.getPlateType()));
}
}
public void writeDeliveredStrainWellComposition(PrintWriter w, Plate plate, List<DeliveredStrainWell> wells)
throws IOException {
switch (plate.getPlateType()) {
case PLATE_TYPE_96_WELLS:
case PLATE_TYPE_96_WELLS_BLOCK:
// Strain files don't use normal plate tables.
w.println(">>wells\tmsid\tcomposition");
for (DeliveredStrainWell well : wells) {
w.println(StringUtils.join(new String[] {
well.getWell(), well.getMsid(), well.getComposition()
}, "\t"));
}
w.println();
break;
default:
// TODO: use a better exception type here.
throw new RuntimeException(String.format("Found unexpected plate type: %s", plate.getPlateType()));
}
}
public void writeInductionWellComposition(PrintWriter w, Plate plate, List<InductionWell> wells)
throws IOException {
switch (plate.getPlateType()) {
case PLATE_TYPE_24_WELLS:
case PLATE_TYPE_24_WELLS_BLOCK:
String[][][] tables = new String[7][PLATE_DIM_24_WELL_Y][PLATE_DIM_24_WELL_X];
for (InductionWell well : wells) {
int y = well.getPlateRow();
int x = well.getPlateColumn();
tables[0][y][x] = well.getMsid();
tables[1][y][x] = well.getChemicalSource();
tables[2][y][x] = well.getComposition();
tables[3][y][x] = well.getChemical();
tables[4][y][x] = well.getStrainSource();
tables[5][y][x] = well.getNote();
tables[6][y][x] = well.getGrowth() == null ? "" : well.getGrowth().toString();
}
for (int i = 0; i < PLATE_LABELS_INDUCTION_WELLS.size(); i++) {
write24CellTable(w, PLATE_LABELS_INDUCTION_WELLS.get(i), tables[i]);
}
break;
default:
// TODO: use a better exception type here.
throw new RuntimeException(String.format("Found unexpected plate type: %s", plate.getPlateType()));
}
}
public void writePregrowthWellComposition(PrintWriter w, Plate plate, List<PregrowthWell> wells)
throws IOException {
switch (plate.getPlateType()) {
case PLATE_TYPE_24_WELLS:
case PLATE_TYPE_24_WELLS_BLOCK:
String[][][] tables = new String[6][PLATE_DIM_24_WELL_Y][PLATE_DIM_24_WELL_X];
for (PregrowthWell well : wells) {
int y = well.getPlateRow();
int x = well.getPlateColumn();
tables[0][y][x] = well.getSourcePlate();
tables[1][y][x] = well.getMsid();
tables[2][y][x] = well.getComposition();
tables[3][y][x] = well.getSourceWell();
tables[4][y][x] = well.getNote();
tables[5][y][x] = well.getGrowth() == null ? "" : well.getGrowth().toString();
}
for (int i = 0; i < PLATE_LABELS_PREGROWTH_WELLS.size(); i++) {
write24CellTable(w, PLATE_LABELS_PREGROWTH_WELLS.get(i), tables[i]);
}
break;
default:
// TODO: use a better exception type here.
throw new RuntimeException(String.format("Found unexpected plate type: %s", plate.getPlateType()));
}
}
protected void writePlateAttributes(PrintWriter w, Plate plate) throws IOException {
w.format(">%s\t%s\n", "name", plate.getName());
w.format(">%s\t%s\n", "description", plate.getDescription());
w.format(">%s\t%s\n", "barcode", plate.getBarcode());
w.println(">schema\tplate\n");
w.format(">%s\t%s\n", "location", plate.getLocation());
w.format(">%s\t%s\n", "plate_type", plate.getPlateType());
if (plate.getSolvent() != null) {
w.format(">%s\t%s\n", "solvent", plate.getSolvent());
}
w.format(">%s\t%d\n", "temperature", plate.getTemperature());
w.println();
}
protected void write96CellTable(PrintWriter w, String tableName, String[][] values) {
writeCellTable(w, tableName, values, PLATE_DIM_96_WELL_X, PLATE_DIM_96_WELL_Y);
}
protected void write24CellTable(PrintWriter w, String tableName, String[][] values) {
writeCellTable(w, tableName, values, PLATE_DIM_24_WELL_X, PLATE_DIM_24_WELL_Y);
}
protected void writeCellTable(PrintWriter w, String tableName, String[][] values,
int expectedXDim, int expectedYDim) {
StringBuilder headerBuilder = new StringBuilder(String.format(">>%s", tableName));
for (int i = 0; i < expectedXDim; i++) {
// Columns are one-indexed in composition tables.
headerBuilder.append(String.format("\t%d", i + 1));
}
w.println(headerBuilder.toString());
if (values.length != expectedYDim) {
throw new RuntimeException(String.format(
"ERROR: found incorrect row dimension for 96 well plate. Expected %d, but got %d.\n",
expectedYDim, values.length));
}
char rowName = 'A';
for (int i = 0; i < values.length; i++) {
if (values[i].length != expectedXDim) {
throw new RuntimeException(String.format(
"ERROR: found incorrect column dimension for 96 well plate. Expected %d, but got %d.\n",
expectedXDim, values.length));
}
w.format("%c\t%s\n", rowName, StringUtils.join(values[i], '\t'));
rowName++;
}
w.println();
}
public void writePlateCompositionByBarcode(DB db, String plateBarcode, Writer dest) throws Exception {
Plate p = Plate.getPlateByBarcode(db, plateBarcode);
writePlateComposition(db, p, dest);
}
public void writePlateCompositionByName(DB db, String name, Writer dest) throws Exception {
Plate p = Plate.getPlateByName(db, name);
writePlateComposition(db, p, dest);
}
public void writePlateComposition(DB db, Plate p, Writer dest) throws Exception {
PlateCompositionWriter writer = new PlateCompositionWriter();
PrintWriter w = new PrintWriter(dest, true);
writePlateAttributes(w, p);
switch (p.getContentType()) {
case LCMS:
List<LCMSWell> LCMSWells = LCMSWell.getInstance().getByPlateId(db, p.getId());
writer.writeLCMSWellComposition(w, p, LCMSWells);
break;
case STANDARD:
List<StandardWell> stdWells = StandardWell.getInstance().getByPlateId(db, p.getId());
writer.writeStandardWellComposition(w, p, stdWells);
break;
case DELIVERED_STRAIN:
List<DeliveredStrainWell> strainWells = DeliveredStrainWell.getInstance().getByPlateId(db, p.getId());
writer.writeDeliveredStrainWellComposition(w, p, strainWells);
break;
case INDUCTION:
List<InductionWell> inductionWells = InductionWell.getInstance().getByPlateId(db, p.getId());
writer.writeInductionWellComposition(w, p, inductionWells);
break;
case PREGROWTH:
List<PregrowthWell> pregrowthWells = PregrowthWell.getInstance().getByPlateId(db, p.getId());
writer.writePregrowthWellComposition(w, p, pregrowthWells);
break;
default:
throw new RuntimeException(String.format("Unrecognized table type: %s", p.getContentType()));
}
}
}