// $HeadURL$
// $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.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import edu.harvard.med.screensaver.db.DAOTransaction;
import edu.harvard.med.screensaver.db.LibrariesDAO;
import edu.harvard.med.screensaver.db.Criterion.Operator;
import edu.harvard.med.screensaver.model.MolarConcentration;
import edu.harvard.med.screensaver.model.MolarUnit;
import edu.harvard.med.screensaver.model.Volume;
import edu.harvard.med.screensaver.model.VolumeUnit;
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.LibraryContentsVersion;
import edu.harvard.med.screensaver.model.libraries.LibraryType;
import edu.harvard.med.screensaver.model.libraries.LibraryWellType;
import edu.harvard.med.screensaver.model.libraries.Plate;
import edu.harvard.med.screensaver.model.libraries.PlateSize;
import edu.harvard.med.screensaver.model.libraries.PlateStatus;
import edu.harvard.med.screensaver.model.libraries.PlateType;
import edu.harvard.med.screensaver.model.libraries.Well;
import edu.harvard.med.screensaver.model.screens.ScreenType;
import edu.harvard.med.screensaver.model.users.ScreensaverUserRole;
import edu.harvard.med.screensaver.service.libraries.LibraryContentsVersionManager;
import edu.harvard.med.screensaver.service.libraries.LibraryCreator;
import edu.harvard.med.screensaver.service.libraries.PlateUpdater;
import edu.harvard.med.screensaver.ui.arch.datatable.column.TableColumn;
import edu.harvard.med.screensaver.ui.arch.view.AbstractBackingBeanTest;
public class LibraryCopyDetailViewerTest extends AbstractBackingBeanTest
{
private static Logger log = Logger.getLogger(LibraryCopyDetailViewerTest.class);
@Autowired
protected LibraryCopyDetail libraryCopyDetail;
@Autowired
protected LibraryContentsVersionManager libraryContentsVersionManager;
@Autowired
protected LibraryCreator libraryCreator;
@Autowired
protected LibrariesDAO librariesDao;
private Copy _copyC;
private Copy _copyD;
private Library _library;
private Library _library2;
private Copy _copy2;
private MolarConcentration minMolarConcentration = MolarConcentration.makeConcentration("100", MolarUnit.PICOMOLAR);
private MolarConcentration maxMolarConcentration = MolarConcentration.makeConcentration("100", MolarUnit.MILLIMOLAR);
private MolarConcentration primaryMolarConcentration = MolarConcentration.makeConcentration("200", MolarUnit.MICROMOLAR);
private BigDecimal minMgMlConcentration = new BigDecimal("0.010");
private BigDecimal maxMgMlConcentration = new BigDecimal("5.000");
private BigDecimal primaryMgMlConcentration = new BigDecimal("3.000");
private BigDecimal primaryMgMlConcentration2 = new BigDecimal("2.000");
protected void setUp() throws Exception
{
super.setUp();
genericEntityDao.doInTransaction(new DAOTransaction() {
@Override
public void runTransaction()
{
_admin.addScreensaverUserRole(ScreensaverUserRole.LIBRARY_COPIES_ADMIN);
_admin.addScreensaverUserRole(ScreensaverUserRole.LIBRARIES_ADMIN);
_admin = genericEntityDao.mergeEntity(_admin);
_library = new Library(null, "lib", "lib", ScreenType.SMALL_MOLECULE, LibraryType.COMMERCIAL, 1, 6, PlateSize.WELLS_96);
libraryCreator.createLibrary(_library);
for (Well well : _library.getWells())
{
well.setLibraryWellType(LibraryWellType.EXPERIMENTAL);
}
_library = genericEntityDao.mergeEntity(_library);
_copyC = _library.createCopy(null, CopyUsageType.LIBRARY_SCREENING_PLATES, "C");
_copyD = _library.createCopy(null, CopyUsageType.LIBRARY_SCREENING_PLATES, "D");
_library2 = new Library(null, "lib2", "lib2", ScreenType.SMALL_MOLECULE, LibraryType.COMMERCIAL, 7, 12, PlateSize.WELLS_96);
libraryCreator.createLibrary(_library2);
for (Well well : _library2.getWells())
{
well.setLibraryWellType(LibraryWellType.EXPERIMENTAL);
}
_library2 = genericEntityDao.mergeEntity(_library2);
_copy2 = _library2.createCopy(null, CopyUsageType.LIBRARY_SCREENING_PLATES, "2");
libraryCopyDetail.getCurrentScreensaverUser().setScreensaverUser(_admin);
genericEntityDao.flush();
LibraryContentsVersion lcv = _library.createContentsVersion(_admin);
genericEntityDao.persistEntity(lcv);
lcv = _library2.createContentsVersion(_admin);
genericEntityDao.persistEntity(lcv);
genericEntityDao.flush(); //?
_admin = genericEntityDao.reloadEntity(_admin);
_library = genericEntityDao.reloadEntity(_library, false, Library.contentsVersions);
libraryContentsVersionManager.releaseLibraryContentsVersion(_library.getLatestContentsVersion(), _admin);
_library2 = genericEntityDao.reloadEntity(_library2, false, Library.contentsVersions);
libraryContentsVersionManager.releaseLibraryContentsVersion(_library2.getLatestContentsVersion(), _admin);
// Create a non-available plate, which should be ignored for concentration calculations at the copy level
Map<String,Object> properties = Maps.newHashMap();
properties.put("copy", _copyC);
properties.put("plateNumber", new Integer(2));
Plate p = genericEntityDao.findEntityByProperties(Plate.class, properties, true);
p.setStatus(PlateStatus.LOST);
}
});
}
public void setupInTransaction_concentrations_heterogeneous()
{
genericEntityDao.doInTransaction(new DAOTransaction() {
@Override
public void runTransaction()
{
_admin = genericEntityDao.reloadEntity(_admin);
_library = genericEntityDao.reloadEntity(_library, false, Library.contentsVersions);
LibraryContentsVersion lcv = _library.createContentsVersion(_admin);
genericEntityDao.persistEntity(lcv);
genericEntityDao.flush(); //?
}
});
genericEntityDao.doInTransaction(new DAOTransaction() {
@Override
public void runTransaction()
{
_library = genericEntityDao.reloadEntity(_library, false, Library.wells);
// ignore plate 4
for (Copy c : _library.getCopies()) {
for (Plate p : c.getPlates().values())
{
p.setStatus(PlateStatus.AVAILABLE);
if (p.getPlateNumber() == 4) {
p.setStatus(PlateStatus.DISCARDED);
}
}
}
// Plate 1 heterogeneous
for (Well well : librariesDao.findWellsForPlate(1))
{
if (well.getColumn() == 0 && well.getRow() == 0) { // set a few min
well.setMgMlConcentration(minMgMlConcentration);
well.setMolarConcentration(minMolarConcentration);
}
else if (well.getColumn() == 2 && well.getRow() == 0) { // set a few max
well.setMgMlConcentration(maxMgMlConcentration);
well.setMolarConcentration(maxMolarConcentration);
}
else {
well.setMgMlConcentration(primaryMgMlConcentration);
well.setMolarConcentration(primaryMolarConcentration);
}
}
// Plate 2 homogeneous
for (Well well : librariesDao.findWellsForPlate(2))
{
well.setMgMlConcentration(primaryMgMlConcentration);
well.setMolarConcentration(primaryMolarConcentration);
}
// Plate 3 homogeneous
for (Well well : librariesDao.findWellsForPlate(3))
{
well.setMgMlConcentration(primaryMgMlConcentration);
well.setMolarConcentration(primaryMolarConcentration);
}
// Plate 4 homogeneous
for (Well well : librariesDao.findWellsForPlate(4))
{
well.setMgMlConcentration(primaryMgMlConcentration);
well.setMolarConcentration(primaryMolarConcentration);
}
// Plate 5 homogeneous
for (Well well : librariesDao.findWellsForPlate(5))
{
well.setMgMlConcentration(primaryMgMlConcentration);
well.setMolarConcentration(primaryMolarConcentration);
}
genericEntityDao.mergeEntity(_library);
genericEntityDao.flush();
}
});
genericEntityDao.doInTransaction(new DAOTransaction() {
@Override
public void runTransaction()
{
_admin = genericEntityDao.reloadEntity(_admin);
_library = genericEntityDao.reloadEntity(_library, false, Library.contentsVersions);
libraryContentsVersionManager.releaseLibraryContentsVersion(_library.getLatestContentsVersion(), _admin);
}
});
}
public void setupInTransaction_concentrations()
{
genericEntityDao.doInTransaction(new DAOTransaction() {
@Override
public void runTransaction()
{
_admin = genericEntityDao.reloadEntity(_admin);
_library = genericEntityDao.reloadEntity(_library, false, Library.contentsVersions);
LibraryContentsVersion lcv = _library.createContentsVersion(_admin);
_library2 = genericEntityDao.reloadEntity(_library2, false, Library.contentsVersions);
lcv = _library2.createContentsVersion(_admin);
genericEntityDao.persistEntity(lcv);
genericEntityDao.flush(); //?
}
});
genericEntityDao.doInTransaction(new DAOTransaction() {
@Override
public void runTransaction()
{
_library = genericEntityDao.reloadEntity(_library, false, Library.wells);
for (Copy c : _library.getCopies()) {
for (Plate p : c.getPlates().values())
{
p.setStatus(PlateStatus.AVAILABLE);
if (p.getPlateNumber() == 4) {
p.setStatus(PlateStatus.DISCARDED);
}
}
}
int i = 0;
for (Well well : _library.getWells()) {
well.setMgMlConcentration(primaryMgMlConcentration);
well.setMolarConcentration(primaryMolarConcentration);
}
genericEntityDao.mergeEntity(_library);
// library 2 has no molar concentrations
_library2 = genericEntityDao.reloadEntity(_library2, false, Library.wells);
for (Copy c : _library2.getCopies()) {
for (Plate p : c.getPlates().values())
{
p.setStatus(PlateStatus.AVAILABLE);
}
}
for (Well well : librariesDao.findWellsForPlate(7))
{
well.setMgMlConcentration(primaryMgMlConcentration2);
log.info("well: " + well.getMolarConcentration() + ", " + well.getLibrary().getShortName());
}
genericEntityDao.mergeEntity(_library2);
}
});
// release last, as the releaseLibraryContentsVersion() call will invoke the plateUpdater
genericEntityDao.doInTransaction(new DAOTransaction() {
@Override
public void runTransaction()
{
_admin = genericEntityDao.reloadEntity(_admin);
_library = genericEntityDao.reloadEntity(_library, false, Library.contentsVersions);
libraryContentsVersionManager.releaseLibraryContentsVersion(_library.getLatestContentsVersion(), _admin);
_library2 = genericEntityDao.reloadEntity(_library2, false, Library.contentsVersions);
libraryContentsVersionManager.releaseLibraryContentsVersion(_library2.getLatestContentsVersion(), _admin);
_copy2 = genericEntityDao.reloadEntity(_copy2);
assertNotNull(_copy2);
assertNull("_copy2.getPrimaryWellMolarConcentration(): " + _copy2.getPrimaryWellMolarConcentration(), _copy2.getPrimaryWellMolarConcentration());
}
});
}
@Transactional
public void testSetMgMlConcentration() throws Exception
{
setupInTransaction_concentrations();
_copyC = genericEntityDao.reloadEntity(_copyC);
assertNotNull(_copyC);
libraryCopyDetail.setEntity(_copyC);
libraryCopyDetail.setMgMlConcentration(new BigDecimal("1"));
libraryCopyDetail.save();
_copyC = genericEntityDao.reloadEntity(_copyC);
assertNotNull(_copyC);
assertTrue("actual:" + _copyC.getWellConcentrationDilutionFactor(),
new BigDecimal("3.0").compareTo(_copyC.getWellConcentrationDilutionFactor()) == 0);
assertTrue("actual: " + _copyC +
_copyC.getNullSafeConcentrationStatistics().getDilutedPrimaryWellMgMlConcentration(_copyC.getWellConcentrationDilutionFactor()),
BigDecimal.ONE.compareTo(_copyC.getNullSafeConcentrationStatistics().getDilutedPrimaryWellMgMlConcentration(_copyC.getWellConcentrationDilutionFactor())) == 0);
}
@Transactional
public void testSetMolarConcentration() throws Exception
{
setupInTransaction_concentrations();
_copyC = genericEntityDao.reloadEntity(_copyC);
assertNotNull(_copyC);
libraryCopyDetail.setEntity(_copyC);
// // on plate = 200 MicroMolar, actual = 333 NanoMolar, or .333 MicroMolar, pdf = 600.600600... pdf_rounded = 600.60, rounded, on plate: .330 uM, actual .333000333000333... uM
BigDecimal pdf_rounded = new BigDecimal("600.60");
libraryCopyDetail.setMolarConcentrationValue("333");
libraryCopyDetail.getMolarConcentrationType().setSelection(MolarUnit.NANOMOLAR);
libraryCopyDetail.save();
_copyC = genericEntityDao.reloadEntity(_copyC);
assertNotNull(_copyC);
assertEquals("for copy: " + _copyC, primaryMgMlConcentration, _copyC.getPrimaryWellMgMlConcentration());
assertEquals("for copy: " + _copyC, primaryMolarConcentration, _copyC.getPrimaryWellMolarConcentration());
assertTrue("actual:" + _copyC.getWellConcentrationDilutionFactor(),
pdf_rounded.compareTo(_copyC.getWellConcentrationDilutionFactor()) == 0);
// this involves roundipdf_roundedng, scale = pdf.scale
assertTrue("actual:" +
_copyC.getNullSafeConcentrationStatistics().getDilutedPrimaryWellMolarConcentration(_copyC.getWellConcentrationDilutionFactor()),
MolarConcentration.makeConcentration("330.000", MolarUnit.NANOMOLAR)
.compareTo(_copyC.getNullSafeConcentrationStatistics().getDilutedPrimaryWellMolarConcentration(_copyC.getWellConcentrationDilutionFactor())) == 0);
}
@Transactional
public void testSetMgMlConcentration_rounded() throws Exception
{
setupInTransaction_concentrations();
_copyC = genericEntityDao.reloadEntity(_copyC);
assertNotNull(_copyC);
libraryCopyDetail.setEntity(_copyC);
// Simple test: original: 3, target 2.11, pdf (exact) = 1.4218, pdf (rounded, scale=2) = 1.42
BigDecimal target = new BigDecimal("2.11");
BigDecimal pdf_rounded = new BigDecimal("1.42");
libraryCopyDetail.setMgMlConcentration(target);
libraryCopyDetail.getMolarConcentrationType().setSelection(MolarUnit.NANOMOLAR);
libraryCopyDetail.save();
_copyC = genericEntityDao.reloadEntity(_copyC);
assertNotNull(_copyC);
assertEquals("for copy: " + _copyC, primaryMgMlConcentration, _copyC.getPrimaryWellMgMlConcentration());
assertTrue("actual:" + _copyC.getWellConcentrationDilutionFactor(),
pdf_rounded.compareTo(_copyC.getWellConcentrationDilutionFactor()) == 0);
assertTrue("actual:" +
_copyC.getNullSafeConcentrationStatistics().getDilutedPrimaryWellMgMlConcentration(_copyC.getWellConcentrationDilutionFactor()),
target.compareTo(_copyC.getNullSafeConcentrationStatistics().getDilutedPrimaryWellMgMlConcentration(_copyC.getWellConcentrationDilutionFactor())) == 0);
List<Plate> plates = genericEntityDao.findEntitiesByProperty(Plate.class, "copy", _copyC, true, Plate.updateActivities.castToSubtype(Plate.class));
assertEquals(6, plates.size());
for (Plate p : plates) {
assertTrue("for plate: " + p + " actual:" + p.getPrimaryWellMgMlConcentration(),
primaryMgMlConcentration.compareTo(p.getPrimaryWellMgMlConcentration()) == 0);
assertTrue("for plate: " + p + " actual:" +
p.getNullSafeConcentrationStatistics().getDilutedPrimaryWellMgMlConcentration(_copyC.getWellConcentrationDilutionFactor()),
target.compareTo(p.getNullSafeConcentrationStatistics().getDilutedPrimaryWellMgMlConcentration(_copyC.getWellConcentrationDilutionFactor())) == 0);
assertTrue("for plate: " + p + " actual:" + p.getWellConcentrationDilutionFactor(),
pdf_rounded.compareTo(p.getWellConcentrationDilutionFactor()) == 0);
}
}
@Transactional
public void testSetMolarError() throws Exception
{
setupInTransaction_concentrations();
_copy2 = genericEntityDao.reloadEntity(_copy2);
assertNotNull(_copy2);
assertNull(_copy2.getPrimaryWellMolarConcentration());
libraryCopyDetail.getMessages().getMessagesAndDequeue(); // clear any messages on the viewer
libraryCopyDetail.setEntity(_copy2);
// // on plate = 100 MicroMolar, actual = 333 NanoMolar, or .333 MicroMolar, pdf = 300.300300... pdf_rounded = 300.30, rounded, on plate: .330 uM, actual .333000333000333... uM
BigDecimal pdf_rounded = new BigDecimal("300.30");
libraryCopyDetail.setMolarConcentrationValue("333");
libraryCopyDetail.getMolarConcentrationType().setSelection(MolarUnit.NANOMOLAR);
libraryCopyDetail.save(); // should throw an error as there is no molar concentration on the copy
assertFalse(libraryCopyDetail.getMessages().getQueuedMessages().isEmpty());
assertTrue("libraryCopyDetail.getMessages().getQueuedMessages().get(0).getSecond().getSummary(): " + libraryCopyDetail.getMessages().getQueuedMessages().get(0).getSecond().getSummary(),
libraryCopyDetail.getMessages().getQueuedMessages().get(0).getSecond().getSummary().contains("Set using the dilution factor instead"));
_copy2 = genericEntityDao.reloadEntity(_copy2);
assertNotNull(_copy2);
assertTrue(BigDecimal.ONE.compareTo(_copy2.getWellConcentrationDilutionFactor())==0);
assertNull(_copy2.getNullSafeConcentrationStatistics().getPrimaryWellMolarConcentration());
}
}