// $HeadURL:
// http://forge.abcd.harvard.edu/svn/screensaver/branches/iccbl/library-copy-mgmt/src/edu/harvard/med/screensaver/ui/searchresults/PlateSearchResults.java
// $
// $Id$
//
// Copyright © 2010 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.math.BigDecimal;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import edu.harvard.med.screensaver.db.GenericEntityDAO;
import edu.harvard.med.screensaver.db.LibrariesDAO;
import edu.harvard.med.screensaver.db.Criterion.Operator;
import edu.harvard.med.screensaver.db.datafetcher.DataFetcherUtil;
import edu.harvard.med.screensaver.db.datafetcher.EntityDataFetcher;
import edu.harvard.med.screensaver.db.hqlbuilder.HqlBuilder;
import edu.harvard.med.screensaver.model.MolarConcentration;
import edu.harvard.med.screensaver.model.Volume;
import edu.harvard.med.screensaver.model.activities.Activity;
import edu.harvard.med.screensaver.model.activities.AdministrativeActivity;
import edu.harvard.med.screensaver.model.activities.AdministrativeActivityType;
import edu.harvard.med.screensaver.model.libraries.Copy;
import edu.harvard.med.screensaver.model.libraries.CopyUsageType;
import edu.harvard.med.screensaver.model.libraries.Library;
import edu.harvard.med.screensaver.model.libraries.Plate;
import edu.harvard.med.screensaver.model.libraries.PlateLocation;
import edu.harvard.med.screensaver.model.libraries.PlateStatus;
import edu.harvard.med.screensaver.model.libraries.PlateType;
import edu.harvard.med.screensaver.model.libraries.Quadrant;
import edu.harvard.med.screensaver.model.libraries.ScreeningStatistics;
import edu.harvard.med.screensaver.model.libraries.VolumeStatistics;
import edu.harvard.med.screensaver.model.meta.PropertyPath;
import edu.harvard.med.screensaver.model.meta.RelationshipPath;
import edu.harvard.med.screensaver.model.screens.ScreenType;
import edu.harvard.med.screensaver.model.users.ScreensaverUser;
import edu.harvard.med.screensaver.model.users.ScreensaverUserRole;
import edu.harvard.med.screensaver.ui.activities.ActivitySearchResults;
import edu.harvard.med.screensaver.ui.arch.datatable.column.DateColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.DateTimeColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.IntegerColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.TableColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.VolumeColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.entity.DateEntityColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.entity.EnumEntityColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.entity.FixedDecimalEntityColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.entity.HasFetchPaths;
import edu.harvard.med.screensaver.ui.arch.datatable.column.entity.IntegerEntityColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.entity.MolarConcentrationEntityColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.entity.TextEntityColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.entity.UserNameColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.column.entity.VolumeEntityColumn;
import edu.harvard.med.screensaver.ui.arch.datatable.model.InMemoryEntityDataModel;
import edu.harvard.med.screensaver.ui.arch.searchresults.EntityBasedEntitySearchResults;
import edu.harvard.med.screensaver.ui.arch.searchresults.EntityUpdateSearchResults;
import edu.harvard.med.screensaver.ui.arch.view.aspects.UICommand;
/**
* A SearchResult that provides detailed information for library copy {@link Plate}s. Each row represents one physical
* plate.
*
* @author atolopko
*/
public class LibraryCopyPlateSearchResults extends EntityBasedEntitySearchResults<Plate,Integer>
{
private static final Logger log = Logger.getLogger(LibraryCopyPlateFinder.class);
private static final Predicate<Plate> PlateScreeningStatisticsNotInitialized = new Predicate<Plate>() {
public boolean apply(Plate plate)
{
return plate.getScreeningStatistics() == null;
}
};
private static final Predicate<Plate> PlateVolumeStatisticsNotInitialized = new Predicate<Plate>() {
public boolean apply(Plate plate)
{
return plate.getVolumeStatistics() == null;
}
};
private static final String COLUMN_GROUP_CONCENTRATION = "Concentration";
private GenericEntityDAO _dao;
private LibrariesDAO _librariesDao;
private LibraryViewer _libraryViewer;
private LibraryCopyViewer _libraryCopyViewer;
private ActivitySearchResults _activitiesBrowser;
private LibraryCopyPlateBatchEditor _libraryCopyPlateBatchEditor;
private String _reviewMessage;
private EntityUpdateSearchResults<Plate,Integer> _entityUpdateHistoryBrowser;
protected LibraryCopyPlateSearchResults()
{}
public LibraryCopyPlateSearchResults(GenericEntityDAO dao,
LibrariesDAO librariesDao,
LibraryViewer libraryViewer,
LibraryCopyViewer libraryCopyViewer,
ActivitySearchResults activitiesBrowser,
LibraryCopyPlateBatchEditor libraryCopyPlateBatchEditor)
{
_dao = dao;
_librariesDao = librariesDao;
_libraryViewer = libraryViewer;
_libraryCopyViewer = libraryCopyViewer;
_activitiesBrowser = activitiesBrowser;
_libraryCopyPlateBatchEditor = libraryCopyPlateBatchEditor;
setEditingRole(ScreensaverUserRole.LIBRARY_COPIES_ADMIN);
}
private void addLibraryTypeRestriction(HqlBuilder hql, String rootAlias)
{
hql.from(rootAlias, Plate.copy, "c").from("c", Copy.library, "l").
whereIn("l", "libraryType", LibrarySearchResults.LIBRARY_TYPES_TO_DISPLAY);
}
@Override
public void searchAll()
{
_mode = Mode.ALL;
setTitle("Library Copy Plates Browser");
EntityDataFetcher<Plate,Integer> plateDataFetcher = new EntityDataFetcher<Plate,Integer>(Plate.class, _dao) {
@Override
public void addDomainRestrictions(HqlBuilder hql)
{
addLibraryTypeRestriction(hql, getRootAlias());
}
};
initialize(plateDataFetcher);
setTableFilterMode(true);
}
public void searchPlatesForCopy(final Copy copy)
{
_mode = Mode.COPY;
setTitle("Library Copy Plates for copy " + copy.getLibrary().getLibraryName() + ", copy " + copy.getName());
EntityDataFetcher<Plate,Integer> plateDataFetcher =
new EntityDataFetcher<Plate,Integer>(Plate.class, _dao) {
@Override
public void addDomainRestrictions(HqlBuilder hql)
{
DataFetcherUtil.addDomainRestrictions(hql, Plate.copy, copy, getRootAlias());
addLibraryTypeRestriction(hql, getRootAlias());
}
};
initialize(plateDataFetcher);
}
public void searchPlatesForLibrary(final Library library)
{
_mode = Mode.LIBRARY;
setTitle("Library Copy Plates for library " + library.getLibraryName());
EntityDataFetcher<Plate,Integer> plateDataFetcher =
new EntityDataFetcher<Plate,Integer>(Plate.class, _dao) {
@Override
public void addDomainRestrictions(HqlBuilder hql)
{
DataFetcherUtil.addDomainRestrictions(hql, Plate.copy.to(Copy.library), library, getRootAlias());
addLibraryTypeRestriction(hql, getRootAlias());
}
};
initialize(plateDataFetcher);
}
public void searchForPlates(String searchDescription, final Set<Integer> plateIds)
{
_mode = Mode.SET;
setTitle("Library Copy Plates search for " + searchDescription);
EntityDataFetcher<Plate,Integer> plateDataFetcher =
new EntityDataFetcher<Plate,Integer>(Plate.class, _dao) {
@Override
public void addDomainRestrictions(HqlBuilder hql)
{
DataFetcherUtil.addDomainRestrictions(hql, getRootAlias(), plateIds);
addLibraryTypeRestriction(hql, getRootAlias());
}
};
initialize(plateDataFetcher);
}
private Set<TableColumn<Plate,?>> screeningStatisticColumns;
private Set<TableColumn<Plate,?>> volumeStatisticColumns;
private enum Mode {
ALL,
SET,
COPY,
LIBRARY,
};
private Mode _mode;
private void initialize(EntityDataFetcher<Plate,Integer> plateDataFetcher)
{
initialize(new InMemoryEntityDataModel<Plate,Integer,Plate>(plateDataFetcher) {
private Predicate<TableColumn<Plate,?>> isScreeningStatisticsColumnWithCriteria = new Predicate<TableColumn<Plate,?>>() {
@Override
public boolean apply(TableColumn<Plate,?> column)
{
return screeningStatisticColumns.contains(column) && column.hasCriteria();
}
};
private Function<List<Plate>,Void> calculatePlateScreeningStatistics = new Function<List<Plate>,Void>() {
@Override
public Void apply(List<Plate> plates)
{
_librariesDao.calculatePlateScreeningStatistics(plates);
return null;
}
};
private Predicate<TableColumn<Plate,?>> isVolumeStatisticsColumnWithCriteria = new Predicate<TableColumn<Plate,?>>() {
@Override
public boolean apply(TableColumn<Plate,?> column)
{
return volumeStatisticColumns.contains(column) && column.hasCriteria();
}
};
private Function<List<Copy>,Void> calculateCopyScreeningStatistics = new Function<List<Copy>,Void>() {
@Override
public Void apply(List<Copy> copies)
{
_librariesDao.calculateCopyScreeningStatistics(copies);
return null;
}
};
@Override
public void fetch(List<? extends TableColumn<Plate,?>> columns)
{
// add fetch properties that are needed for review message generation
if (columns.size() > 0) {
((HasFetchPaths<Plate>) columns.get(0)).addRelationshipPath(Plate.location);
((HasFetchPaths<Plate>) columns.get(0)).addRelationshipPath(Plate.copy.to(Copy.library));
}
((HasFetchPaths<Plate>) columns.get(0)).addRelationshipPath(RelationshipPath.from(Plate.class).to("updateActivities"));
super.fetch(columns);
}
@Override
public void filter(List<? extends TableColumn<Plate,?>> columns)
{
if (_mode == Mode.ALL && !hasCriteriaDefined(getColumnManager().getAllColumns())) {
setWrappedData(Collections.EMPTY_LIST); // for memory performance, initialize with an empty list.
}
else {
boolean calcScreeningStatisticsBeforeFiltering = Iterables.any(columns, isScreeningStatisticsColumnWithCriteria);
boolean calcVolumeStatisticsBeforeFiltering = Iterables.any(columns, isVolumeStatisticsColumnWithCriteria);
if (calcScreeningStatisticsBeforeFiltering) {
calculateScreeningStatistics(_unfilteredData);
}
if (calcVolumeStatisticsBeforeFiltering) {
calculateVolumeStatistics(_unfilteredData);
}
super.filter(columns);
if (!calcScreeningStatisticsBeforeFiltering) {
calculateScreeningStatistics(_unfilteredData);
}
if (!calcVolumeStatisticsBeforeFiltering) {
calculateVolumeStatistics(_unfilteredData);
}
}
updateReviewMessage();
}
private boolean hasCriteriaDefined(List<? extends TableColumn<?,?>> columns)
{
for (TableColumn<?,?> column : columns) {
if (column.hasCriteria()) return true;
}
return false;
}
private void calculateScreeningStatistics(Iterable<Plate> plates)
{
List<Plate> platesWithoutStatistics = Lists.newArrayList(Iterables.filter(plates, PlateScreeningStatisticsNotInitialized));
for (List<Plate> partition : Lists.partition(platesWithoutStatistics, 1024)) {
_librariesDao.calculatePlateScreeningStatistics(partition);
}
}
private void calculateVolumeStatistics(Iterable<Plate> plates)
{
List<Plate> platesWithoutStatistics = Lists.newArrayList(Iterables.filter(plates, PlateVolumeStatisticsNotInitialized));
for (List<Plate> partition : Lists.partition(platesWithoutStatistics, 1024)) {
_librariesDao.calculatePlateVolumeStatistics(partition);
}
}
});
_libraryCopyPlateBatchEditor.initialize();
}
@Override
protected List<TableColumn<Plate,?>> buildColumns()
{
List<TableColumn<Plate,?>> columns = Lists.newArrayList();
screeningStatisticColumns = Sets.newHashSet();
volumeStatisticColumns = Sets.newHashSet();
columns.add(new IntegerEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("plateNumber"),
"Plate",
"Plate number",
TableColumn.UNGROUPED) {
@Override
public Integer getCellValue(Plate plate)
{
return plate.getPlateNumber();
}
});
columns.add(new TextEntityColumn<Plate>(Plate.copy.toProperty("name"),
"Copy",
"The library copy containing the plate",
TableColumn.UNGROUPED) {
@Override
public String getCellValue(Plate plate)
{
return plate.getCopy().getName();
}
@Override
public boolean isCommandLink()
{
return true;
}
@Override
public Object cellAction(Plate plate)
{
return _libraryCopyViewer.viewEntity(plate.getCopy());
}
});
Iterables.getLast(columns).setVisible(_mode != Mode.COPY);
columns.add(new EnumEntityColumn<Plate,CopyUsageType>(Plate.copy.toProperty("usageType"),
"Copy Usage Type",
"The usage type of the copy containing the plate",
TableColumn.UNGROUPED,
CopyUsageType.values()) {
@Override
public CopyUsageType getCellValue(Plate plate)
{
return plate.getCopy().getUsageType();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new TextEntityColumn<Plate>(Plate.copy.to(Copy.library).toProperty("libraryName"),
"Library",
"The library containing the plate",
TableColumn.UNGROUPED) {
@Override
public String getCellValue(Plate plate)
{
return plate.getCopy().getLibrary().getLibraryName();
}
@Override
public boolean isCommandLink()
{
return true;
}
@Override
public Object cellAction(Plate plate)
{
return _libraryViewer.viewEntity(plate.getCopy().getLibrary());
}
});
Iterables.getLast(columns).setVisible(_mode == Mode.ALL);
columns.add(new IntegerEntityColumn<Plate>(Plate.stockPlate.toProperty("plateNumber"),
"Maps to Library Plate Number",
"The stock plate to which this master stock plate maps",
TableColumn.UNGROUPED) {
@Override
public Integer getCellValue(Plate plate)
{
return plate.getStockPlateMapping() == null ? null : plate.getStockPlateMapping().getPlateNumber();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new EnumEntityColumn<Plate,Quadrant>(Plate.quadrant,
"Maps To Library Plate Quadrant",
"The quadrant of the stock plate to which this master stock plate maps",
TableColumn.UNGROUPED,
Quadrant.values()) {
@Override
public Quadrant getCellValue(Plate plate)
{
return plate.getStockPlateMapping() == null ? null : plate.getStockPlateMapping().getQuadrant();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new EnumEntityColumn<Plate,ScreenType>(Plate.copy.to(Copy.library).toProperty("screenType"),
"Screen Type",
"'RNAi' or 'Small Molecule'",
TableColumn.UNGROUPED,
ScreenType.values()) {
@Override
public ScreenType getCellValue(Plate plate)
{
return plate.getCopy().getLibrary().getScreenType();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new EnumEntityColumn<Plate,PlateStatus>(PropertyPath.from(Plate.class).toProperty("status"),
"Status",
"The plate status",
TableColumn.UNGROUPED,
PlateStatus.values()) {
@Override
public PlateStatus getCellValue(Plate plate)
{
return plate.getStatus();
}
});
columns.add(new DateEntityColumn<Plate>(PropertyPath.from(Plate.class).to(Plate.updateActivities).to(Activity.dateOfActivity),
"Status Date",
"The date on which the status took effect",
TableColumn.UNGROUPED) {
@Override
protected LocalDate getDate(Plate plate)
{
// note: we call getLastRecordedUpdateActivityOfType() instead of getLastUpdateActivityOfType(), as PlateBatchUpdater may
// have created an status update activity with an activity date that is more recent than the current activity's status date
return plate.getLastRecordedUpdateActivityOfType(AdministrativeActivityType.PLATE_STATUS_UPDATE).getDateOfActivity();
}
});
columns.add(new UserNameColumn<Plate,ScreensaverUser>(PropertyPath.from(Plate.class).to(Plate.updateActivities).to(Activity.performedBy),
"Status Change Performed By",
"The person that performed the activity appropriate for the plate status",
TableColumn.UNGROUPED,
null/* TODO */) {
@Override
protected ScreensaverUser getUser(Plate plate)
{
return plate.getLastUpdateActivityOfType(AdministrativeActivityType.PLATE_STATUS_UPDATE).getPerformedBy();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new TextEntityColumn<Plate>(Plate.location.toProperty("room"),
"Room",
"The room where the plate is stored",
TableColumn.UNGROUPED) {
@Override
public String getCellValue(Plate plate)
{
if (plate.getLocation() == null) {
return null;
}
return plate.getLocation().getRoom();
}
});
columns.add(new TextEntityColumn<Plate>(Plate.location.toProperty("freezer"),
"Freezer",
"The freezer where the plate is stored",
TableColumn.UNGROUPED) {
@Override
public String getCellValue(Plate plate)
{
if (plate.getLocation() == null) {
return null;
}
return plate.getLocation().getFreezer();
}
});
columns.add(new TextEntityColumn<Plate>(Plate.location.toProperty("shelf"),
"Shelf",
"The freezer shelf upon which plate is stored",
TableColumn.UNGROUPED) {
@Override
public String getCellValue(Plate plate)
{
if (plate.getLocation() == null) {
return null;
}
return plate.getLocation().getShelf();
}
});
columns.add(new TextEntityColumn<Plate>(Plate.location.toProperty("bin"),
"Bin",
"The bin in which the plate is stored",
TableColumn.UNGROUPED) {
@Override
public String getCellValue(Plate plate)
{
if (plate.getLocation() == null) {
return null;
}
return plate.getLocation().getBin();
}
});
columns.add(new EnumEntityColumn<Plate,PlateType>(PropertyPath.from(Plate.class).toProperty("plateType"),
"Plate Type",
"The plate type",
TableColumn.UNGROUPED,
PlateType.values()) {
@Override
public PlateType getCellValue(Plate plate)
{
return plate.getPlateType();
}
});
columns.add(new VolumeEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("wellVolume"),
"Initial Well Volume",
"The volume of each well of the plate when it was created",
TableColumn.UNGROUPED) {
@Override
public Volume getCellValue(Plate plate)
{
return plate.getWellVolume();
}
});
volumeStatisticColumns.add(Iterables.getLast(columns));
columns.add(new VolumeColumn<Plate>("Average Remaining Volume",
"The average volume remaining across all wells of this plate",
TableColumn.UNGROUPED) {
@Override
public Volume getCellValue(Plate plate)
{
return getNullSafeVolumeStatistics(plate).getAverageRemaining();
}
});
buildConcentrationColumns(columns);
columns.add(new DateEntityColumn<Plate>(PropertyPath.from(Plate.class).to(Plate.updateActivities).to(Activity.dateOfActivity),
"Location Transfer Date",
"The last time the plate was transfered to a new location",
TableColumn.UNGROUPED) {
@Override
protected LocalDate getDate(Plate plate)
{
return plate.getLastUpdateActivityOfType(AdministrativeActivityType.PLATE_LOCATION_TRANSFER).getDateOfActivity();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new UserNameColumn<Plate,ScreensaverUser>(PropertyPath.from(Plate.class).to(Plate.updateActivities).to(Activity.performedBy),
"Location Transfer Performed By ",
"The person that transfered the plate to a new location",
TableColumn.UNGROUPED,
null/* TODO */) {
@Override
protected ScreensaverUser getUser(Plate plate)
{
return plate.getLastUpdateActivityOfType(AdministrativeActivityType.PLATE_LOCATION_TRANSFER).getPerformedBy();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("facilityId"),
"Facility ID",
"The identifier used by the facility to uniquely identify the plate",
TableColumn.UNGROUPED) {
@Override
public String getCellValue(Plate plate)
{
return plate.getFacilityId();
}
});
columns.add(new DateEntityColumn<Plate>(Plate.platedActivity.toProperty("dateOfActivity"),
"Date Plated",
"The date the plate was created",
TableColumn.UNGROUPED) {
@Override
protected LocalDate getDate(Plate plate)
{
return plate.getPlatedActivity() == null ? null : plate.getPlatedActivity().getDateOfActivity();
}
});
columns.add(new DateEntityColumn<Plate>(Plate.retiredActivity.toProperty("dateOfActivity"),
"Date Retired",
"The date the plate was retired",
TableColumn.UNGROUPED) {
@Override
protected LocalDate getDate(Plate plate)
{
return plate.getRetiredActivity() == null ? null : plate.getRetiredActivity().getDateOfActivity();
}
});
columns.add(new IntegerColumn<Plate>("Assay Plate Count", "The number of assay plates screened for this plate", TableColumn.UNGROUPED) {
@Override
public Integer getCellValue(Plate plate)
{
return getNullSafeScreeningStatistics(plate).getAssayPlateCount();
}
});
screeningStatisticColumns.add(Iterables.getLast(columns));
columns.add(new IntegerColumn<Plate>("Screening Count", "The total number of times this plate has been screened, ignoring replicates",
TableColumn.UNGROUPED) {
@Override
public Integer getCellValue(Plate plate)
{
return getNullSafeScreeningStatistics(plate).getScreeningCount();
}
@Override
public boolean isCommandLink()
{
return true;
}
@Override
public Object cellAction(Plate plate)
{
_activitiesBrowser.searchLibraryScreeningActivitiesForPlate(plate);
return BROWSE_ACTIVITIES;
}
});
screeningStatisticColumns.add(Iterables.getLast(columns));
columns.add(new DateColumn<Plate>("First Date Screened", "The date the copy was first screened", TableColumn.UNGROUPED) {
@Override
public LocalDate getDate(Plate plate)
{
return getNullSafeScreeningStatistics(plate).getFirstDateScreened();
}
});
screeningStatisticColumns.add(Iterables.getLast(columns));
columns.add(new DateColumn<Plate>("Last Date Screened", "The date the copy was last screened", TableColumn.UNGROUPED) {
@Override
public LocalDate getDate(Plate plate)
{
return getNullSafeScreeningStatistics(plate).getLastDateScreened();
}
});
screeningStatisticColumns.add(Iterables.getLast(columns));
columns.add(new IntegerColumn<Plate>("Data Loading Count", "The number of screen results loaded for screens of this copy", TableColumn.UNGROUPED) {
@Override
public Integer getCellValue(Plate plate)
{
return getNullSafeScreeningStatistics(plate).getDataLoadingCount();
}
});
screeningStatisticColumns.add(Iterables.getLast(columns));
columns.add(new DateColumn<Plate>("First Date Data Loaded", "The date of the first screen result data loading activity", TableColumn.UNGROUPED) {
@Override
public LocalDate getDate(Plate plate)
{
return getNullSafeScreeningStatistics(plate).getFirstDateDataLoaded();
}
});
screeningStatisticColumns.add(Iterables.getLast(columns));
Iterables.getLast(columns).setVisible(false);
columns.add(new DateColumn<Plate>("Last Date Data Loaded", "The date of the last screen result data loading activity", TableColumn.UNGROUPED) {
@Override
public LocalDate getDate(Plate plate)
{
return getNullSafeScreeningStatistics(plate).getLastDateDataLoaded();
}
});
screeningStatisticColumns.add(Iterables.getLast(columns));
Iterables.getLast(columns).setVisible(false);
columns.add(new DateTimeColumn<Plate>("Last Updated", "The date on which the plate's most recent administrative update was recorded", TableColumn.UNGROUPED) {
@Override
public DateTime getDateTime(Plate plate)
{
SortedSet<AdministrativeActivity> activities = plate.getUpdateActivities();
if (activities.isEmpty()) {
return null;
}
// TODO: should order by dateCreated, rather than dateOfActivity
return activities.last().getDateCreated();
}
@Override
public boolean isCommandLink()
{
return true;
}
@Override
public Object cellAction(Plate plate)
{
_entityUpdateHistoryBrowser.searchForParentEntity(plate);
return BROWSE_ENTITY_UPDATE_HISTORY;
}
});
columns.add(new DateColumn<Plate>("Last Comment Date", "The date of the last comment added on this plate", TableColumn.UNGROUPED) {
@Override
public LocalDate getDate(Plate plate)
{
return plate.getLastUpdateActivityOfType(AdministrativeActivityType.COMMENT).getDateOfActivity();
}
@Override
public boolean isCommandLink()
{
return true;
}
@Override
public Object cellAction(Plate plate)
{
_entityUpdateHistoryBrowser.searchForParentEntity(plate);
_entityUpdateHistoryBrowser.setTitle("Comments for " + plate); // TODO: need user-friendly toString(), see [#2560]
((TableColumn<AdministrativeActivity,AdministrativeActivityType>) _entityUpdateHistoryBrowser.getColumnManager().getColumn("Update Type")).
resetCriteria().setOperatorAndValue(Operator.EQUAL, AdministrativeActivityType.COMMENT);
return BROWSE_ENTITY_UPDATE_HISTORY;
}
});
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).to(Plate.updateActivities).toProperty("comments"),
"Last Comment",
"Last Comment",
TableColumn.UNGROUPED) {
@Override
public String getCellValue(Plate plate)
{
return plate.getLastUpdateActivityOfType(AdministrativeActivityType.COMMENT).getComments();
}
});
Iterables.getLast(columns).setVisible(false);
return columns;
}
private void buildConcentrationColumns(List<TableColumn<Plate,?>> columns)
{
// Concentration Columns
// molar values - undiluted
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("minMolarConcentration"),
"Undiluted Minimum Well Molar Concentration",
"The undiluted minimum molar concentration of the wells of the plate when it was created",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
MolarConcentration value = plate.getMinMolarConcentration();
return value == null ? "n/a": value.toString();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("maxMolarConcentration"),
"Undiluted Maximum Well Molar Concentration",
"The undiluted maximum molar concentration of the wells of the plate when it was created",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
MolarConcentration value = plate.getMaxMolarConcentration();
return value == null ? "n/a": value.toString();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("primaryWellMolarConcentration"),
"Undiluted Primary Well Molar Concentration",
"The undiluted value of the most frequent plate well molar concentration on creation",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
MolarConcentration value = plate.getPrimaryWellMolarConcentration();
return value == null ? "n/a": value.toString();
}
});
Iterables.getLast(columns).setVisible(false);
// molar values - undiluted
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("concentrationStatistics"),
"Final Minimum Well Molar Concentration",
"The diluted minimum molar concentration of the wells of the plate when it was created",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
BigDecimal df = plate.getCopy().getWellConcentrationDilutionFactor();
MolarConcentration value = plate.getNullSafeConcentrationStatistics().getDilutedMinMolarConcentration(df);
return value == null ? "n/a": value.toString();
}
});
Iterables.getLast(columns).setVisible(true);
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("concentrationStatistics"),
"Final Maximum Well Molar Concentration",
"The diluted maximum molar concentration of the wells of the plate when it was created",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
BigDecimal df = plate.getCopy().getWellConcentrationDilutionFactor();
MolarConcentration value = plate.getNullSafeConcentrationStatistics().getDilutedMaxMolarConcentration(df);
return value == null ? "n/a": value.toString();
}
});
Iterables.getLast(columns).setVisible(true);
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("concentrationStatistics"),
"Final Primary Molar Concentration",
"The diluted primary (most often occurring) plate well molar concentration",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
BigDecimal df = plate.getCopy().getWellConcentrationDilutionFactor();
MolarConcentration value = plate.getNullSafeConcentrationStatistics().getDilutedPrimaryWellMolarConcentration(df);
return value == null ? "n/a": value.toString();
}
});
Iterables.getLast(columns).setVisible(false);
// mg/mL values - undiluted
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("minMgMlConcentration"),
"Undiluted Minimum Well Concentration (mg/mL)",
"The undiluted minimum mg/mL concentration of the wells of the plate when it was created",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
BigDecimal value = plate.getMinMgMlConcentration();
return value == null ? "n/a" : value.toString();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("maxMgMlConcentration"),
"Undiluted Maximum Well Concentration (mg/mL)",
"The undiluted minimum mg/mL concentration of the wells of the plate when it was created",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
BigDecimal value = plate.getMaxMgMlConcentration();
return value == null ? "n/a" : value.toString();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("primaryWellMgMlConcentration"),
"Undiluted Primary Well Concentration (mg/mL)",
"The undiluted value of the most frequent plate well mg/mL concentration on creation",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
BigDecimal value = plate.getPrimaryWellMgMlConcentration();
return value == null ? "n/a" : value.toString();
}
});
Iterables.getLast(columns).setVisible(false);
// mg/mL values - diluted
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("concentrationStatistics"),
"Final Minimum Well Concentration (mg/mL)",
"The diluted minimum mg/mL concentration of the wells of the plate when it was created",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
BigDecimal df = plate.getCopy().getWellConcentrationDilutionFactor();
BigDecimal value = plate.getNullSafeConcentrationStatistics().getDilutedMinMgMlConcentration(df);
return value == null ? "n/a" : value.toString();
}
});
Iterables.getLast(columns).setVisible(true);
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("concentrationStatistics"),
"Final Maximum Well Concentration (mg/mL)",
"The diluted minimum mg/mL concentration of the wells of the plate when it was created",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
BigDecimal df = plate.getCopy().getWellConcentrationDilutionFactor();
BigDecimal value = plate.getNullSafeConcentrationStatistics().getDilutedMaxMgMlConcentration(df);
return value == null ? "n/a" : value.toString();
}
});
Iterables.getLast(columns).setVisible(true);
columns.add(new TextEntityColumn<Plate>(PropertyPath.from(Plate.class).toProperty("concentrationStatistics"),
"Final Primary Concentration (mg/mL)",
"The diluted primary (most often occurring) plate well mg/mL concentration",
COLUMN_GROUP_CONCENTRATION) {
@Override
public String getCellValue(Plate plate)
{
BigDecimal df = plate.getCopy().getWellConcentrationDilutionFactor();
BigDecimal value = plate.getNullSafeConcentrationStatistics().getDilutedPrimaryWellMgMlConcentration(df);
return value == null ? "n/a" : value.toString();
}
});
Iterables.getLast(columns).setVisible(false);
columns.add(new FixedDecimalEntityColumn<Plate>(PropertyPath.from(Plate.class).to("copy").toProperty("wellConcentrationDilutionFactor"),
"Well Concentration Dilution Factor",
"The factor by which the original library well concentration is diluted for this copy plate",
COLUMN_GROUP_CONCENTRATION) {
@Override
public BigDecimal getCellValue(Plate plate)
{
return plate.getCopy().getWellConcentrationDilutionFactor();
}
});
Iterables.getLast(columns).setVisible(false);
}
private static ScreeningStatistics getNullSafeScreeningStatistics(Plate plate)
{
ScreeningStatistics screeningStatistics = plate.getScreeningStatistics();
if (screeningStatistics == null) {
return ScreeningStatistics.NullScreeningStatistics;
}
return screeningStatistics;
}
private static VolumeStatistics getNullSafeVolumeStatistics(Plate plate)
{
VolumeStatistics volumeStatistics = plate.getVolumeStatistics();
if (volumeStatistics == null) {
return VolumeStatistics.Null;
}
return volumeStatistics;
}
public boolean isBatchEditable()
{
return getScreensaverUser().isUserInRole(getEditingRole());
}
@UICommand
public String batchClear()
{
_libraryCopyPlateBatchEditor.initialize();
return REDISPLAY_PAGE_ACTION_RESULT;
}
public String getReviewMessage()
{
return _reviewMessage;
}
private void updateReviewMessage()
{
if (!isBatchEditable() || getRowCount() == 0) {
_reviewMessage = "";
}
Set<Plate> plates = Sets.newHashSet(getDataTableModel().iterator());
int nLibraries = Sets.newHashSet(Iterables.transform(plates, Functions.compose(Copy.ToLibrary, Plate.ToCopy))).size();
int nCopies = Sets.newHashSet(Iterables.transform(plates, Plate.ToCopy)).size();
Set<PlateLocation> locations = Sets.newHashSet(Iterables.filter(Iterables.transform(plates, Plate.ToLocation), Predicates.notNull()));
int nRooms = Sets.newHashSet(Iterables.transform(locations, PlateLocation.ToRoom)).size();
int nFreezers = Sets.newHashSet(Iterables.transform(locations, PlateLocation.ToRoomFreezer)).size();
int nShelves = Sets.newHashSet(Iterables.transform(locations, PlateLocation.ToRoomFreezerShelf)).size();
StringBuilder msg = new StringBuilder("Updating ").append(getRowCount()).append(" plate");
if (getRowCount() != 1) {
msg.append('s');
}
msg.append(" from ");
msg.append(nLibraries).append(" librar").append(nLibraries == 1 ? "y" : "ies");
msg.append(" and ").append(nCopies).append(" cop").append(nCopies == 1 ? "y" : "ies");
msg.append(" across ").append(locations.size()).append(" bin location").append(locations.size() == 1 ? "" : "s").append(" on ");
msg.append(nShelves).append(" shel").append(nShelves == 1 ? "f" : "ves");
msg.append(" in ").append(nFreezers).append(" freezer").append(nFreezers == 1 ? "" : "s");
msg.append(" in ").append(nRooms).append(" room").append(nRooms == 1 ? "" : "s");
msg.append(". Proceed?");
_reviewMessage = msg.toString();
}
@UICommand
public String batchUpdate()
{
if (isBatchEditable()) {
Iterator<Plate> rowIter = getDataTableModel().iterator();
try {
boolean updated = _libraryCopyPlateBatchEditor.updatePlates(Sets.newHashSet(rowIter));
// if user input validation fails, we do not want to reset the input fields, allowing the
// user to modify, as necessary; if we proceeded on below, the reload() call would reset the UI
if (!updated) {
return REDISPLAY_PAGE_ACTION_RESULT;
}
}
catch (Exception e) {
showMessage("applicationError", e.getMessage());
}
// note: we reload() after success *or* failure, since we want the Plate entities to be reloaded in either case
reload();
if (getNestedIn() != null) {
getNestedIn().reload();
}
}
return REDISPLAY_PAGE_ACTION_RESULT;
}
public LibraryCopyPlateBatchEditor getBatchEditor()
{
return _libraryCopyPlateBatchEditor;
}
public void setEntityUpdateHistoryBrowser(EntityUpdateSearchResults<Plate,Integer> entityUpdateHistoryBrowser)
{
_entityUpdateHistoryBrowser = entityUpdateHistoryBrowser;
}
public EntityUpdateSearchResults<Plate,Integer> getEntityUpdateHistoryBrowser()
{
return _entityUpdateHistoryBrowser;
}
}