/*************************************************************************
* *
* 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.biointerpretation.desalting;
import chemaxon.license.LicenseProcessingException;
import chemaxon.reaction.ReactionException;
import com.act.biointerpretation.Utils.ReactionProjector;
import com.act.lcms.db.io.LoadPlateCompositionIntoDB;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class ChemicalDesalter {
private static final Logger LOGGER = LogManager.getFormatterLogger(ChemicalDesalter.class);
public static final String OPTION_OUTPUT_PREFIX = "o";
public static final String OPTION_INCHI_INPUT_LIST = "i";
public static final String HELP_MESSAGE = StringUtils.join(new String[]{
"This class desalts a list of InChIs and outputs the results to a file. To desalt an entire installer DB, ",
"use BiointerpretationDriver."
}, "");
public static final List<Option.Builder> OPTION_BUILDERS = new ArrayList<Option.Builder>() {{
add(Option.builder(OPTION_OUTPUT_PREFIX)
.argName("output prefix")
.desc("A prefix for the output data/pdf files")
.hasArg().required()
.longOpt("output-prefix")
);
add(Option.builder(OPTION_INCHI_INPUT_LIST)
.argName("inchi list file")
.desc("A file containing a list of InChIs to desalt")
.hasArg().required()
.longOpt("input-inchis")
);
add(Option.builder("h")
.argName("help")
.desc("Prints this help message")
.longOpt("help")
);
}};
public static final HelpFormatter HELP_FORMATTER = new HelpFormatter();
static {
HELP_FORMATTER.setWidth(100);
}
private Desalter desalter = new Desalter(new ReactionProjector());
public static void main(String[] args) throws Exception {
Options opts = new Options();
for (Option.Builder b : OPTION_BUILDERS) {
opts.addOption(b.build());
}
CommandLine cl = null;
try {
CommandLineParser parser = new DefaultParser();
cl = parser.parse(opts, args);
} catch (ParseException e) {
System.err.format("Argument parsing failed: %s\n", e.getMessage());
HELP_FORMATTER.printHelp(LoadPlateCompositionIntoDB.class.getCanonicalName(), HELP_MESSAGE, opts, null, true);
System.exit(1);
}
if (cl.hasOption("help")) {
HELP_FORMATTER.printHelp(ReactionDesalter.class.getCanonicalName(), HELP_MESSAGE, opts, null, true);
return;
}
ChemicalDesalter runner = new ChemicalDesalter();
String outAnalysis = cl.getOptionValue(OPTION_OUTPUT_PREFIX);
if (cl.hasOption(OPTION_INCHI_INPUT_LIST)) {
File inputFile = new File(cl.getOptionValue(OPTION_INCHI_INPUT_LIST));
if (!inputFile.exists()) {
System.err.format("Cannot find input file at %s\n", inputFile.getAbsolutePath());
HELP_FORMATTER.printHelp(LoadPlateCompositionIntoDB.class.getCanonicalName(), HELP_MESSAGE, opts, null, true);
System.exit(1);
}
runner.exampleChemicalsList(outAnalysis, inputFile);
}
}
public ChemicalDesalter() {
}
public void exampleChemicalsList(String outputPrefix, File inputFile)
throws IOException, LicenseProcessingException, ReactionException {
desalter.initReactors();
List<String> inchis = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(inputFile))) {
String line;
// Slurp in the list of InChIs from the input file.
while ((line = reader.readLine()) != null) {
inchis.add(line.trim());
}
}
generateAnalysisOfDesaltingSaltyChemicals(inchis, outputPrefix);
}
/**
* This function bins each reaction into modified, unchanged, errors and complex files based on
* processing them through the desalter module.
*
* @param salties A list of reactions
* @param outputPrefix The output prefix for the generated files
*/
private void generateAnalysisOfDesaltingSaltyChemicals(List<String> salties, String outputPrefix) {
try (
BufferedWriter substrateModifiedFileWriter =
new BufferedWriter(new FileWriter(new File(outputPrefix + "_modified.txt")));
BufferedWriter substrateUnchangedFileWriter =
new BufferedWriter(new FileWriter(new File(outputPrefix + "_unchanged.txt")));
BufferedWriter substrateErrorsFileWriter =
new BufferedWriter(new FileWriter(new File(outputPrefix + "_errors.txt")));
BufferedWriter substrateComplexFileWriter =
new BufferedWriter(new FileWriter(new File(outputPrefix + "_complex.txt")))
) {
for (int i = 0; i < salties.size(); i++) {
String salty = salties.get(i);
String saltySmile = null;
try {
saltySmile = desalter.InchiToSmiles(salty);
} catch (Exception err) {
LOGGER.error(String.format("Exception caught while desalting inchi: %s with error message: %s\n", salty,
err.getMessage()));
substrateErrorsFileWriter.write("InchiToSmiles1\t" + salty);
substrateErrorsFileWriter.newLine();
continue;
}
Map<String, Integer> results = null;
try {
results = desalter.desaltInchi(salty);
} catch (Exception err) {
LOGGER.error(String.format("Exception caught while desalting inchi: %s with error message: %s\n", salty,
err.getMessage()));
substrateErrorsFileWriter.append("cleaned\t" + salty);
substrateErrorsFileWriter.newLine();
continue;
}
//Not sure results can be size zero or null, but check anyway
if (results == null) {
LOGGER.error(String.format("Clean results are null for chemical: %s\n", salty));
substrateErrorsFileWriter.append("clean results are null:\t" + salty);
substrateErrorsFileWriter.newLine();
continue;
}
if (results.isEmpty()) {
LOGGER.error(String.format("Clean results are empty for chemical: %s\n", salty));
substrateErrorsFileWriter.append("clean results are empty:\t" + salty);
substrateErrorsFileWriter.newLine();
continue;
}
//If cleaning resulted in a single organic product
if (results.size() == 1) {
String cleaned = results.keySet().iterator().next();
String cleanSmile = null;
try {
cleanSmile = desalter.InchiToSmiles(cleaned);
} catch (Exception err) {
LOGGER.error(String.format("Exception caught while desalting inchi: %s with error message: %s\n", cleaned,
err.getMessage()));
substrateErrorsFileWriter.append("InchiToSmiles2\t" + salty);
substrateErrorsFileWriter.newLine();
}
if (!salty.equals(cleaned)) {
String[] stringElements = {salty, cleaned, saltySmile, cleanSmile};
substrateModifiedFileWriter.append(StringUtils.join(Arrays.asList(stringElements), "\t"));
substrateModifiedFileWriter.newLine();
} else {
String[] stringElements = {salty, saltySmile};
substrateUnchangedFileWriter.append(StringUtils.join(Arrays.asList(stringElements), "\t"));
substrateUnchangedFileWriter.newLine();
}
} else {
//Otherwise there were multiple organic products
substrateComplexFileWriter.append(">>\t" + salty + "\t" + saltySmile);
substrateComplexFileWriter.newLine();
for (String inchi : results.keySet()) {
substrateComplexFileWriter.append("\t" + inchi + "\t" + desalter.InchiToSmiles(inchi));
substrateComplexFileWriter.newLine();
}
}
}
} catch (IOException exception) {
LOGGER.error(String.format("IOException: %s", exception.getMessage()));
}
}
}