// $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.screensaver.ui.libraries; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import edu.harvard.med.screensaver.ScreensaverConstants; import edu.harvard.med.screensaver.db.GenericEntityDAO; import edu.harvard.med.screensaver.db.LibrariesDAO; import edu.harvard.med.screensaver.db.datafetcher.Tuple; import edu.harvard.med.screensaver.io.image.ImageLocatorUtil; import edu.harvard.med.screensaver.io.libraries.WellsSdfDataExporter; import edu.harvard.med.screensaver.io.libraries.smallmolecule.LibraryContentsVersionReference; import edu.harvard.med.screensaver.io.libraries.smallmolecule.StructureImageLocator; import edu.harvard.med.screensaver.io.screenresults.ScreenResultReporter; import edu.harvard.med.screensaver.io.screenresults.ScreenResultReporter.ConfirmationReport; import edu.harvard.med.screensaver.model.libraries.Gene; import edu.harvard.med.screensaver.model.libraries.Library; import edu.harvard.med.screensaver.model.libraries.LibraryType; import edu.harvard.med.screensaver.model.libraries.LibraryWellType; import edu.harvard.med.screensaver.model.libraries.Reagent; import edu.harvard.med.screensaver.model.libraries.SilencingReagent; import edu.harvard.med.screensaver.model.libraries.SmallMoleculeReagent; import edu.harvard.med.screensaver.model.libraries.Well; import edu.harvard.med.screensaver.model.screenresults.AnnotationValue; import edu.harvard.med.screensaver.model.screenresults.ConfirmedPositiveValue; import edu.harvard.med.screensaver.model.screens.Publication; import edu.harvard.med.screensaver.model.screens.Screen; import edu.harvard.med.screensaver.model.screens.Study; import edu.harvard.med.screensaver.policy.EntityViewPolicy; import edu.harvard.med.screensaver.ui.arch.datatable.column.TableColumn; import edu.harvard.med.screensaver.ui.arch.util.JSFUtils; import edu.harvard.med.screensaver.ui.arch.util.SimpleCell; import edu.harvard.med.screensaver.ui.arch.view.SearchResultContextEntityViewerBackingBean; import edu.harvard.med.screensaver.ui.arch.view.aspects.UICommand; import edu.harvard.med.screensaver.ui.screens.AnnotationHeaderColumn; import edu.harvard.med.screensaver.ui.screens.StudyViewer; import edu.harvard.med.screensaver.util.NullSafeUtils; import edu.harvard.med.screensaver.util.StringUtils; public class WellViewer extends SearchResultContextEntityViewerBackingBean<Well,Tuple<String>> { private static final Logger log = Logger.getLogger(WellViewer.class); public static final String GENBANK_ACCESSION_NUMBER_LOOKUP_URL_PREFIX = "http://www.ncbi.nlm.nih.gov/entrez/viewer.fcgi?val="; public static final String ENTREZGENE_ID_LOOKUP_URL_PREFIX = "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?db=gene&cmd=Retrieve&dopt=full_report&list_uids="; public static final String PUBCHEM_CID_LOOKUP_URL_PREFIX = "http://pubchem.ncbi.nlm.nih.gov/summary/summary.cgi?cid="; public static final String CHEMBANK_ID_LOOKUP_URL_PREFIX = "http://chembank.broad.harvard.edu/chemistry/viewMolecule.htm?cbid="; public static final String CHEMBL_ID_LOOKUP_URL_PREFIX = "http://www.ebi.ac.uk/chembldb/compound/inspect/CHEMBL"; private LibraryViewer _libraryViewer; private LibrariesDAO _librariesDao; private EntityViewPolicy _entityViewPolicy; private StructureImageLocator _structureImageLocator; private StudyViewer _studyViewer; private WellsSdfDataExporter _wellsSdfDataExporter; private LibraryContentsVersionReference _libraryContentsVersionRef; // Note: the annotation search results is used by LINCS, it will display all annotations from all studies in one table. TODO: rework this (by nesting two levels?) so that annotions are visually grouped by study private AnnotationSearchResults _annotationSearchResults; // Note: the annotation name value table is used to display a generic table listing annotations from multiple studies, grouped in the well viewer private List<SimpleCell> _annotationNameValueTable; private DataModel _otherWellsDataModel; private DataModel _duplexWellsDataModel; private Reagent _versionedRestrictedReagent; private boolean _isAnnotationSearchResultsInitialized = false; private ConfirmationReportTableModel _confirmationReportTableModel; private ScreenResultReporter _screenResultReporter; /** * @motivation for CGLIB2 */ protected WellViewer() {} public WellViewer(WellViewer thisProxy, WellSearchResults wellSearchResults, GenericEntityDAO dao, LibrariesDAO librariesDAO, EntityViewPolicy entityViewPolicy, LibraryViewer libraryViewer, StructureImageLocator structureImageLocator, StudyViewer studyViewer, WellsSdfDataExporter wellsSdfDataExporter, LibraryContentsVersionReference libraryContentsVersionRef, AnnotationSearchResults annotationSearchResults, ScreenResultReporter screenResultReporter) { super(thisProxy, Well.class, ScreensaverConstants.BROWSE_WELLS, ScreensaverConstants.VIEW_WELL, dao, wellSearchResults); _librariesDao = librariesDAO; _entityViewPolicy = entityViewPolicy; _libraryViewer = libraryViewer; _structureImageLocator = structureImageLocator; _studyViewer = studyViewer; _wellsSdfDataExporter = wellsSdfDataExporter; _libraryContentsVersionRef = libraryContentsVersionRef == null ? new LibraryContentsVersionReference() : libraryContentsVersionRef; _annotationSearchResults = annotationSearchResults; _screenResultReporter = screenResultReporter; getIsPanelCollapsedMap().put("otherWells", Boolean.TRUE); getIsPanelCollapsedMap().put("duplexWells", Boolean.TRUE); getIsPanelCollapsedMap().put("annotations", Boolean.TRUE); getIsPanelCollapsedMap().put("studyHeaders", Boolean.TRUE); } /** * Compounds in certain libraries are to be treated specially - we need to display a special message to give some * idea to the user why there are no structures for these compounds. Returns a non-null, non-empty message * explaining why there is no structure, when such a message is applicable to the library that contains this well. */ public String getSpecialMessage() { if (getEntity() == null) { return null; } if (! getEntity().getLibraryWellType().equals(LibraryWellType.EXPERIMENTAL)) { return null; } Library library = getEntity().getLibrary(); // HACK: special case messages if (library.getLibraryType().equals(LibraryType.NATURAL_PRODUCTS)) { return "Structure information is unavailable for compounds in natural products libraries."; } return null; } public String viewLibrary() { return _libraryViewer.viewEntity(getEntity().getLibrary()); } @UICommand public String downloadSDFile() { InputStream inputStream = null; try { inputStream = _wellsSdfDataExporter.export(ImmutableSet.of(getEntity().getWellKey().toString()).iterator()); JSFUtils.handleUserDownloadRequest(getFacesContext(), inputStream, _wellsSdfDataExporter.getFileName(), _wellsSdfDataExporter.getMimeType()); } catch (IOException e) { reportApplicationError(e.toString()); } finally { if (inputStream != null){ IOUtils.closeQuietly(inputStream); } } return REDISPLAY_PAGE_ACTION_RESULT; } public DataModel getOtherWellsDataModel() { if (_otherWellsDataModel == null) { if (_versionedRestrictedReagent != null) { Set<Reagent> reagents = _librariesDao.findReagents(_versionedRestrictedReagent.getVendorId(), true /* * Reagent.well.to(Well. * library) */); reagents.remove(_versionedRestrictedReagent); List<Reagent> list = Lists.newArrayList(reagents); Collections.sort(list, new Comparator<Reagent>() { @Override public int compare(Reagent o1, Reagent o2) { if(o1==o2) return 0; return o1.getWell().getWellId().compareTo(o2.getWell().getWellId()); }} ); _otherWellsDataModel = new ListDataModel(list); } else { _otherWellsDataModel = new ListDataModel(Lists.newArrayList()); } } return _otherWellsDataModel; } // Annotation search results - used by LINCS - all annotations (for all studies) in one table public AnnotationSearchResults getAnnotationSearchResults() { // lazy initialization of _annotationSearchResults, for performance (avoid expense of determining columns, if not being viewed) if (!_isAnnotationSearchResultsInitialized && !getIsPanelCollapsedMap().get("annotations")) { _annotationSearchResults.searchForAnnotations(getEntity()); _isAnnotationSearchResultsInitialized = true; } return _annotationSearchResults; } // Annotation name value table: (generic build) - annotations are grouped by study, shown as separate "name/value" tables (name/value table are simple property name/property value) public DataModel getAnnotationNameValueTable() { return new ListDataModel(_annotationNameValueTable); } public boolean isAnnotationListAvailable() { return !_annotationNameValueTable.isEmpty(); } private void initializeAnnotationValuesTable(Well well) { List<AnnotationValue> annotationValues = new ArrayList<AnnotationValue>(); if (_versionedRestrictedReagent != null) { Reagent reagent = (Reagent) getDao().reloadEntity(_versionedRestrictedReagent, true, Reagent.annotationValues).restrict(); annotationValues.addAll(reagent.getAnnotationValues().values()); for (Iterator iterator = annotationValues.iterator(); iterator.hasNext();) { AnnotationValue annotationValue = (AnnotationValue) iterator.next(); if (annotationValue.isRestricted()) { iterator.remove(); } } } Collections.sort(annotationValues, new Comparator<AnnotationValue>() { public int compare(AnnotationValue o1, AnnotationValue o2) { return o1.getAnnotationType().getStudy().getFacilityId() .compareTo(o2.getAnnotationType().getStudy().getFacilityId()); } }); _annotationNameValueTable = new ArrayList<SimpleCell>(annotationValues.size()); // Create the top level list of AV's for (AnnotationValue value : annotationValues) { // for each AV, create Meta "grouping" information // - this is all of the study information List<SimpleCell> metaInformation = new ArrayList<SimpleCell>(); for (AnnotationHeaderColumn headerColumn : EnumSet.allOf(AnnotationHeaderColumn.class)) { String headerValue = headerColumn.getValue(value.getReagent(), value.getAnnotationType()); if (!StringUtils.isEmpty(headerValue)) { final Study study = value.getAnnotationType().getStudy(); if (headerColumn == AnnotationHeaderColumn.STUDY_NAME) { // Fix for bug [#3220] Study link in Well Viewer Annotation table is broken // Note: cannot use an anonymous inner class with er-ri-1.0.jar // FYI: http://stackoverflow.com/questions/2998745/how-to-invoke-jsf-action-on-an-anonymous-class-el-cannot-access-it // TODO: why is this working for TableColumn? // SimpleCell cell = new SimpleCell(headerColumn.getColName(), headerValue, headerColumn.getDescription()) { // @Override // public boolean isCommandLink() { // return true; // } // // @Override // public Object cellAction() { // return _studyViewer.viewEntity(study); // } // }; StudyCell cell = new StudyCell(headerColumn.getColName(), headerValue, headerColumn.getDescription(), study); metaInformation.add(cell); } else { metaInformation.add(new SimpleCell(headerColumn.getColName(), headerValue, headerColumn.getDescription())); } } } String textValue = value.getAnnotationType().isNumeric() ? "" + value.getNumericValue() : value.getValue(); _annotationNameValueTable.add( new SimpleCell( value.getAnnotationType().getName(), textValue, value.getAnnotationType().getDescription(), metaInformation) .setGroupId("" + value.getAnnotationType().getStudy().getFacilityId())); } } // Fix for bug [#3220] Study link in Well Viewer Annotation table is broken // Note: cannot use an anonymous inner class with er-ri-1.0.jar // FYI: http://stackoverflow.com/questions/2998745/how-to-invoke-jsf-action-on-an-anonymous-class-el-cannot-access-it public class StudyCell extends SimpleCell { Study study; public StudyCell(String title, Object value, String description, Study study) { super(title, value, description); this.study = study; } @Override public Object cellAction() { return _studyViewer.viewEntity(study); } @Override public boolean isCommandLink() { return true; } } @Override protected void initializeEntity(Well well) { getDao().needReadOnly(well, Well.library); getDao().needReadOnly(well, Well.deprecationActivity); Reagent versionedReagent = _libraryContentsVersionRef.value() == null ? well.getLatestReleasedReagent() : well.getReagents().get(_libraryContentsVersionRef.value()); if (versionedReagent != null) { if (well.getLibrary().getReagentType().equals(SilencingReagent.class)) { getDao().needReadOnly((SilencingReagent) versionedReagent, SilencingReagent.vendorGenes.to(Gene.genbankAccessionNumbers)); getDao().needReadOnly((SilencingReagent) versionedReagent, SilencingReagent.vendorGenes.to(Gene.entrezgeneSymbols)); getDao().needReadOnly((SilencingReagent) versionedReagent, SilencingReagent.facilityGenes.to(Gene.genbankAccessionNumbers)); getDao().needReadOnly((SilencingReagent) versionedReagent, SilencingReagent.facilityGenes.to(Gene.entrezgeneSymbols)); getDao().needReadOnly((SilencingReagent) versionedReagent, SilencingReagent.duplexWells.to(Well.library)); } if (well.getLibrary().getReagentType().equals(SmallMoleculeReagent.class)) { getDao().needReadOnly((SmallMoleculeReagent) versionedReagent, SmallMoleculeReagent.compoundNames); getDao().needReadOnly((SmallMoleculeReagent) versionedReagent, SmallMoleculeReagent.pubchemCids); getDao().needReadOnly((SmallMoleculeReagent) versionedReagent, SmallMoleculeReagent.chembankIds); getDao().needReadOnly((SmallMoleculeReagent) versionedReagent, SmallMoleculeReagent.chemblIds); getDao().needReadOnly((SmallMoleculeReagent) versionedReagent, SmallMoleculeReagent.molfileList); } getDao().needReadOnly(versionedReagent, Reagent.libraryContentsVersion); getDao().needReadOnly(versionedReagent, Reagent.publications); _versionedRestrictedReagent = (Reagent) versionedReagent.restrict(); } else { _versionedRestrictedReagent = null; } } @Override protected void initializeViewer(Well well) { initializeAnnotationValuesTable(well); _otherWellsDataModel = null; _duplexWellsDataModel = null; _isAnnotationSearchResultsInitialized = false; _confirmationReportTableModel = null; } public Reagent getRestrictedReagent() { return _versionedRestrictedReagent; } @Override protected Serializable convertEntityId(String entityIdAsString) { return entityIdAsString; } public String getGenbankAccessionNumberUrlPrefix() { return GENBANK_ACCESSION_NUMBER_LOOKUP_URL_PREFIX; } public String getEntrezgeneIdUrlPrefix() { return ENTREZGENE_ID_LOOKUP_URL_PREFIX; } public String getPubchemCidUrlPrefix() { return PUBCHEM_CID_LOOKUP_URL_PREFIX; } public String getChembankIdUrlPrefix() { return CHEMBANK_ID_LOOKUP_URL_PREFIX; } public String getChemblIdUrlPrefix() { return CHEMBL_ID_LOOKUP_URL_PREFIX; } public String getCompoundImageUrl() { URL url = _structureImageLocator.getImageUrl((SmallMoleculeReagent) getRestrictedReagent()); // TODO: consider using: return NullSafeUtils.toString(ImageLocatorUtil.toExtantContentUrl(url), ""); return url == null ? null : url.toString(); } public String getCompoundMolecularFormula() { return ((SmallMoleculeReagent) getRestrictedReagent()).getMolecularFormula() == null ? "" : ((SmallMoleculeReagent) getRestrictedReagent()).getMolecularFormula().toHtml(); } public DataModel getPublicationsDataModel() { if (_versionedRestrictedReagent == null) return null; ArrayList<Publication> publications = new ArrayList<Publication>(getRestrictedReagent().getPublications()); Collections.sort(publications, new Comparator<Publication>() { public int compare(Publication p1, Publication p2) { return p1.getAuthors().compareTo(p2.getAuthors()); } }); return new ListDataModel(publications); } //// confirmation report table model public class ConfirmationReportTableModel { public DataModel _columnDataModel = new ListDataModel(); //List<SilencingReagent> public DataModel _dataModel = new ListDataModel(); // List<Map<SilencingReagent,SimpleCell>> private ConfirmationReport _report; public ConfirmationReportTableModel() {}; public ConfirmationReportTableModel(ConfirmationReport report) { _report = report; _dataModel = new ListDataModel(_report.getScreens()); List<SilencingReagent> duplexReagents = Lists.newArrayList(); for (SilencingReagent dr : _report.getDuplexReagents()) { duplexReagents.add((SilencingReagent) getDao().reloadEntity(dr, true, SilencingReagent.well.castToSubtype(SilencingReagent.class)).restrict()); } _columnDataModel = new ListDataModel(duplexReagents); } public DataModel getDataModel() { return _dataModel; } public DataModel getColumnDataModel() { return _columnDataModel; } public SimpleCell getCell() { Screen s = getScreen(); SilencingReagent r = _report.getDuplexReagents().get(_columnDataModel.getRowIndex()); ConfirmedPositiveValue value = _report.getResults().get(s).get(r); String style = getStyleClass(value); return new SimpleCell(r.getSequence(), value, "Value for " + r.getSequence()) .withStyleClass(style) .withLinkValue(r.getWell()); } private String getStyleClass(ConfirmedPositiveValue value) { String style = "confirmationReportInconclusiveOrNoData"; if (value != null) { switch (value) { case CONFIRMED_POSITIVE: style = "confirmationReportConfirmedPositive"; break; case FALSE_POSITIVE: style = "confirmationReportFalsePositive"; break; } } return style; } public Screen getScreen() { return _report.getScreens().get(_dataModel.getRowIndex()); } } public ConfirmationReportTableModel getConfirmationReport() { Reagent reagent = getRestrictedReagent(); if (_confirmationReportTableModel == null) { if (reagent == null || !(reagent instanceof SilencingReagent) || !reagent.getWell().getLibrary().isPool()) { _confirmationReportTableModel = new ConfirmationReportTableModel(); } if (_confirmationReportTableModel == null) { //TODO: will this report become too stale if a user session lasts too long? ConfirmationReport report = _screenResultReporter.getDuplexReconfirmationReport((SilencingReagent) reagent); if (report.getDuplexReagents().isEmpty()) { // this will occur if there are no confirmation results. the UI should still show the duplex wells report.setDuplexReagents(Lists.newArrayList(Iterables.transform(((SilencingReagent) reagent).getDuplexSilencingReagents(), SilencingReagent.<SilencingReagent>ToRestricted()))); } _confirmationReportTableModel = new ConfirmationReportTableModel(report); } } return _confirmationReportTableModel; } }