// $HeadURL$
// $Id$
//
// Copyright © 2006, 2010, 2011, 2012 by the President and Fellows of Harvard College.
//
// Screensaver is an open-source project developed by the ICCB-L and NSRB labs
// at Harvard Medical School. This software is distributed under the terms of
// the GNU General Public License.
package edu.harvard.med.iccbl.screensaver.io.screens;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.cli.OptionBuilder;
import org.apache.log4j.Logger;
import edu.harvard.med.screensaver.db.DAOTransaction;
import edu.harvard.med.screensaver.db.DAOTransactionRollbackException;
import edu.harvard.med.screensaver.db.GenericEntityDAO;
import edu.harvard.med.screensaver.db.LibrariesDAO;
import edu.harvard.med.screensaver.db.ScreenDAO;
import edu.harvard.med.screensaver.io.CommandLineApplication;
import edu.harvard.med.screensaver.io.FatalParseException;
import edu.harvard.med.screensaver.io.screens.StudyCreator;
import edu.harvard.med.screensaver.io.workbook2.Cell;
import edu.harvard.med.screensaver.io.workbook2.CellVocabularyParser;
import edu.harvard.med.screensaver.io.workbook2.Workbook;
import edu.harvard.med.screensaver.io.workbook2.WorkbookParseError;
import edu.harvard.med.screensaver.io.workbook2.Worksheet;
import edu.harvard.med.screensaver.model.libraries.Reagent;
import edu.harvard.med.screensaver.model.libraries.ReagentVendorIdentifier;
import edu.harvard.med.screensaver.model.libraries.Well;
import edu.harvard.med.screensaver.model.libraries.WellKey;
import edu.harvard.med.screensaver.model.screenresults.AnnotationType;
import edu.harvard.med.screensaver.model.screens.ProjectPhase;
import edu.harvard.med.screensaver.model.screens.Screen;
import edu.harvard.med.screensaver.model.screens.ScreenType;
import edu.harvard.med.screensaver.model.screens.StudyType;
import edu.harvard.med.screensaver.model.users.LabAffiliation;
import edu.harvard.med.screensaver.model.users.LabHead;
import edu.harvard.med.screensaver.model.users.ScreeningRoomUser;
public class MedicinalCompoundsStudyCreator extends CommandLineApplication
{
// static members
private static Logger log = Logger.getLogger(MedicinalCompoundsStudyCreator.class);
private static final String STUDY_NAME = "100002";
private static final String TITLE = "Annotations on Suitability of Compounds: G. Cuny & K. Lee";
private static final String SUMMARY =
"Note for screeners regarding medchem annotation:\n" +
"For historical reasons, the screening libraries contain many compounds that may be unsuitable for further study. These include compounds that are unstable and/or reactive, the very properties that may have contributed to apparent activity upon screening. Although experiments are needed to establish definitively whether compounds are false (or physiologically uninteresting) positives in a given screen, some compounds have obvious, chemically offensive functionalities from a structural point of view. Those compounds can be de-prioritized, so that attention can be directed to compounds that are more likely to be true positives. " +
"The medicinal chemistry group evaluates the structures of library compounds on an ongoing basis, and the following annotations are applied:\n" +
"'A': Compounds that have no obvious liabilities and that are deemed acceptable for initial follow-up work.\n" +
"'B': Compounds that are deemed risky for follow-up and, if resources are limiting, should probably be given lower priority than Category A compounds.\n" +
"'C': Compounds that are not recommended for follow-up, and, if resources are limiting, should be given lower priority than Category A and B compounds.\n" +
"No flag: Compounds that have not yet been evaluated by the medicinal chemistry group.";
private static final String A_NO_SPECIFIC_CONCERNS = "A: No specific concerns";
private static final String B_POTENTIAL_LIABILITY = "B: Potential liability";
private static final String C_SUBSTANTIAL_LIABILITY = "C: Substantial liability";
private static final SortedMap<String,String> VALID_MEDCHEM_COMMENT_VALUES = new TreeMap<String,String>();
static {
VALID_MEDCHEM_COMMENT_VALUES.put("A", A_NO_SPECIFIC_CONCERNS);
VALID_MEDCHEM_COMMENT_VALUES.put("a", A_NO_SPECIFIC_CONCERNS);
VALID_MEDCHEM_COMMENT_VALUES.put("B", B_POTENTIAL_LIABILITY);
VALID_MEDCHEM_COMMENT_VALUES.put("b", B_POTENTIAL_LIABILITY);
VALID_MEDCHEM_COMMENT_VALUES.put("C", C_SUBSTANTIAL_LIABILITY);
VALID_MEDCHEM_COMMENT_VALUES.put("c", C_SUBSTANTIAL_LIABILITY);
VALID_MEDCHEM_COMMENT_VALUES.put("", null);
}
private static final String MECHEM_COMMENT_COLUMN_HEADER = "Medchem comment";
private static final String VENDOR_ID_COLUMN_HEADER = "Vendor_ID";
private static final String VENDOR_COLUMN_HEADER = "Vendor";
private static final String PLATE_COLUMN_HEADER = "Plate";
private static final String WELL_COLUMN_HEADER = "Well";
private static final String LAB_AFFILIATION_NAME = "Brigham and Women's Hospital";
private GenericEntityDAO _dao = null;
private ScreenDAO _screenDao = null;
private LibrariesDAO _librariesDao = null;
public MedicinalCompoundsStudyCreator(String[] args)
{
super(args);
_dao = (GenericEntityDAO) getSpringBean("genericEntityDao");
_screenDao = (ScreenDAO) getSpringBean("screenDao");
_librariesDao = (LibrariesDAO) getSpringBean("librariesDao");
}
public static void main(String[] args)
{
final MedicinalCompoundsStudyCreator app = new MedicinalCompoundsStudyCreator(args);
app.addCommandLineOption(OptionBuilder.isRequired().hasArg().withArgName("workbook file").withLongOpt("input-file").create("f"));
app.processOptions(true, true);
app.run();
}
public void run()
{
_dao.doInTransaction(new DAOTransaction() {
public void runTransaction() {
try {
Screen study = _dao.findEntityByProperty(Screen.class, Screen.facilityId.getPath(), STUDY_NAME);
if (study != null) {
_screenDao.deleteStudy(study);
}
LabAffiliation labAffiliation = _dao.findEntityByProperty(LabAffiliation.class, "affiliationName", LAB_AFFILIATION_NAME);
if (labAffiliation == null) {
throw new RuntimeException("expected lab affiliation " + LAB_AFFILIATION_NAME + " to exist");
}
LabHead labHead = (LabHead) StudyCreator.findOrCreateScreeningRoomUser(_dao, "Gregory", "Cuny", "gcuny@rics.bwh.harvard.edu", true, labAffiliation);
ScreeningRoomUser leadScreener = StudyCreator.findOrCreateScreeningRoomUser(_dao, "Kyungae", "Lee", "kyungae_lee@hms.harvard.edu", false, null);
study = new Screen(findAdministratorUser(),
STUDY_NAME,
leadScreener,
labHead,
ScreenType.SMALL_MOLECULE,
StudyType.IN_SILICO,
ProjectPhase.ANNOTATION,
TITLE);
study.setSummary(SUMMARY);
AnnotationType annotType = study.createAnnotationType("Notes on Suitability",
A_NO_SPECIFIC_CONCERNS + ". " +
B_POTENTIAL_LIABILITY + ". " +
C_SUBSTANTIAL_LIABILITY + ". " +
"No value indicates the compound has not yet been reviewed by this study.",
false);
int n = loadAndCreateReagents(getCommandLineOptionValue("f", File.class), annotType);
_dao.saveOrUpdateEntity(study);
log.info("created " + n + " annotations");
}
catch (Exception e) {
throw new DAOTransactionRollbackException(e);
}
}
});
log.info("study successfully added to database");
}
protected int loadAndCreateReagents(File workbookFile, AnnotationType annotType)
throws FileNotFoundException
{
int n = 0;
Workbook workbook = new Workbook(workbookFile);
for (int iSheet = 0; iSheet < workbook.getWorkbook().getNumberOfSheets(); ++iSheet) {
Worksheet sheet = workbook.getWorksheet(iSheet);
n += loadAndCreateReagentsFromSheet(sheet, annotType);
}
if (workbook.getHasErrors()) {
log.error("Encountered " + workbook.getErrors().size() + " error(s).");
for (WorkbookParseError error : workbook.getErrors()) {
log.error(error.toString());
}
}
return n;
}
private int loadAndCreateReagentsFromSheet(Worksheet sheet, AnnotationType annotType)
{
int n = 0;
try {
short iVendorColumn = (short) findColumnForHeader(sheet, VENDOR_COLUMN_HEADER);
short iVendorIdColumn = (short) findColumnForHeader(sheet, VENDOR_ID_COLUMN_HEADER);
short iMedChemCommentColumn = (short) findColumnForHeader(sheet, MECHEM_COMMENT_COLUMN_HEADER);
short iPlateColumn = (short) findColumnForHeader(sheet, PLATE_COLUMN_HEADER);
short iWellColumn = (short) findColumnForHeader(sheet, WELL_COLUMN_HEADER);
if (iVendorColumn >= 0 && iVendorIdColumn >= 0 && iMedChemCommentColumn >= 0) {
for (int iRow = 1; iRow < sheet.getRows(); ++iRow) { // TODO: use iterator
if( !sheet.getCell(iVendorColumn, iRow, true).isEmpty()
&& !sheet.getCell(iVendorIdColumn, iRow, true).isEmpty())
{
Reagent reagent = findReagent(sheet.getCell(iPlateColumn, iRow, true).getInteger(),
sheet.getCell(iWellColumn, iRow, true).getString(),
sheet.getCell(iVendorIdColumn, iRow, true).getAsString(),
sheet.getCell(iVendorIdColumn, iRow, true).getAsString());
if (reagent != null) {
Cell medChemCommentCell = (Cell) sheet.getCell(iMedChemCommentColumn, iRow, false).clone();
CellVocabularyParser<String> MEDCHEM_COMMENT_CELL_PARSER = new CellVocabularyParser<String>(VALID_MEDCHEM_COMMENT_VALUES, null, "bad annotation value");
String value = MEDCHEM_COMMENT_CELL_PARSER.parse(medChemCommentCell);
if (annotType.createAnnotationValue(reagent, value) != null) {
++n;
}
}
}
}
}
return n;
}
catch (FatalParseException e) {
sheet.addWorkbookError(e.getClass().getName() + ":" + e.getMessage());
return 0;
}
}
private Reagent findReagent(Integer plateNumber,
String wellName,
String vendorName,
String reagentIdentifier)
{
ReagentVendorIdentifier rvi = new ReagentVendorIdentifier(vendorName, reagentIdentifier);
Set<Reagent> set = _librariesDao.findReagents(rvi, false);
Reagent reagent = null;
//Reagent reagent = _dao.findEntityById(Reagent.class, rvi);
if (set.isEmpty()) {
log.warn("no library contains reagent " + rvi);
}
else if (set.size() > 1) {
throw new RuntimeException("more than one reagent found for RVI: " + rvi + ", reagents: " + set);
}
else {
reagent = set.iterator().next();
}
if (reagent == null) {
WellKey wellKey = new WellKey(plateNumber, wellName);
//log.warn("reagent does not exist with ID " + rvi);
Well well = _dao.findEntityById(Well.class, wellKey.toString());
if (well == null) {
log.error("unknown reagent " + rvi + "; looking for reagent in well, but no such well " + wellKey);
}
else {
reagent = well.<Reagent>getLatestReleasedReagent();
if (reagent == null) {
log.error("unknown reagent " + rvi + " and no reagent in well " + wellKey);
}
}
}
return reagent;
}
private static int findColumnForHeader(Worksheet sheet, String headerName)
{
for(Cell cell: sheet.getRow(0))
{
if(cell.getAsString().equals(headerName)) return cell.getColumn();
}
return -1;
}
}