/*************************************************************************
* *
* 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.sars;
import chemaxon.formats.MolExporter;
import chemaxon.formats.MolFormatException;
import chemaxon.formats.MolImporter;
import chemaxon.sss.SearchConstants;
import chemaxon.sss.search.MolSearch;
import chemaxon.sss.search.MolSearchOptions;
import chemaxon.sss.search.SearchException;
import chemaxon.struc.Molecule;
import chemaxon.struc.RxnMolecule;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
/**
* A SAR that accepts only single substrates which contain a particular substructure.
*/
public class OneSubstrateSubstructureSar implements Sar {
private static final Logger LOGGER = LogManager.getFormatterLogger(OneSubstrateSubstructureSar.class);
private static final String INCHI_SETTINGS = "inchi:AuxNone";
private static final String PRINT_FAILURE = "FAILED_TO_PRINT_SAR";
/**
* Search loosely by default to avoid weeding out potentially viable matches.
*/
private static final MolSearchOptions LOOSE_SEARCH_OPTIONS = new MolSearchOptions(SearchConstants.SUBSTRUCTURE);
static {
LOOSE_SEARCH_OPTIONS.setStereoModel(SearchConstants.STEREO_IGNORE);
LOOSE_SEARCH_OPTIONS.setVagueBondLevel(SearchConstants.VAGUE_BOND_LEVEL4);
LOOSE_SEARCH_OPTIONS.setTimeoutLimitMilliseconds(1000);
}
private Molecule substructure;
private MolSearch searcher;
/**
* For Json reading.
*/
private OneSubstrateSubstructureSar() {
searcher = new MolSearch();
searcher.setSearchOptions(LOOSE_SEARCH_OPTIONS);
}
public OneSubstrateSubstructureSar(Molecule substructure, MolSearchOptions searchOptions) {
this();
this.substructure = substructure;
searcher.setQuery(substructure);
}
public OneSubstrateSubstructureSar(Molecule substructure) {
this(substructure, LOOSE_SEARCH_OPTIONS);
}
@Override
public boolean test(List<Molecule> substrates) {
// This class of SARs is only valid on single-substrate reactions.
if (substrates.size() != 1) {
return false;
}
// Return true if the searcher finds a match
searcher.setTarget(substrates.get(0));
try {
return searcher.findFirst() != null;
} catch (SearchException e) {
// Log error but don't propagate upward. Have never seen this before.
LOGGER.error("Error on testing substrates with SAR %s", getSubstructureInchi());
return false;
}
}
@JsonProperty("substructure_inchi")
private String getSubstructureInchi() {
try {
return MolExporter.exportToFormat(substructure, INCHI_SETTINGS);
} catch (IOException e) {
LOGGER.error("IOException on exporting sar to inchi, %s", e.getMessage());
return PRINT_FAILURE;
}
}
/**
* For JSON reading
*
* @param inchi Inchi to set as substructure, as read from json.
* @throws MolFormatException
*/
private void setSubstructureInchi(String inchi) throws MolFormatException {
substructure = MolImporter.importMol(inchi);
searcher.setQuery(substructure);
}
public static class Factory implements SarFactory {
private final McsCalculator mcsCalculator;
public Factory(McsCalculator mcsCalculator) {
this.mcsCalculator = mcsCalculator;
}
@Override
public Sar buildSar(List<RxnMolecule> reactions) {
if (!DbAPI.areAllOneSubstrate(reactions)) {
throw new IllegalArgumentException("Reactions are not all one substrate.");
}
List<Molecule> substrates = reactions.stream().map(rxn -> rxn.getReactants()[0]).collect(Collectors.toList());
Molecule sarMcs = mcsCalculator.getMCS(substrates);
return new OneSubstrateSubstructureSar(sarMcs);
}
}
}