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

Java tutorial

Introduction

Here is the source code for com.act.lcms.db.model.StandardIonResult.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.XZ;
import com.act.lcms.db.analysis.StandardIonAnalysis;
import com.act.lcms.db.io.DB;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
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.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

public class StandardIonResult extends BaseDBModel<StandardIonResult> {

    public static final String TABLE_NAME = "standard_ion_result";

    protected static final StandardIonResult INSTANCE = new StandardIonResult();

    public static StandardIonResult getInstance() {
        return INSTANCE;
    }

    private enum DB_FIELD implements DBFieldEnumeration {
        ID(1, -1, "id"), CHEMICAL(2, 1, "chemical"), STANDARD_WELL_ID(3, 2, "standard_well_id"), NEGATIVE_WELL_IDS(
                4, 3,
                "negative_well_ids"), STANDARD_ION_RESULTS(5, 4, "standard_ion_results"), PLOTTING_RESULT_PATHS(6,
                        5, "plotting_result_paths"), BEST_METLIN_ION(7, 6,
                                "best_metlin_ion"), MANUAL_OVERRIDE(8, 7, "manual_override");

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

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

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

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

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

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

    @Override
    protected List<StandardIonResult> fromResultSet(ResultSet resultSet)
            throws SQLException, IOException, ClassNotFoundException {
        List<StandardIonResult> results = new ArrayList<>();
        while (resultSet.next()) {
            Integer id = resultSet.getInt(DB_FIELD.ID.getOffset());
            String chemical = resultSet.getString(DB_FIELD.CHEMICAL.getOffset());
            Integer standardWellId = resultSet.getInt(DB_FIELD.STANDARD_WELL_ID.getOffset());
            List<Integer> negativeWellIds = StandardIonResult
                    .deserializeNegativeWellIds(resultSet.getString(DB_FIELD.NEGATIVE_WELL_IDS.getOffset()));
            LinkedHashMap<String, XZ> analysisResults = StandardIonResult.deserializeStandardIonAnalysisResult(
                    resultSet.getString(DB_FIELD.STANDARD_ION_RESULTS.getOffset()));
            Map<String, String> plottingResultFilePaths = StandardIonResult
                    .deserializePlottingPaths(resultSet.getString(DB_FIELD.PLOTTING_RESULT_PATHS.getOffset()));
            String bestMetlinIon = resultSet.getString(DB_FIELD.BEST_METLIN_ION.getOffset());
            Integer manual_override_id = resultSet.getInt(DB_FIELD.MANUAL_OVERRIDE.getOffset());
            if (resultSet.wasNull()) {
                manual_override_id = null;
            }

            results.add(new StandardIonResult(id, chemical, standardWellId, negativeWellIds, analysisResults,
                    plottingResultFilePaths, bestMetlinIon, manual_override_id));
        }

        return results;
    }

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    protected void bindInsertOrUpdateParameters(PreparedStatement stmt, String chemical, Integer standardWellId,
            List<Integer> negativeWellIds, LinkedHashMap<String, XZ> analysisResults,
            Map<String, String> plottingResultFileMapping, String bestMetlinIon, Integer manualOverrideId)
            throws SQLException, IOException {
        stmt.setString(DB_FIELD.CHEMICAL.getInsertUpdateOffset(), chemical);
        stmt.setInt(DB_FIELD.STANDARD_WELL_ID.getInsertUpdateOffset(), standardWellId);
        stmt.setString(DB_FIELD.NEGATIVE_WELL_IDS.getInsertUpdateOffset(),
                OBJECT_MAPPER.writeValueAsString(negativeWellIds));
        stmt.setString(DB_FIELD.PLOTTING_RESULT_PATHS.getInsertUpdateOffset(),
                serializePlottingPaths(plottingResultFileMapping));
        stmt.setString(DB_FIELD.STANDARD_ION_RESULTS.getInsertUpdateOffset(),
                serializeStandardIonAnalysisResult(analysisResults));
        stmt.setString(DB_FIELD.BEST_METLIN_ION.getInsertUpdateOffset(), bestMetlinIon);

        if (manualOverrideId == null) {
            stmt.setNull(DB_FIELD.MANUAL_OVERRIDE.getInsertUpdateOffset(), Types.INTEGER);
        } else {
            stmt.setInt(DB_FIELD.MANUAL_OVERRIDE.getInsertUpdateOffset(), manualOverrideId);
        }
    }

    @Override
    protected void bindInsertOrUpdateParameters(PreparedStatement stmt, StandardIonResult ionResult)
            throws SQLException, IOException {
        bindInsertOrUpdateParameters(stmt, ionResult.getChemical(), ionResult.getStandardWellId(),
                ionResult.getNegativeWellIds(), ionResult.getAnalysisResults(),
                ionResult.getPlottingResultFilePaths(), ionResult.getBestMetlinIon(),
                ionResult.getManualOverrideId());
    }

    private static final TypeReference<List<Integer>> typeRefForNegativeWells = new TypeReference<List<Integer>>() {
    };
    private static final TypeReference<Map<String, XZ>> typeRefForStandardIonAnalysis = new TypeReference<Map<String, XZ>>() {
    };
    private static final TypeReference<Map<String, String>> typeRefForPlottingPaths = new TypeReference<Map<String, String>>() {
    };

    private static List<Integer> deserializeNegativeWellIds(String serializedNegativeIds) throws IOException {
        return OBJECT_MAPPER.readValue(serializedNegativeIds, typeRefForNegativeWells);
    }

    private static LinkedHashMap<String, XZ> deserializeStandardIonAnalysisResult(String jsonEntry)
            throws IOException {
        // We have to re-sorted the deserialized results so that we meet the contract expected by the caller.
        Map<String, XZ> deserializedResult = OBJECT_MAPPER.readValue(jsonEntry, typeRefForStandardIonAnalysis);
        TreeMap<Double, String> sortedIntensityToIon = new TreeMap<>(Collections.reverseOrder());

        for (Map.Entry<String, XZ> val : deserializedResult.entrySet()) {
            sortedIntensityToIon.put(val.getValue().getIntensity(), val.getKey());
        }

        LinkedHashMap<String, XZ> sortedResult = new LinkedHashMap<>();
        for (Map.Entry<Double, String> val : sortedIntensityToIon.entrySet()) {
            String ion = val.getValue();
            sortedResult.put(ion, deserializedResult.get(ion));
        }

        return sortedResult;
    }

    private static String serializeStandardIonAnalysisResult(LinkedHashMap<String, XZ> analysis)
            throws IOException {
        return OBJECT_MAPPER.writeValueAsString(analysis);
    }

    private static Map<String, String> deserializePlottingPaths(String jsonEntry) throws IOException {
        return OBJECT_MAPPER.readValue(jsonEntry, typeRefForPlottingPaths);
    }

    private static String serializePlottingPaths(Map<String, String> analysis) throws IOException {
        return OBJECT_MAPPER.writeValueAsString(analysis);
    }

    public static StandardIonResult getForChemicalAndStandardWellAndNegativeWells(File lcmsDir, DB db,
            String chemical, StandardWell standardWell, List<StandardWell> negativeWells, String plottingDirectory,
            Map<String, List<Double>> restrictedTimeWindows) throws Exception {
        return StandardIonResult.getInstance().getByChemicalAndStandardWellAndNegativeWells(lcmsDir, db, chemical,
                standardWell, negativeWells, plottingDirectory, restrictedTimeWindows);
    }

    /**
     * This function gets a Standard Ion Result based on the input parameters.
     * @param lcmsDir The directory where the LCMS scans live.
     * @param db The DB from which to extract plate data.
     * @param chemical The name of the chemical that is to be analyzed.
     * @param standardWell The standard well from which the data is extracted.
     * @param negativeWells The negative wells against which benchmark tests are done.
     * @param plottingDirectory The dir where the plotted graphs are stored in.
     * @return A StandardIonResult object that encapsulates the standard ion analysis data.
     * @throws Exception
     */
    public StandardIonResult getByChemicalAndStandardWellAndNegativeWells(File lcmsDir, DB db, String chemical,
            StandardWell standardWell, List<StandardWell> negativeWells, String plottingDirectory,
            Map<String, List<Double>> restrictedTimeWindows) throws Exception {
        List<Integer> negativeWellIds = new ArrayList<>(negativeWells.size());
        for (StandardWell negativeWell : negativeWells) {
            negativeWellIds.add(negativeWell.getId());
        }

        Collections.sort(negativeWellIds);

        StandardIonResult cachedResult = this.getByChemicalAndStandardWellAndNegativeWells(db, chemical,
                standardWell.getId(), negativeWellIds);

        if (cachedResult == null) {
            StandardIonResult computedResult = StandardIonAnalysis
                    .getSnrResultsForStandardWellComparedToValidNegativesAndPlotDiagnostics(lcmsDir, db,
                            standardWell, negativeWells, new HashMap<>(), chemical, plottingDirectory,
                            restrictedTimeWindows);

            if (computedResult == null) {
                return null;
            } else {
                computedResult.setNegativeWellIds(negativeWellIds);
                computedResult.setManualOverrideId(null);
                return insert(db, computedResult);
            }
        } else {
            return cachedResult;
        }
    }

    /**
     * This function takes as input a list of standard ion results and outputs a map of media to list of ion results.
     * @param db
     * @param standardIonResults
     * @return A mapping of media to list of ion results
     * @throws IOException
     * @throws SQLException
     * @throws ClassNotFoundException
     */
    public static Map<String, List<StandardIonResult>> categorizeListOfStandardWellsByMedia(DB db,
            List<StandardIonResult> standardIonResults) throws IOException, SQLException, ClassNotFoundException {

        Map<String, List<StandardIonResult>> categories = new HashMap<>();
        for (StandardIonResult result : standardIonResults) {
            if (StandardWell
                    .isMediaMeOH(StandardWell.getInstance().getById(db, result.getStandardWellId()).getMedia())) {
                List<StandardIonResult> res = categories.get(StandardWell.MEDIA_TYPE.MEOH.name());
                if (res == null) {
                    res = new ArrayList<>();
                }
                res.add(result);
                categories.put(StandardWell.MEDIA_TYPE.MEOH.name(), res);
            } else if (StandardWell.doesMediaContainYeastExtract(
                    StandardWell.getInstance().getById(db, result.getStandardWellId()).getMedia())) {
                List<StandardIonResult> res = categories.get(StandardWell.MEDIA_TYPE.YEAST.name());
                if (res == null) {
                    res = new ArrayList<>();
                }
                res.add(result);
                categories.put(StandardWell.MEDIA_TYPE.YEAST.name(), res);
            } else if (StandardWell
                    .isMediaWater(StandardWell.getInstance().getById(db, result.getStandardWellId()).getMedia())) {
                List<StandardIonResult> res = categories.get(StandardWell.MEDIA_TYPE.WATER.name());
                if (res == null) {
                    res = new ArrayList<>();
                }
                res.add(result);
                categories.put(StandardWell.MEDIA_TYPE.WATER.name(), res);
            }
        }

        return categories;
    }

    // Extra access patterns.
    public static final String GET_BY_CHEMICAL_AND_STANDARD_WELL_AND_NEGATIVE_WELLS = StringUtils
            .join(new String[] { "SELECT", StringUtils.join(StandardIonResult.getInstance().getAllFields(), ','),
                    "from", StandardIonResult.getInstance().getTableName(), "where chemical = ?",
                    "  and standard_well_id = ?", "  and negative_well_ids = ?", }, " ");

    private StandardIonResult getByChemicalAndStandardWellAndNegativeWells(DB db, String chemical,
            Integer standardWellId, List<Integer> negativeWellIds) throws Exception {
        try (PreparedStatement stmt = db.getConn()
                .prepareStatement(GET_BY_CHEMICAL_AND_STANDARD_WELL_AND_NEGATIVE_WELLS)) {
            stmt.setString(1, chemical);
            stmt.setInt(2, standardWellId);
            stmt.setString(3, OBJECT_MAPPER.writeValueAsString(negativeWellIds));

            try (ResultSet resultSet = stmt.executeQuery()) {
                StandardIonResult result = expectOneResult(resultSet,
                        String.format("chemical = %s, standard_well_id = %d, negative_well_ids = %s", chemical,
                                standardWellId, OBJECT_MAPPER.writeValueAsString(negativeWellIds)));
                return result;
            }
        }
    }

    public static List<StandardIonResult> getByChemicalName(DB db, String chemical) throws Exception {
        return StandardIonResult.getInstance().getForChemicalName(db, chemical);
    }

    private List<StandardIonResult> getForChemicalName(DB db, String chemical) throws Exception {
        try (PreparedStatement stmt = db.getConn()
                .prepareStatement(makeGetQueryForSelectField(DB_FIELD.CHEMICAL.getFieldName()))) {
            stmt.setString(1, chemical);
            try (ResultSet resultSet = stmt.executeQuery()) {
                return fromResultSet(resultSet);
            }
        }
    }

    private Integer id;
    private String chemical;
    private Integer standardWellId;
    private List<Integer> negativeWellIds;
    private String bestMetlinIon;
    private LinkedHashMap<String, XZ> analysisResults;
    private Map<String, String> plottingResultFilePaths;
    private Integer manualOverrideId;

    public StandardIonResult() {
    }

    public StandardIonResult(Integer id, String chemical, Integer standardWellId, List<Integer> negativeWellIds,
            LinkedHashMap<String, XZ> analysisResults, Map<String, String> plottingResultFilePaths,
            String bestMelinIon, Integer manualOverrideId) {
        this.id = id;
        this.chemical = chemical;
        this.standardWellId = standardWellId;
        this.negativeWellIds = negativeWellIds;
        this.plottingResultFilePaths = plottingResultFilePaths;
        this.analysisResults = analysisResults;
        this.bestMetlinIon = bestMelinIon;
        this.manualOverrideId = manualOverrideId;
    }

    @Override
    public Integer getId() {
        return id;
    }

    @Override
    public void setId(Integer id) {
        this.id = id;
    }

    public String getChemical() {
        return chemical;
    }

    public void setChemical(String chemical) {
        this.chemical = chemical;
    }

    public String getBestMetlinIon() {
        return bestMetlinIon;
    }

    public void setBestMetlinIon(String bestMetlinIon) {
        this.bestMetlinIon = bestMetlinIon;
    }

    public Integer getStandardWellId() {
        return standardWellId;
    }

    public void setStandardWellId(Integer standardWellId) {
        this.standardWellId = standardWellId;
    }

    public List<Integer> getNegativeWellIds() {
        return negativeWellIds;
    }

    public void setNegativeWellIds(List<Integer> negativeWellIds) {
        this.negativeWellIds = negativeWellIds;
    }

    public LinkedHashMap<String, XZ> getAnalysisResults() {
        return analysisResults;
    }

    public void setAnalysisResults(LinkedHashMap<String, XZ> result) {
        this.analysisResults = result;
    }

    public Map<String, String> getPlottingResultFilePaths() {
        return plottingResultFilePaths;
    }

    public void setPlottingResultFilePaths(Map<String, String> plottingResultFilePaths) {
        this.plottingResultFilePaths = plottingResultFilePaths;
    }

    public Integer getManualOverrideId() {
        return manualOverrideId;
    }

    public void setManualOverrideId(Integer manualOverrideId) {
        this.manualOverrideId = manualOverrideId;
    }
}