// $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.iccbl.screensaver.io.screens; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.apache.log4j.Logger; import org.hibernate.SQLQuery; import org.hibernate.Session; import edu.harvard.med.screensaver.db.DAOTransaction; import edu.harvard.med.screensaver.db.GenericEntityDAO; import edu.harvard.med.screensaver.db.Query; import edu.harvard.med.screensaver.io.CommandLineApplication; import edu.harvard.med.screensaver.model.libraries.Plate; import edu.harvard.med.screensaver.model.screenresults.AssayPlate; import edu.harvard.med.screensaver.model.screens.LibraryScreening; import edu.harvard.med.screensaver.model.users.AdministratorUser; import edu.harvard.med.screensaver.model.users.ScreeningRoomUser; /** * Command-line application that splits LibraryScreenings into two or more, to bring legacy data into compliance with * the current domain model, where a library screening may only contain one assay plate per library plate number and * replicate ordinal. * * @author <a mailto="andrew_tolopko@hms.harvard.edu">Andrew Tolopko</a> */ public class LibraryScreeningSplitter { private static final String MIGRATION_COMMENT_TEMPLATE = "This library screening was created automatically during data migration. It was created to accommodate library plates that were screened multiple times within the original library screening (#)."; private static Logger log = Logger.getLogger(LibraryScreeningSplitter.class); public static void main(String[] args) { final CommandLineApplication app = new CommandLineApplication(args); app.processOptions(true, false); final GenericEntityDAO dao = (GenericEntityDAO) app.getSpringBean("genericEntityDao"); dao.doInTransaction(new DAOTransaction() { @Override public void runTransaction() { Set<LibraryScreening> libraryScreeningsWithDuplicatePlateNumbers = findLibraryScreeningsWithDuplicatePlateNumbers(dao); for (LibraryScreening libraryScreening : libraryScreeningsWithDuplicatePlateNumbers) { splitLibraryScreening(libraryScreening, dao); } //throw new DAOTransactionRollbackException("debug"); } }); } private static void splitLibraryScreening(LibraryScreening libraryScreening, GenericEntityDAO dao) { do { libraryScreening = dao.reloadEntity(libraryScreening); List<AssayPlate> assayPlatesToMove = findAssayPlatesToMove(libraryScreening, dao); if (assayPlatesToMove.isEmpty()) break; log.info("splitting " + libraryScreening); libraryScreening = cloneLibraryScreening(libraryScreening, assayPlatesToMove, dao); log.info("created " + libraryScreening + " with assay plates " + assayPlatesToMove); dao.flush(); dao.clear(); } while (true); } private static List<AssayPlate> findAssayPlatesToMove(LibraryScreening libraryScreening, GenericEntityDAO dao) { List<AssayPlate> assayPlatesToMove = Lists.newArrayList(); List<AssayPlate> allAssayPlates = dao.findEntitiesByProperty(AssayPlate.class, "libraryScreening", libraryScreening); Collections.sort(allAssayPlates, new Comparator<AssayPlate>() { @Override public int compare(AssayPlate a1, AssayPlate a2) { int result = Integer.valueOf(a1.getPlateNumber()).compareTo(Integer.valueOf(a2.getPlateNumber())); if (result == 0) { result = a1.getPlateScreened().getCopy().getName().compareTo(a2.getPlateScreened().getCopy().getName()); } if (result == 0) { result = Integer.valueOf(a1.getReplicateOrdinal()).compareTo(Integer.valueOf(a2.getReplicateOrdinal())); } return result; } }); Map<Integer,Plate> platesToKeep = Maps.newHashMap(); for (AssayPlate assayPlate : allAssayPlates) { Plate plateToKeep = platesToKeep.get(assayPlate.getPlateNumber()); if (plateToKeep == null) { platesToKeep.put(assayPlate.getPlateNumber(), assayPlate.getPlateScreened()); } else if (!plateToKeep.equals(assayPlate.getPlateScreened())) { assayPlatesToMove.add(assayPlate); } } return assayPlatesToMove; } private static LibraryScreening cloneLibraryScreening(LibraryScreening libraryScreening, List<AssayPlate> assayPlatesToMove, GenericEntityDAO dao) { AdministratorUser createdBy = null; if (libraryScreening.getCreatedBy() != null) { createdBy = dao.findEntityById(AdministratorUser.class, libraryScreening.getCreatedBy().getEntityId()); } ScreeningRoomUser performedBy = dao.findEntityById(ScreeningRoomUser.class, libraryScreening.getPerformedBy().getEntityId()); LibraryScreening newLibraryScreening = libraryScreening.getScreen().createLibraryScreening(createdBy, performedBy, libraryScreening.getDateOfActivity()); newLibraryScreening.setAbaseTestsetId(libraryScreening.getAbaseTestsetId()); newLibraryScreening.setAssayProtocol(libraryScreening.getAssayProtocol()); newLibraryScreening.setAssayProtocolLastModifiedDate(libraryScreening.getAssayProtocolLastModifiedDate()); newLibraryScreening.setAssayProtocolType(libraryScreening.getAssayProtocolType()); newLibraryScreening.setComments(MIGRATION_COMMENT_TEMPLATE.replaceFirst("#", Integer.toString(libraryScreening.getEntityId())) + "\n" + (libraryScreening.getComments() == null ? "" : libraryScreening.getComments())); newLibraryScreening.setMolarConcentration(libraryScreening.getMolarConcentration()); newLibraryScreening.setForExternalLibraryPlates(libraryScreening.isForExternalLibraryPlates()); newLibraryScreening.setNumberOfReplicates(libraryScreening.getNumberOfReplicates()); newLibraryScreening.setVolumeTransferredPerWellToAssayPlates(libraryScreening.getVolumeTransferredPerWellToAssayPlates()); newLibraryScreening.setVolumeTransferredPerWellFromLibraryPlates(libraryScreening.getVolumeTransferredPerWellFromLibraryPlates()); newLibraryScreening.setAssayWellVolume(libraryScreening.getAssayWellVolume()); for (AssayPlate assayPlate : assayPlatesToMove) { assayPlate.setLibraryScreening(newLibraryScreening); dao.saveOrUpdateEntity(assayPlate); } dao.saveOrUpdateEntity(newLibraryScreening); return newLibraryScreening; } private static Set<LibraryScreening> findLibraryScreeningsWithDuplicatePlateNumbers(GenericEntityDAO dao) { List<Integer> libraryScreeningIds = dao.runQuery(new Query() { @Override public List execute(Session session) { SQLQuery sqlQuery = session.createSQLQuery("select distinct library_screening_id from assay_plate ap join lab_activity la on(la.activity_id=ap.library_screening_id) join screen s on(s.screen_id=la.screen_id) group by library_screening_id, plate_number, replicate_ordinal having count(*) > 1;"); return sqlQuery.list(); } }); Set<LibraryScreening> result = Sets.newHashSet(); for (Integer id : libraryScreeningIds) { result.add(dao.findEntityById(LibraryScreening.class, id)); } return result; } }