/************************************************************************* * * * 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.model; import com.act.lcms.db.io.DB; import com.act.lcms.db.io.parser.PlateCompositionParser; import org.apache.commons.lang3.tuple.Pair; import java.io.IOException; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; public class InductionWell extends PlateWell<InductionWell> { public static final String TABLE_NAME = "wells_induction"; protected static final InductionWell INSTANCE = new InductionWell(); public static InductionWell getInstance() { return INSTANCE; } private enum DB_FIELD implements DBFieldEnumeration { ID(1, -1, "id"), PLATE_ID(2, 1, "plate_id"), PLATE_ROW(3, 2, "plate_row"), PLATE_COLUMN(4, 3, "plate_column"), MSID(5, 4, "msid"), CHEMICAL_SOURCE(6, 5, "chemical_source"), COMPOSITION(7, 6, "composition"), CHEMICAL(8, 7, "chemical"), STRAIN_SOURCE(9, 8, "strain_source"), NOTE(10, 9, "note"), GROWTH(11, 10, "growth"), ; private final int offset; private final int insertUpdateOffset; private final String fieldName; DB_FIELD(int offset, int insertUpdateOffset, String fieldName) { this.offset = offset; this.insertUpdateOffset = insertUpdateOffset; this.fieldName = fieldName; } @Override public int getOffset() { return offset; } @Override public int getInsertUpdateOffset() { return insertUpdateOffset; } @Override public String getFieldName() { return fieldName; } @Override public String toString() { return this.fieldName; } public static String[] names() { DB_FIELD[] values = DB_FIELD.values(); String[] names = new String[values.length]; for (int i = 0; i < values.length; i++) { names[i] = values[i].getFieldName(); } return names; } } protected static final List<String> ALL_FIELDS = Collections.unmodifiableList(Arrays.asList(DB_FIELD.names())); // id is auto-generated on insertion. protected static final List<String> INSERT_UPDATE_FIELDS = INSTANCE.makeInsertUpdateFields(); @Override public String getTableName() { return TABLE_NAME; } @Override public List<String> getAllFields() { return ALL_FIELDS; } @Override public List<String> getInsertUpdateFields() { return INSERT_UPDATE_FIELDS; } protected static final String GET_BY_ID_QUERY = INSTANCE.makeGetByIDQuery(); @Override protected String getGetByIDQuery() { return GET_BY_ID_QUERY; } protected static final String GET_BY_PLATE_ID_QUERY = INSTANCE.makeGetByPlateIDQuery(); @Override protected String getGetByPlateIDQuery() { return GET_BY_PLATE_ID_QUERY; } protected static final String INSERT_QUERY = INSTANCE.makeInsertQuery(); @Override public String getInsertQuery() { return INSERT_QUERY; } protected static final String UPDATE_QUERY = INSTANCE.makeUpdateQuery(); @Override public String getUpdateQuery() { return UPDATE_QUERY; } @Override protected List<InductionWell> fromResultSet(ResultSet resultSet) throws SQLException { List<InductionWell> results = new ArrayList<>(); while (resultSet.next()) { Integer id = resultSet.getInt(DB_FIELD.ID.getOffset()); Integer plateId = resultSet.getInt(DB_FIELD.PLATE_ID.getOffset()); Integer plateRow = resultSet.getInt(DB_FIELD.PLATE_ROW.getOffset()); Integer plateColumn = resultSet.getInt(DB_FIELD.PLATE_COLUMN.getOffset()); String msid = resultSet.getString(DB_FIELD.MSID.getOffset()); String chemicalSource = resultSet.getString(DB_FIELD.CHEMICAL_SOURCE.getOffset()); String composition = resultSet.getString(DB_FIELD.COMPOSITION.getOffset()); String chemical = resultSet.getString(DB_FIELD.CHEMICAL.getOffset()); String strainSource = resultSet.getString(DB_FIELD.STRAIN_SOURCE.getOffset()); String note = resultSet.getString(DB_FIELD.NOTE.getOffset()); Integer growth = resultSet.getInt(DB_FIELD.GROWTH.getOffset()); if (resultSet.wasNull()) { growth = null; } results.add(new InductionWell(id, plateId, plateRow, plateColumn, msid, chemicalSource, composition, chemical, strainSource, note, growth)); } return results; } // Insert/Update protected void bindInsertOrUpdateParameters( PreparedStatement stmt, Integer plateId, Integer plateRow, Integer plateColumn, String msid, String chemicalSource, String composition, String chemical, String strainSource, String note, Integer growth) throws SQLException { stmt.setInt(DB_FIELD.PLATE_ID.getInsertUpdateOffset(), plateId); stmt.setInt(DB_FIELD.PLATE_ROW.getInsertUpdateOffset(), plateRow); stmt.setInt(DB_FIELD.PLATE_COLUMN.getInsertUpdateOffset(), plateColumn); stmt.setString(DB_FIELD.MSID.getInsertUpdateOffset(), msid); stmt.setString(DB_FIELD.CHEMICAL_SOURCE.getInsertUpdateOffset(), chemicalSource); stmt.setString(DB_FIELD.COMPOSITION.getInsertUpdateOffset(), composition); stmt.setString(DB_FIELD.CHEMICAL.getInsertUpdateOffset(), chemical); stmt.setString(DB_FIELD.STRAIN_SOURCE.getInsertUpdateOffset(), strainSource); stmt.setString(DB_FIELD.NOTE.getInsertUpdateOffset(), note); if (growth == null) { stmt.setNull(DB_FIELD.GROWTH.getInsertUpdateOffset(), Types.INTEGER); } else { stmt.setInt(DB_FIELD.GROWTH.getInsertUpdateOffset(), growth); } } @Override protected void bindInsertOrUpdateParameters(PreparedStatement stmt, InductionWell sw) throws SQLException { bindInsertOrUpdateParameters(stmt, sw.getPlateId(), sw.getPlateRow(), sw.getPlateColumn(), sw.getMsid(), sw.getChemicalSource(), sw.getComposition(), sw.getChemical(), sw.getStrainSource(), sw.getNote(), sw.getGrowth()); } public InductionWell insert( DB db, Integer plateId, Integer plateRow, Integer plateColumn, String msid, String chemicalSource, String composition, String chemical, String strainSource, String note, Integer growth) throws SQLException, IOException { return INSTANCE.insert(db, new InductionWell(null, plateId, plateRow, plateColumn, msid, chemicalSource, composition, chemical, strainSource, note, growth)); } // Parsing/loading // TODO: remove this once this growth specification is no longer used. private static final Map<String, Integer> PLUS_MINUS_GROWTH_TO_INT = Collections.unmodifiableMap( new HashMap<String, Integer>() {{ put("---", 0); put("--", 1); put("-", 2); put("+", 3); put("++", 4); put("+++", 5); }}); public List<InductionWell> insertFromPlateComposition(DB db, PlateCompositionParser parser, Plate p) throws SQLException, IOException { Map<String, String> plateAttributes = parser.getPlateProperties(); Map<Pair<String, String>, String> msids = parser.getCompositionTables().get("msid"); List<Pair<String, String>> sortedCoordinates = new ArrayList<>(msids.keySet()); Collections.sort(sortedCoordinates, new Comparator<Pair<String, String>>() { // TODO: parse the values of these pairs as we read them so we don't need this silly comparator. @Override public int compare(Pair<String, String> o1, Pair<String, String> o2) { if (o1.getKey().equals(o2.getKey())) { return Integer.valueOf(Integer.parseInt(o1.getValue())).compareTo(Integer.parseInt(o2.getValue())); } return o1.getKey().compareTo(o2.getKey()); } }); List<InductionWell> results = new ArrayList<>(); for (Pair<String, String> coords : sortedCoordinates) { String msid = msids.get(coords); if (msid == null || msid.isEmpty()) { continue; } String chemicalSource = parser.getCompositionTables().get("chemical_source").get(coords); String composition = parser.getCompositionTables().get("composition").get(coords); String chemical = parser.getCompositionTables().get("chemical").get(coords); String strainSource = parser.getCompositionTables().get("strain_source").get(coords); String note = null; if (parser.getCompositionTables().get("note") != null) { note = parser.getCompositionTables().get("note").get(coords); } // TODO: ditch this when we start using floating point numbers for growth values. Integer growth = null; Map<Pair<String, String>, String> growthTable = parser.getCompositionTables().get("growth"); if (growthTable == null || growthTable.get(coords) == null) { String plateGrowth = plateAttributes.get("growth"); if (plateGrowth != null) { growth = Integer.parseInt(trimAndComplain(plateGrowth)); } } else { String growthStr = growthTable.get(coords); if (PLUS_MINUS_GROWTH_TO_INT.containsKey(growthStr)) { growth = PLUS_MINUS_GROWTH_TO_INT.get(growthStr); } else { growth = Integer.parseInt(growthStr); // If it's not using +/- format, it should be an integer from 1-5. } } Pair<Integer, Integer> index = parser.getCoordinatesToIndices().get(coords); InductionWell s = INSTANCE.insert(db, p.getId(), index.getLeft(), index.getRight(), msid, chemicalSource, composition, chemical, strainSource, note, growth); results.add(s); } return results; } public static String trimAndComplain(String val) { String tval = val.trim(); if (!val.equals(tval)) { System.err.format("WARNING: trimmed spurious whitespace from '%s'\n", val); } return tval; } private String msid; private String chemicalSource; private String composition; private String chemical; private String strainSource; private String note; private Integer growth; private InductionWell() { } protected InductionWell(Integer id, Integer plateId, Integer plateRow, Integer plateColumn, String msid, String chemicalSource, String composition, String chemical, String strainSource, String note, Integer growth) { this.id = id; this.plateId = plateId; this.plateRow = plateRow; this.plateColumn = plateColumn; this.msid = msid; this.chemicalSource = chemicalSource; this.composition = composition; this.chemical = chemical; this.strainSource = strainSource; this.note = note; this.growth = growth; } public String getMsid() { return msid; } public void setMsid(String msid) { this.msid = msid; } public String getChemicalSource() { return chemicalSource; } public void setChemicalSource(String chemicalSource) { this.chemicalSource = chemicalSource; } public String getComposition() { return composition; } public void setComposition(String composition) { this.composition = composition; } public String getChemical() { return chemical; } public void setChemical(String chemical) { this.chemical = chemical; } public String getStrainSource() { return strainSource; } public void setStrainSource(String strainSource) { this.strainSource = strainSource; } public String getNote() { return note; } public void setNote(String note) { this.note = note; } public Integer getGrowth() { return growth; } public void setGrowth(Integer growth) { this.growth = growth; } }