com.act.lcms.db.model.MS1ScanForWellAndMassCharge.java Source code

Java tutorial

Introduction

Here is the source code for com.act.lcms.db.model.MS1ScanForWellAndMassCharge.java

Source

/*************************************************************************
*                                                                        *
*  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.MS1;
import com.act.lcms.XZ;
import com.act.lcms.db.io.DB;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class MS1ScanForWellAndMassCharge extends BaseDBModel<MS1ScanForWellAndMassCharge> implements Serializable {

    private static final long serialVersionUID = 8606939292070032578L;

    public static final String TABLE_NAME = "ms1_for_well_and_mass_charge";

    protected static final MS1ScanForWellAndMassCharge INSTANCE = new MS1ScanForWellAndMassCharge();

    public static MS1ScanForWellAndMassCharge 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"), USE_SNR(5, 4, "use_snr"), SCAN_FILE(6, 5, "scan_file"), CHEMICAL_NAME(7, 6,
                        "chemical_name"), METLIN_IONS(8, 7, "metlin_ions"), IONS_TO_SPECTRA(9, 8,
                                "ions_to_spectra"), IONS_TO_INTEGRAL(10, 9, "ions_to_integral"), IONS_TO_MAX(11, 10,
                                        "ions_to_max"), IONS_TO_LOG_SNR(12, 11,
                                                "ions_to_log_snr"), IONS_TO_AVG_SIGNAL(13, 12,
                                                        "ions_to_avg_signal"), IONS_TO_AVG_AMBIENT(14, 13,
                                                                "ions_to_avg_ambient"), INDIVIDUAL_MAX_INTENSITIES(
                                                                        15, 14,
                                                                        "individual_max_intensities"), MAX_Y_AXIS(
                                                                                16, 15, "max_y_axis");

        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 = Collections
            .unmodifiableList(ALL_FIELDS.subList(1, ALL_FIELDS.size()));

    @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 = MS1ScanForWellAndMassCharge.getInstance().makeGetByIDQuery();

    @Override
    protected String getGetByIDQuery() {
        return GET_BY_ID_QUERY;
    }

    protected static final String INSERT_QUERY = MS1ScanForWellAndMassCharge.getInstance().makeInsertQuery();

    @Override
    public String getInsertQuery() {
        return INSERT_QUERY;
    }

    protected static final String UPDATE_QUERY = MS1ScanForWellAndMassCharge.getInstance().makeUpdateQuery();

    @Override
    public String getUpdateQuery() {
        return UPDATE_QUERY;
    }

    @Override
    protected List<MS1ScanForWellAndMassCharge> fromResultSet(ResultSet resultSet)
            throws SQLException, IOException, ClassNotFoundException {
        List<MS1ScanForWellAndMassCharge> 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());
            Double maxYAxis = resultSet.getDouble(DB_FIELD.MAX_Y_AXIS.getOffset());
            Boolean useSNR = resultSet.getBoolean(DB_FIELD.USE_SNR.getOffset());
            String lcmsScanFilePath = resultSet.getString(DB_FIELD.SCAN_FILE.getOffset());
            String chemicalName = resultSet.getString(DB_FIELD.CHEMICAL_NAME.getOffset());
            List<String> metlinIons = MS1ScanForWellAndMassCharge
                    .deserializeMetlinIons(resultSet.getString(DB_FIELD.METLIN_IONS.getOffset()));
            Map<String, List<XZ>> ionsToSpectra = MS1ScanForWellAndMassCharge
                    .deserialize(resultSet.getBytes(DB_FIELD.IONS_TO_SPECTRA.getOffset()));
            Map<String, Double> ionsToIntegral = MS1ScanForWellAndMassCharge
                    .deserialize(resultSet.getBytes(DB_FIELD.IONS_TO_INTEGRAL.getOffset()));
            Map<String, Double> ionsToMax = MS1ScanForWellAndMassCharge
                    .deserialize(resultSet.getBytes(DB_FIELD.IONS_TO_MAX.getOffset()));
            Map<String, Double> ionsToLogSNR = MS1ScanForWellAndMassCharge
                    .deserialize(resultSet.getBytes(DB_FIELD.IONS_TO_LOG_SNR.getOffset()));
            Map<String, Double> ionsToAvgSignal = MS1ScanForWellAndMassCharge
                    .deserialize(resultSet.getBytes(DB_FIELD.IONS_TO_AVG_SIGNAL.getOffset()));
            Map<String, Double> ionsToAvgAmbient = MS1ScanForWellAndMassCharge
                    .deserialize(resultSet.getBytes(DB_FIELD.IONS_TO_AVG_AMBIENT.getOffset()));
            Map<String, Double> individualMaxIntensities = MS1ScanForWellAndMassCharge
                    .deserialize(resultSet.getBytes(DB_FIELD.INDIVIDUAL_MAX_INTENSITIES.getOffset()));

            results.add(new MS1ScanForWellAndMassCharge(id, plateId, plateColumn, plateRow, useSNR,
                    lcmsScanFilePath, chemicalName, metlinIons, ionsToSpectra, ionsToIntegral, ionsToMax,
                    ionsToLogSNR, ionsToAvgSignal, ionsToAvgAmbient, individualMaxIntensities, maxYAxis));
        }

        return results;
    }

    protected void bindInsertOrUpdateParameters(PreparedStatement stmt, Integer plateId, Integer plateRow,
            Integer plateColumn, Boolean useSNR, String lcmsScanFileDir, String chemicalName,
            List<String> metlinIons, Map<String, List<XZ>> ionsToSpectra, Map<String, Double> ionsToIntegral,
            Map<String, Double> ionsToMax, Map<String, Double> ionsToLogSNR, Map<String, Double> ionsToAvgSignal,
            Map<String, Double> ionsToAvgAmbient, Map<String, Double> individualMaxIntensities, Double maxYAxis)
            throws SQLException, IOException {
        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.setBoolean(DB_FIELD.USE_SNR.getInsertUpdateOffset(), useSNR);
        stmt.setString(DB_FIELD.SCAN_FILE.getInsertUpdateOffset(), lcmsScanFileDir);
        stmt.setString(DB_FIELD.CHEMICAL_NAME.getInsertUpdateOffset(), chemicalName);
        stmt.setString(DB_FIELD.METLIN_IONS.getInsertUpdateOffset(), OBJECT_MAPPER.writeValueAsString(metlinIons));
        stmt.setBytes(DB_FIELD.IONS_TO_SPECTRA.getInsertUpdateOffset(), serialize(ionsToSpectra));
        stmt.setBytes(DB_FIELD.IONS_TO_INTEGRAL.getInsertUpdateOffset(), serialize(ionsToIntegral));
        stmt.setBytes(DB_FIELD.IONS_TO_LOG_SNR.getInsertUpdateOffset(), serialize(ionsToLogSNR));
        stmt.setBytes(DB_FIELD.IONS_TO_AVG_AMBIENT.getInsertUpdateOffset(), serialize(ionsToAvgAmbient));
        stmt.setBytes(DB_FIELD.IONS_TO_AVG_SIGNAL.getInsertUpdateOffset(), serialize(ionsToAvgSignal));
        stmt.setBytes(DB_FIELD.INDIVIDUAL_MAX_INTENSITIES.getInsertUpdateOffset(),
                serialize(individualMaxIntensities));
        stmt.setBytes(DB_FIELD.IONS_TO_MAX.getInsertUpdateOffset(), serialize(ionsToMax));
        stmt.setDouble(DB_FIELD.MAX_Y_AXIS.getInsertUpdateOffset(), maxYAxis);
    }

    @Override
    protected void bindInsertOrUpdateParameters(PreparedStatement stmt, MS1ScanForWellAndMassCharge ms1Result)
            throws SQLException, IOException {
        bindInsertOrUpdateParameters(stmt, ms1Result.getPlateId(), ms1Result.getPlateRow(),
                ms1Result.getPlateColumn(), ms1Result.getUseSNR(), ms1Result.getScanFilePath(),
                ms1Result.getChemicalName(), ms1Result.getMetlinIons(), ms1Result.getIonsToSpectra(),
                ms1Result.getIonsToIntegral(), ms1Result.getIonsToMax(), ms1Result.getIonsToLogSNR(),
                ms1Result.getIonsToAvgSignal(), ms1Result.getIonsToAvgAmbient(),
                ms1Result.getIndividualMaxIntensities(), ms1Result.getMaxYAxis());
    }

    private Integer id;
    private Integer plateId;
    private Integer plateRow;
    private Integer plateColumn;
    private Boolean useSNR;
    private String lcmsScanFileDir;
    private String chemicalName;
    private List<String> metlinIons = new ArrayList<>();
    private Map<String, List<XZ>> ionsToSpectra = new HashMap<>();
    private Map<String, Double> ionsToIntegral = new HashMap<>();
    private Map<String, Double> ionsToMax = new HashMap<>();
    private Map<String, Double> ionsToLogSNR = new HashMap<>();
    private Map<String, Double> ionsToAvgSignal = new HashMap<>();
    private Map<String, Double> ionsToAvgAmbient = new HashMap<>();
    private Map<String, Double> individualMaxIntensities = new HashMap<>();
    private Double maxYAxis = 0.0d; // default to 0

    public MS1ScanForWellAndMassCharge() {
    }

    public MS1ScanForWellAndMassCharge(Integer id, Integer plateId, Integer plateColumn, Integer plateRow,
            Boolean useSNR, String lcmsScanFileDir, String chemicalName, List<String> metlinIons,
            Map<String, List<XZ>> ionsToSpectra, Map<String, Double> ionsToIntegral, Map<String, Double> ionsToMax,
            Map<String, Double> ionsToAvgSignal, Map<String, Double> ionsToAvgAmbient,
            Map<String, Double> ionsToLogSNR, Map<String, Double> individualMaxIntensities, Double maxYAxis) {
        this.id = id;
        this.useSNR = useSNR;
        this.lcmsScanFileDir = lcmsScanFileDir;
        this.chemicalName = chemicalName;
        this.ionsToSpectra = ionsToSpectra;
        this.ionsToAvgAmbient = ionsToAvgAmbient;
        this.ionsToAvgSignal = ionsToAvgSignal;
        this.ionsToIntegral = ionsToIntegral;
        this.individualMaxIntensities = individualMaxIntensities;
        this.maxYAxis = maxYAxis;
        this.ionsToMax = ionsToMax;
        this.plateRow = plateColumn;
        this.plateId = plateId;
        this.plateRow = plateRow;
        this.ionsToLogSNR = ionsToLogSNR;
        this.metlinIons = metlinIons;
    }

    /**
     * Serialize an object to an array of Serialized, gzip'd bytes.
     *
     * Note that this returns a byte stream (a) to be symmetrical with deserialize, and (b) because we anticipate
     * manifesting the entire byte array at some point so there's no advantage to streaming the results.  If that changes
     * and performance suffers from allocating the entire byte array, we can use byte streams instead (and we'll probably
     * have bigger performance problems to deal with anyway).
     *
     * @param object The object to serialize
     * @param <T> The type of the object (unbound to allow serialization of Maps, which sadly don't explicitly implement
     *            Serializable).
     * @return A byte array representing a compressed object stream for the specified object.
     * @throws IOException
     */
    private static <T> byte[] serialize(T object) throws IOException {
        ByteArrayOutputStream postGzipOutputStream = new ByteArrayOutputStream();

        try (ObjectOutputStream out = new ObjectOutputStream(new GZIPOutputStream(postGzipOutputStream))) {
            out.writeObject(object);
        }

        return postGzipOutputStream.toByteArray();
    }

    private static <T> T deserialize(byte[] object) throws IOException, ClassNotFoundException {
        T map = null;

        try (ObjectInputStream ois = new ObjectInputStream(new GZIPInputStream(new ByteArrayInputStream(object)))) {
            // TODO: consider checking this cast?  Though we'd just throw an exception anyway, so...
            map = (T) ois.readObject();
        }

        return map;
    }

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final TypeReference<List<String>> TYPE_REFERENCE_FOR_METLIN_IONS = new TypeReference<List<String>>() {
    };

    private static List<String> deserializeMetlinIons(String serializedMetlinIons) throws IOException {
        return OBJECT_MAPPER.readValue(serializedMetlinIons, TYPE_REFERENCE_FOR_METLIN_IONS);
    }

    public MS1ScanForWellAndMassCharge insert(DB db, MS1ScanForWellAndMassCharge ms1Result)
            throws SQLException, IOException {

        Connection conn = db.getConn();
        try (PreparedStatement stmt = conn.prepareStatement(
                MS1ScanForWellAndMassCharge.getInstance().getInsertQuery(), Statement.RETURN_GENERATED_KEYS)) {

            bindInsertOrUpdateParameters(stmt, ms1Result.getPlateId(), ms1Result.getPlateRow(),
                    ms1Result.getPlateColumn(), ms1Result.getUseSNR(), ms1Result.getScanFilePath(),
                    ms1Result.getChemicalName(), ms1Result.getMetlinIons(), ms1Result.getIonsToSpectra(),
                    ms1Result.getIonsToIntegral(), ms1Result.getIonsToMax(), ms1Result.getIonsToAvgSignal(),
                    ms1Result.getIonsToAvgAmbient(), ms1Result.getIonsToLogSNR(),
                    ms1Result.getIndividualMaxIntensities(), ms1Result.getMaxYAxis());

            stmt.executeUpdate();
            try (ResultSet resultSet = stmt.getGeneratedKeys()) {
                if (resultSet.next()) {
                    // Get auto-generated id.
                    int id = resultSet.getInt(1);
                    ms1Result.setId(id);
                    return ms1Result;
                } else {
                    System.err.format("ERROR: could not retrieve autogenerated key for ms1 scan result\n");
                    return null;
                }
            }
        }
    }

    // There are no update queries since this model is being used a compute chache of the results of the
    // ms1 scan results, where the results are immutable given the same parameters.

    // Extra access patterns.
    public MS1ScanForWellAndMassCharge getByPlateIdPlateRowPlateColUseSnrScanFileChemical(DB db, Plate plate,
            PlateWell well, Boolean useSnr, ScanFile scanFile, String chemicalName, Map<String, Double> metlinIons,
            File lcmsFile) throws Exception {

        // Pre-process the list of metlin ions
        List<String> ions = new ArrayList<>();
        ions.addAll(metlinIons.keySet());
        Collections.sort(ions);

        MS1ScanForWellAndMassCharge result = this
                .getByPlateIdPlateRowPlateColUseSnrScanFileChemicalMetlinIonsFromDb(db, plate, well, useSnr,
                        scanFile.getFilename(), chemicalName, ions);

        if (result == null) {
            // couldn't find entry in the cache
            MS1ScanForWellAndMassCharge construct = getMS1(lcmsFile.getAbsolutePath(), metlinIons);
            construct.setPlateCoordinates(plate.getId(), well.getPlateRow(), well.getPlateColumn());
            construct.setScanFilePath(scanFile.getFilename());
            construct.setUseSnr(useSnr);
            construct.setMetlinIons(ions);
            construct.setChemicalName(chemicalName);
            return insert(db, construct);
        } else {
            return result;
        }
    }

    private static final String GET_BY_PLATE_ID_AND_PLATE_ROW_AND_PLATE_COL_AND_USE_SNR_AND_SCAN_FILE_PATH_AND_CHEMICAL_AND_METLIN_IONS = StringUtils
            .join(new String[] { "SELECT",
                    StringUtils.join(MS1ScanForWellAndMassCharge.getInstance().getAllFields(), ','), "from",
                    MS1ScanForWellAndMassCharge.getInstance().getTableName(), "where plate_id = ?",
                    "  and plate_row = ?", "  and plate_column = ?", "  and use_snr = ?", "  and scan_file = ?",
                    "  and chemical_name = ?", "  and metlin_ions = ?", }, " ");

    private MS1ScanForWellAndMassCharge getByPlateIdPlateRowPlateColUseSnrScanFileChemicalMetlinIonsFromDb(DB db,
            Plate plate, PlateWell well, Boolean useSnr, String scanFile, String chemicalName,
            List<String> metlinIons) throws Exception {
        try (PreparedStatement stmt = db.getConn().prepareStatement(
                GET_BY_PLATE_ID_AND_PLATE_ROW_AND_PLATE_COL_AND_USE_SNR_AND_SCAN_FILE_PATH_AND_CHEMICAL_AND_METLIN_IONS)) {
            stmt.setInt(1, plate.getId());
            stmt.setInt(2, well.getPlateRow());
            stmt.setInt(3, well.getPlateColumn());
            stmt.setBoolean(4, useSnr);
            stmt.setString(5, scanFile);
            stmt.setString(6, chemicalName);
            stmt.setString(7, OBJECT_MAPPER.writeValueAsString(metlinIons));

            try (ResultSet resultSet = stmt.executeQuery()) {
                MS1ScanForWellAndMassCharge result = expectOneResult(resultSet,
                        String.format(
                                "plate_id = %d, plate_row = %d, plate_column = %d, use_snr = %s, scan_file = %s",
                                plate.getId(), well.getPlateRow(), well.getPlateColumn(), useSnr, scanFile));
                return result;
            }
        }
    }

    private MS1ScanForWellAndMassCharge getMS1(String ms1File, Map<String, Double> metlinIons) throws Exception {
        MS1 ms1 = new MS1();
        return ms1.getMS1(metlinIons, ms1File);
    }

    public Integer getPlateId() {
        return plateId;
    }

    public Integer getPlateRow() {
        return plateRow;
    }

    public Integer getPlateColumn() {
        return plateColumn;
    }

    public String getScanFilePath() {
        return lcmsScanFileDir;
    }

    public Boolean getUseSNR() {
        return useSNR;
    }

    public Double getMaxYAxis() {
        return maxYAxis;
    }

    public Map<String, Double> getIndividualMaxYAxis() {
        return individualMaxIntensities;
    }

    public Double getMaxIntensityForIon(String ion) {
        return ionsToMax.get(ion);
    }

    public List<String> getMetlinIons() {
        return metlinIons;
    }

    public Map<String, List<XZ>> getIonsToSpectra() {
        return ionsToSpectra;
    }

    public Map<String, Double> getIonsToIntegral() {
        return ionsToIntegral;
    }

    public Map<String, Double> getIonsToAvgSignal() {
        return ionsToAvgSignal;
    }

    public Map<String, Double> getIonsToMax() {
        return ionsToMax;
    }

    public Map<String, Double> getIonsToLogSNR() {
        return ionsToLogSNR;
    }

    public Map<String, Double> getIonsToAvgAmbient() {
        return ionsToAvgAmbient;
    }

    public Map<String, Double> getIndividualMaxIntensities() {
        return individualMaxIntensities;
    }

    public void setMaxIntensityForIon(String ion, Double max) {
        this.ionsToMax.put(ion, max);
    }

    public Double getLogSNRForIon(String ion) {
        return ionsToLogSNR.get(ion);
    }

    public String getChemicalName() {
        return chemicalName;
    }

    public void setLogSNRForIon(String ion, Double logsnr) {
        this.ionsToLogSNR.put(ion, logsnr);
    }

    public void setMetlinIons(List<String> metlinIons) {
        this.metlinIons = metlinIons;
    }

    public void setAvgIntensityForIon(String ion, Double avgSignal, Double avgAmbient) {
        this.ionsToAvgSignal.put(ion, avgSignal);
        this.ionsToAvgAmbient.put(ion, avgAmbient);
    }

    // This function is set to private since it is only needed by this class to set
    // the plate coordinates.
    private void setPlateCoordinates(Integer plateId, Integer plateRow, Integer plateColumn) {
        this.plateId = plateId;
        this.plateRow = plateRow;
        this.plateColumn = plateColumn;
    }

    public void setUseSnr(Boolean useSNR) {
        this.useSNR = useSNR;
    }

    public void setScanFilePath(String scanFilePath) {
        this.lcmsScanFileDir = scanFilePath;
    }

    public Double getIntegralForIon(String ion) {
        return ionsToIntegral.get(ion);
    }

    public void setIntegralForIon(String ion, Double area) {
        this.ionsToIntegral.put(ion, area);
    }

    public void setMaxYAxis(Double maxYAxis) {
        this.maxYAxis = maxYAxis;
    }

    public void setIndividualMaxIntensities(Map<String, Double> individualMaxIntensities) {
        this.individualMaxIntensities = individualMaxIntensities;
    }

    public void setChemicalName(String name) {
        this.chemicalName = name;
    }
}