// $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.cherrypickrequests;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.faces.model.SelectItem;
import com.google.common.base.Joiner;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.collect.TreeMultimap;
import org.apache.log4j.Logger;
import org.springframework.transaction.annotation.Transactional;
import edu.harvard.med.screensaver.db.CherryPickRequestDAO;
import edu.harvard.med.screensaver.db.GenericEntityDAO;
import edu.harvard.med.screensaver.model.BusinessRuleViolationException;
import edu.harvard.med.screensaver.model.Volume;
import edu.harvard.med.screensaver.model.VolumeUnit;
import edu.harvard.med.screensaver.model.activities.AdministrativeActivity;
import edu.harvard.med.screensaver.model.cherrypicks.CherryPickAssayProtocolsFollowed;
import edu.harvard.med.screensaver.model.cherrypicks.CherryPickFollowupResultsStatus;
import edu.harvard.med.screensaver.model.cherrypicks.CherryPickRequest;
import edu.harvard.med.screensaver.model.cherrypicks.LabCherryPick;
import edu.harvard.med.screensaver.model.cherrypicks.RNAiCherryPickRequest;
import edu.harvard.med.screensaver.model.cherrypicks.ScreenerCherryPick;
import edu.harvard.med.screensaver.model.cherrypicks.SmallMoleculeCherryPickRequest;
import edu.harvard.med.screensaver.model.libraries.Library;
import edu.harvard.med.screensaver.model.libraries.LibraryScreeningStatus;
import edu.harvard.med.screensaver.model.libraries.PlateType;
import edu.harvard.med.screensaver.model.libraries.Well;
import edu.harvard.med.screensaver.model.libraries.WellKey;
import edu.harvard.med.screensaver.model.libraries.WellName;
import edu.harvard.med.screensaver.model.users.AdministratorUser;
import edu.harvard.med.screensaver.model.users.ScreeningRoomUser;
import edu.harvard.med.screensaver.model.users.ScreensaverUserComparator;
import edu.harvard.med.screensaver.policy.CherryPickRequestAllowancePolicy;
import edu.harvard.med.screensaver.ui.arch.util.JSFUtils;
import edu.harvard.med.screensaver.ui.arch.util.UISelectOneBean;
import edu.harvard.med.screensaver.ui.arch.util.UISelectOneEntityBean;
import edu.harvard.med.screensaver.ui.arch.view.EditResult;
import edu.harvard.med.screensaver.ui.arch.view.EditableEntityViewerBackingBean;
import edu.harvard.med.screensaver.ui.arch.view.aspects.UICommand;
import edu.harvard.med.screensaver.ui.screens.ScreenViewer;
import edu.harvard.med.screensaver.util.DevelopmentException;
/**
* Backing bean for Cherry Pick Request Detail Viewer page.
*
* @author <a mailto="andrew_tolopko@hms.harvard.edu">Andrew Tolopko</a>
*/
public class CherryPickRequestDetailViewer extends EditableEntityViewerBackingBean<CherryPickRequest>
{
private static Logger log = Logger.getLogger(CherryPickRequestDetailViewer.class);
private CherryPickRequestDAO _cherryPickRequestDao;
private CherryPickRequestViewer _cherryPickRequestViewer;
private ScreenViewer _screenViewer;
private CherryPickRequestAllowancePolicy<SmallMoleculeCherryPickRequest> _smallMoleculeCherryPickRequestAllowancePolicy;
private CherryPickRequestAllowancePolicy<RNAiCherryPickRequest> _rnaiCherryPickRequestAllowancePolicy;
private UISelectOneBean<PlateType> _assayPlateType;
private UISelectOneEntityBean<ScreeningRoomUser> _requestedBy;
private UISelectOneEntityBean<AdministratorUser> _volumeApprovedBy;
private UISelectOneBean<VolumeUnit> _transferVolumePerWellRequestedType;
private String _transferVolumePerWellRequestedValue;
private UISelectOneBean<VolumeUnit> _transferVolumePerWellApprovedType;
private String _transferVolumePerWellApprovedValue;
/**
* @motivation for CGLIB2
*/
protected CherryPickRequestDetailViewer(){}
public CherryPickRequestDetailViewer(CherryPickRequestDetailViewer thisProxy,
CherryPickRequestViewer cherryPickRequestViewer,
GenericEntityDAO dao,
CherryPickRequestDAO cherryPickRequestDao,
ScreenViewer screenViewer,
CherryPickRequestAllowancePolicy<SmallMoleculeCherryPickRequest> smallMoleculeCherryPickRequestAllowancePolicy,
CherryPickRequestAllowancePolicy<RNAiCherryPickRequest> rnaiCherryPickRequestAllowancePolicy)
{
super(thisProxy,
CherryPickRequest.class,
EDIT_CHERRY_PICK_REQUEST,
dao);
_cherryPickRequestDao = cherryPickRequestDao;
_cherryPickRequestViewer = cherryPickRequestViewer;
_screenViewer = screenViewer;
_smallMoleculeCherryPickRequestAllowancePolicy = smallMoleculeCherryPickRequestAllowancePolicy;
_rnaiCherryPickRequestAllowancePolicy = rnaiCherryPickRequestAllowancePolicy;
getIsPanelCollapsedMap().put("cherryPickRequestDetails", false);
getIsPanelCollapsedMap().put("cherryPickFollowupAssays", true);
}
@Override
public void initializeEntity(CherryPickRequest cherryPickRequest)
{
getDao().needReadOnly(cherryPickRequest,
CherryPickRequest.requestedBy);
getDao().needReadOnly(cherryPickRequest,
CherryPickRequest.emptyWellsOnAssayPlate);
getDao().needReadOnly(cherryPickRequest,
CherryPickRequest.cherryPickScreenings);
}
@Override
public void initializeViewer(final CherryPickRequest cherryPickRequest)
{
SortedSet<ScreeningRoomUser> requestedByCandidates = Sets.newTreeSet();
requestedByCandidates.addAll(cherryPickRequest.getScreen().getAssociatedScreeningRoomUsers());
// add the current requestedBy user, even if it's no longer a valid candidate, to avoid inadvertently changing it
requestedByCandidates.add(cherryPickRequest.getRequestedBy());
_requestedBy = new UISelectOneEntityBean<ScreeningRoomUser>(requestedByCandidates,
cherryPickRequest.getRequestedBy(),
getDao()) {
@Override
protected String makeLabel(ScreeningRoomUser u) { return u.getFullNameLastFirst(); }
};
SortedSet<AdministratorUser> candidateVolumeApprovers = new TreeSet<AdministratorUser>(ScreensaverUserComparator.getInstance());
candidateVolumeApprovers.addAll(getDao().findAllEntitiesOfType(AdministratorUser.class)); // TODO: filter out all but CherryPickAdmins
_volumeApprovedBy = new UISelectOneEntityBean<AdministratorUser>(candidateVolumeApprovers,
cherryPickRequest.getVolumeApprovedBy(),
true,
getDao()) {
@Override
protected String makeLabel(AdministratorUser a) { return a.getFullNameLastFirst(); }
};
_assayPlateType = new UISelectOneBean<PlateType>(Arrays.asList(PlateType.values()), cherryPickRequest.getAssayPlateType());
_transferVolumePerWellRequestedType = null;
_transferVolumePerWellRequestedValue = null;
_transferVolumePerWellApprovedType = null;
_transferVolumePerWellApprovedValue = null;
}
@Override
protected void initializeNewEntity(CherryPickRequest entity)
{
entity.addEmptyWellsOnAssayPlate(entity.getAssayPlateType().getPlateSize().getEdgeWellNames(2));
}
public UISelectOneBean<PlateType> getAssayPlateType()
{
return _assayPlateType;
}
public UISelectOneEntityBean<ScreeningRoomUser> getRequestedBy()
{
return _requestedBy;
}
public UISelectOneEntityBean<AdministratorUser> getVolumeApprovedBy()
{
return _volumeApprovedBy;
}
public int getActiveCherryPickPlatesCount()
{
return getEntity().getActiveCherryPickAssayPlates().size();
}
public int getCompletedCherryPickPlatesCount()
{
return getEntity().getCompletedCherryPickAssayPlates().size();
}
public UISelectOneBean<VolumeUnit> getTransferVolumePerWellRequestedType()
{
try {
if (_transferVolumePerWellRequestedType == null)
{
Volume v = getEntity().getTransferVolumePerWellRequested();
VolumeUnit unit = (v == null ? VolumeUnit.MICROLITERS : v.getUnits());
_transferVolumePerWellRequestedType =
new UISelectOneBean<VolumeUnit>(VolumeUnit.DISPLAY_VALUES, unit)
{
@Override
protected String makeLabel(VolumeUnit t)
{
return t.getValue();
}
};
}
return _transferVolumePerWellRequestedType;
} catch (Exception e) {
log.error("err: " + e);
return null;
}
}
/**
* This method exists to grab the value portion of the Quantity stored
*/
public String getTransferVolumePerWellRequestedValue()
{
if (_transferVolumePerWellRequestedValue == null)
{
if(getEntity().getTransferVolumePerWellRequested() != null)
_transferVolumePerWellRequestedValue =
getEntity().getTransferVolumePerWellRequested().getDisplayValue().toString();
else
_transferVolumePerWellRequestedValue = null;
}
return _transferVolumePerWellRequestedValue;
}
/**
* This method exists to set the value portion of the Quantity stored
* @see #save()
*/
public void setTransferVolumePerWellRequestedValue( String value )
{
_transferVolumePerWellRequestedValue = value;
}
public UISelectOneBean<VolumeUnit> getTransferVolumePerWellApprovedType()
{
try {
if (_transferVolumePerWellApprovedType == null)
{
Volume v = getEntity().getTransferVolumePerWellApproved();
VolumeUnit unit = (v == null ? VolumeUnit.MICROLITERS : v.getUnits());
_transferVolumePerWellApprovedType =
new UISelectOneBean<VolumeUnit>(VolumeUnit.DISPLAY_VALUES, unit)
{
@Override
protected String makeLabel(VolumeUnit t)
{
return t.getValue();
}
};
}
return _transferVolumePerWellApprovedType;
} catch (Exception e) {
log.error("err: " + e);
return null;
}
}
/**
* This method exists to grab the value portion of the Quantity stored
*/
public String getTransferVolumePerWellApprovedValue()
{
if (_transferVolumePerWellApprovedValue == null)
{
if(getEntity().getTransferVolumePerWellApproved() != null)
_transferVolumePerWellApprovedValue =
getEntity().getTransferVolumePerWellApproved().getDisplayValue().toString();
else
_transferVolumePerWellApprovedValue = null;
}
return _transferVolumePerWellApprovedValue;
}
/**
* This method exists to set the value portion of the Quantity stored
* @see #save()
*/
public void setTransferVolumePerWellApprovedValue( String value )
{
_transferVolumePerWellApprovedValue = value;
}
public List<SelectItem> getCherryPickAssayProtocolsFollowedSelectItems()
{
return JSFUtils.createUISelectItemsWithEmptySelection(Arrays.asList(CherryPickAssayProtocolsFollowed.values()),
"<none>");
}
public List<SelectItem> getCherryPickFollowupResultsStatusSelectItems()
{
return JSFUtils.createUISelectItemsWithEmptySelection(Arrays.asList(CherryPickFollowupResultsStatus.values()),
"<none>");
}
@Override
public boolean isDeleteSupported()
{
return !!!getEntity().isAllocated() &&
!!!(getEntity() instanceof RNAiCherryPickRequest && ((RNAiCherryPickRequest) getEntity()).isScreened());
}
@UICommand
@Override
public String delete()
{
if (!!!isDeleteSupported()) {
throw new BusinessRuleViolationException("cannot delete a cherry pick request that has been allocated or that has been screened");
}
_cherryPickRequestDao.deleteCherryPickRequest(getEntity());
return _screenViewer.viewEntity(getEntity().getScreen());
}
@UICommand
public String deleteAllCherryPicks()
{
_cherryPickRequestDao.deleteAllCherryPicks(getEntity());
return _cherryPickRequestViewer.reload();
}
@Override
protected boolean validateEntity(CherryPickRequest entity)
{
boolean valid = true;
try {
Volume.makeVolume(_transferVolumePerWellRequestedValue, _transferVolumePerWellRequestedType.getSelection());
}
catch (Exception e) {
showFieldInputError("Requested Volume", e.getLocalizedMessage());
valid = false;
}
try {
Volume.makeVolume(_transferVolumePerWellApprovedValue, _transferVolumePerWellApprovedType.getSelection());
}
catch (Exception e) {
showFieldInputError("Approved Volume", e.getLocalizedMessage());
valid = false;
}
return valid;
}
@Override
protected void updateEntityProperties(CherryPickRequest entity)
{
getEntity().setTransferVolumePerWellRequested(Volume.makeVolume(_transferVolumePerWellRequestedValue,
_transferVolumePerWellRequestedType.getSelection()));
getEntity().setTransferVolumePerWellApproved(Volume.makeVolume(_transferVolumePerWellApprovedValue,
_transferVolumePerWellApprovedType.getSelection()));
entity.setAssayPlateType(_assayPlateType.getSelection());
entity.setRequestedBy(_requestedBy.getSelection());
entity.setVolumeApprovedBy(_volumeApprovedBy.getSelection());
}
/**
* Get the set of empty rows requested by the screener.
* @return well names that must be left empty on each cherry pick assay plate
*/
public Set<WellName> getEmptyWellsOnAssayPlate()
{
return getEntity().getEmptyWellsOnAssayPlate();
}
/**
* Set the set of empty wells.
* @param emptyWellsOnAssayPlate wells that screener has requested be
* left empty on each cherry pick assay plate
*/
public void setEmptyWellsOnAssayPlate(Set<WellName> emptyWellsOnAssayPlate)
{
getEntity().clearEmptyWellsOnAssayPlate();
getEntity().addEmptyWellsOnAssayPlate(emptyWellsOnAssayPlate);
}
@UICommand
@Transactional
public void showAdminWarnings()
{
// eager fetch all data needed to calculate warnings
CherryPickRequest cherryPickRequest = getDao().reloadEntity(getEntity(),
true,
CherryPickRequest.screen);
getDao().needReadOnly(cherryPickRequest, CherryPickRequest.labCherryPicks.to(LabCherryPick.screenerCherryPick).to(ScreenerCherryPick.screenedWell).to(Well.deprecationActivity));
getDao().needReadOnly(cherryPickRequest, CherryPickRequest.labCherryPicks.to(LabCherryPick.sourceWell).to(Well.deprecationActivity));
getDao().needReadOnly(cherryPickRequest, CherryPickRequest.screenerCherryPicks.to(ScreenerCherryPick.screenedWell).to(Well.library));
boolean warningIssued = false;
warningIssued |= doWarnOnCherryPickAllowanceExceeded(cherryPickRequest);
warningIssued |= doWarnOnInvalidSourceWell(cherryPickRequest);
warningIssued |= doWarnOnDuplicateScreenerCherryPicks(cherryPickRequest);
warningIssued |= doWarnOnDeprecatedWells(cherryPickRequest);
warningIssued |= doWarnOnLibraryScreeningStatus(cherryPickRequest);
if (!warningIssued) {
showMessage("cherryPicks.allCherryPicksAreValid");
}
}
private <CPR extends CherryPickRequest> boolean doWarnOnCherryPickAllowanceExceeded(CPR cherryPickRequest)
{
CherryPickRequestAllowancePolicy<CPR> policy;
if (cherryPickRequest instanceof SmallMoleculeCherryPickRequest) {
policy = (CherryPickRequestAllowancePolicy<CPR>) _smallMoleculeCherryPickRequestAllowancePolicy;
}
else if (cherryPickRequest instanceof RNAiCherryPickRequest){
policy = (CherryPickRequestAllowancePolicy<CPR>) _rnaiCherryPickRequestAllowancePolicy;
}
else {
throw new DevelopmentException("unsupported cherry pick request type: " + cherryPickRequest.getClass().getName());
}
if (policy.isCherryPickAllowanceExceeded(cherryPickRequest)) {
showMessage("cherryPicks.cherryPickAllowanceExceeded",
policy.getCherryPickAllowanceUsed(cherryPickRequest),
policy.getCherryPickAllowance(cherryPickRequest));
return true;
}
return false;
}
private boolean doWarnOnInvalidSourceWell(CherryPickRequest cherryPickRequest)
{
Set<WellKey> invalidCherryPicks = Sets.newHashSet();
for (ScreenerCherryPick screenerCherryPick : cherryPickRequest.getScreenerCherryPicks()) {
if (screenerCherryPick.getLabCherryPicks().size() == 0) {
invalidCherryPicks.add(screenerCherryPick.getScreenedWell().getWellKey());
}
}
if (!!!invalidCherryPicks.isEmpty()) {
showMessage("cherryPicks.invalidWells", Joiner.on(", ").join(invalidCherryPicks));
return true;
}
return false;
}
private boolean doWarnOnDuplicateScreenerCherryPicks(CherryPickRequest cherryPickRequest)
{
Map<WellKey,Number> duplicateScreenerCherryPickWellKeysMap =
_cherryPickRequestDao.findDuplicateCherryPicksForScreen(cherryPickRequest.getScreen());
Set<WellKey> duplicateScreenerCherryPickWellKeys = duplicateScreenerCherryPickWellKeysMap.keySet();
Set<WellKey> ourScreenerCherryPickWellsKeys = new HashSet<WellKey>();
for (ScreenerCherryPick screenerCherryPick : cherryPickRequest.getScreenerCherryPicks()) {
ourScreenerCherryPickWellsKeys.add(screenerCherryPick.getScreenedWell().getWellKey());
}
duplicateScreenerCherryPickWellKeys.retainAll(ourScreenerCherryPickWellsKeys);
if (duplicateScreenerCherryPickWellKeysMap.size() > 0) {
String duplicateWellsList = Joiner.on(", ").join(duplicateScreenerCherryPickWellKeys);
showMessage("cherryPicks.duplicateCherryPicksInScreen",
cherryPickRequest.getScreen().getFacilityId(),
duplicateWellsList);
return true;
}
return false;
}
private boolean doWarnOnDeprecatedWells(CherryPickRequest cherryPickRequest)
{
Multimap<AdministrativeActivity,WellKey> wellDeprecations = TreeMultimap.create();
for (LabCherryPick labCherryPick : cherryPickRequest.getLabCherryPicks()) {
Well well = labCherryPick.getSourceWell();
if (well.isDeprecated()) {
wellDeprecations.put(well.getDeprecationActivity(), well.getWellKey());
}
well = labCherryPick.getScreenerCherryPick().getScreenedWell();
if (well.isDeprecated()) {
wellDeprecations.put(well.getDeprecationActivity(), well.getWellKey());
}
}
for (AdministrativeActivity deprecationActivity : wellDeprecations.keySet()) {
showMessage("cherryPicks.deprecatedWells",
deprecationActivity.getComments(),
Joiner.on(", ").join(wellDeprecations.values()));
return true;
}
return false;
}
private boolean doWarnOnLibraryScreeningStatus(CherryPickRequest cherryPickRequest)
{
Set<Library> libraries = new LinkedHashSet<Library>();
for (LabCherryPick labCherryPick : cherryPickRequest.getLabCherryPicks()) {
libraries.add(labCherryPick.getSourceWell().getLibrary());
libraries.add(labCherryPick.getScreenerCherryPick().getScreenedWell().getLibrary());
}
for (Library library : libraries) {
if (library.getScreeningStatus() != LibraryScreeningStatus.ALLOWED) {
if (!cherryPickRequest.getScreen().getLibrariesPermitted().contains(library)) {
showMessage("libraries.libraryUsageConflict",
library.getShortName(),
library.getScreeningStatus().getValue());
return true;
}
}
}
return false;
}
@Override
protected String postEditAction(EditResult editResult)
{
switch (editResult) {
case CANCEL_EDIT: return _cherryPickRequestViewer.reload();
case SAVE_EDIT: return _cherryPickRequestViewer.reload();
case CANCEL_NEW: return _screenViewer.reload();
case SAVE_NEW: return _screenViewer.reload();
default: return null;
}
}
}