// $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.model.screenresults; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.Transient; import javax.persistence.UniqueConstraint; import javax.persistence.Version; import com.google.common.base.Function; import com.google.common.base.Predicate; import org.apache.log4j.Logger; import org.hibernate.annotations.Parameter; import edu.harvard.med.screensaver.model.AbstractEntity; import edu.harvard.med.screensaver.model.AbstractEntityVisitor; import edu.harvard.med.screensaver.model.DataModelViolationException; import edu.harvard.med.screensaver.model.RequiredPropertyException; import edu.harvard.med.screensaver.model.activities.AdministrativeActivity; import edu.harvard.med.screensaver.model.annotations.ContainedEntity; import edu.harvard.med.screensaver.model.annotations.ToOne; import edu.harvard.med.screensaver.model.libraries.Plate; import edu.harvard.med.screensaver.model.libraries.Reagent; import edu.harvard.med.screensaver.model.meta.Cardinality; import edu.harvard.med.screensaver.model.meta.RelationshipPath; import edu.harvard.med.screensaver.model.screens.AssayPlateStatus; import edu.harvard.med.screensaver.model.screens.LibraryScreening; import edu.harvard.med.screensaver.model.screens.Screen; /** * A physical plate that is derived from a library {@link Plate} and that is used to perform the actual experimentation * in its wells by combining the {@link Reagent}s with the biological targets (e.g., cells). If screening multiple * replicates, one assay plate will exist for each replicate and will have an assigned {@link #getReplicateOrdinal() * replicate ordinal}. If an assay plate screening fails, it may be necessary to create new assay plates to re-screen * the library {@link Plate}, but these repeated assay plates must belong to new {@link LibraryScreening}. It is valid * for an AssayPlate to have a missing {@link #getLibraryScreening() library screening}, while having a * {@link #getScreenResultDataLoading() data loading activity}. It is not valid for an AssayPlate to exist without * having an association to one of these activities. * * @author <a mailto="andrew_tolopko@hms.harvard.edu">Andrew Tolopko</a> */ @Entity @org.hibernate.annotations.Entity @org.hibernate.annotations.Proxy @Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "libraryScreeningId", "plateNumber", "replicateOrdinal" }) }) @ContainedEntity(containingEntityClass=Screen.class) public class AssayPlate extends AbstractEntity<Integer> implements Comparable<AssayPlate> { private static final long serialVersionUID = 1L; private static final Logger log = Logger.getLogger(AssayWell.class); public static final RelationshipPath<AssayPlate> screen = RelationshipPath.from(AssayPlate.class).to("screen", Cardinality.TO_ONE); public static final RelationshipPath<AssayPlate> plateScreened = RelationshipPath.from(AssayPlate.class).to("plateScreened", Cardinality.TO_ONE); public static final RelationshipPath<AssayPlate> libraryScreening = RelationshipPath.from(AssayPlate.class).to("libraryScreening", Cardinality.TO_ONE); public static final RelationshipPath<AssayPlate> screenResultDataLoading = RelationshipPath.from(AssayPlate.class).to("screenResultDataLoading", Cardinality.TO_ONE); public static final Function<AssayPlate,Plate> ToPlate = new Function<AssayPlate,Plate>() { public Plate apply(AssayPlate ap) { return ap.getPlateScreened(); } }; public static final Function<AssayPlate,Integer> ToPlateNumber = new Function<AssayPlate,Integer>() { public Integer apply(AssayPlate ap) { return ap.getPlateNumber(); } }; public static final Function<AssayPlate,Integer> ToReplicateOrdinal = new Function<AssayPlate,Integer>() { public Integer apply(AssayPlate ap) { return ap.getReplicateOrdinal(); } }; public static final Function<AssayPlate,LibraryScreening> ToLibraryScreening = new Function<AssayPlate,LibraryScreening>() { public LibraryScreening apply(AssayPlate from) { return from.getLibraryScreening(); } }; public static final Function<AssayPlate,AdministrativeActivity> ToScreenResultDataLoading = new Function<AssayPlate,AdministrativeActivity>() { public AdministrativeActivity apply(AssayPlate from) { return from.getScreenResultDataLoading(); } }; public static final Predicate<AssayPlate> IsScreened = new Predicate<AssayPlate>() { public boolean apply(AssayPlate ap) { return ap.getStatus().compareTo(AssayPlateStatus.SCREENED) >= 0; } }; public static final Predicate<AssayPlate> IsDataLoaded = new Predicate<AssayPlate>() { public boolean apply(AssayPlate ap) { return ap.getStatus().compareTo(AssayPlateStatus.DATA_LOADED) >= 0; } }; public static final Predicate<AssayPlate> IsFirstReplicate = new Predicate<AssayPlate>() { public boolean apply(AssayPlate ap) { return ap.getReplicateOrdinal() == 0; } }; /** @motivation {@link #IsScreened} can return true if {@link AssayPlateStatus} > SCREENED even if there is no LibraryScreening */ public static final Predicate<AssayPlate> HasLibraryScreening = new Predicate<AssayPlate>() { public boolean apply(AssayPlate ap) { return ap.getLibraryScreening() != null; } }; private Integer _version; private Screen _screen; private int _plateNumber; private Plate _plateScreened; private int _replicateOrdinal; private LibraryScreening _libraryScreening; private AdministrativeActivity _screenResultDataLoading; private double _zScore; /** @motivation for hibernate */ protected AssayPlate() {} /* should only be called by Screen.createAssayPlate */ public AssayPlate(Screen screen, Plate plateScreened, int replicateOrdinal) { this(screen, plateScreened.getPlateNumber(), replicateOrdinal); setPlateScreened(plateScreened); } /** * @motivation this alternate constructor is necessary for cases where * Screensaver has not been used to track library copies or * library screenings, so that the necessary Plate and/or * AssayPlate entities do not exist. */ public AssayPlate(Screen screen, int plateNumber, int replicateOrdinal) { if (screen == null) { throw new RequiredPropertyException(this, "screen"); } if (plateNumber < 0) { throw new RequiredPropertyException(this, "plateScreened"); } if (replicateOrdinal < 0) { throw new DataModelViolationException("replicateOrdinal must be non-negative"); } _screen = screen; _plateNumber = plateNumber; _replicateOrdinal = replicateOrdinal; } @Override public Object acceptVisitor(AbstractEntityVisitor visitor) { return visitor.visit(this); } @Id @org.hibernate.annotations.GenericGenerator(name="assay_plate_id_seq", strategy="sequence", parameters = { @Parameter(name="sequence", value="assay_plate_id_seq") }) @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="assay_plate_id_seq") public Integer getAssayPlateId() { return getEntityId(); } private void setAssayPlateId(Integer assayPlateId) { setEntityId(assayPlateId); } @ManyToOne(cascade = { CascadeType.MERGE }) @JoinColumn(name="plateId", nullable=true, updatable=false) @org.hibernate.annotations.ForeignKey(name="fk_assay_plate_to_plate") @ToOne(hasNonconventionalSetterMethod = true) public Plate getPlateScreened() { return _plateScreened; } private void setPlateScreened(Plate plateScreened) { _plateScreened = plateScreened; } @Column(nullable=false, updatable=false) public int getPlateNumber() { return _plateNumber; } private void setPlateNumber(int plateNumber) { _plateNumber = plateNumber; } @ManyToOne @JoinColumn(name="libraryScreeningId") @org.hibernate.annotations.ForeignKey(name="fk_assay_plate_to_library_screening") public LibraryScreening getLibraryScreening() { return _libraryScreening; } public void setLibraryScreening(LibraryScreening libraryScreening) { _libraryScreening = libraryScreening; } @ManyToOne @JoinColumn(name="screenResultDataLoadingId") @org.hibernate.annotations.ForeignKey(name="fk_assay_plate_to_screen_result_data_loading") public AdministrativeActivity getScreenResultDataLoading() { return _screenResultDataLoading; } public void setScreenResultDataLoading(AdministrativeActivity screenResultDataLoading) { _screenResultDataLoading = screenResultDataLoading; } @Column(nullable=false, updatable=false) public int getReplicateOrdinal() { return _replicateOrdinal; } private void setReplicateOrdinal(int replicateOrdinal) { _replicateOrdinal = replicateOrdinal; } /** * @motivation for hibernate */ @Column(nullable=false) @Version private Integer getVersion() { return _version; } /** * @motivation for hibernate */ private void setVersion(Integer version) { _version = version; } public int compareTo(AssayPlate other) { int result = Integer.valueOf(getPlateNumber()).compareTo(Integer.valueOf(other.getPlateNumber())); if (result == 0) { result = Integer.valueOf(getReplicateOrdinal()).compareTo(Integer.valueOf(other.getReplicateOrdinal())); } if (result == 0) { if (getLibraryScreening() == null && other.getLibraryScreening() == null) { return 0; } if (getLibraryScreening() != null && other.getLibraryScreening() != null) { return getLibraryScreening().compareTo(other.getLibraryScreening()); } if (getLibraryScreening() == null) { if (other.getLibraryScreening() == null) { return 0; } return 1; } return -1; } return result; } @ManyToOne @JoinColumn(name="screenId", nullable=false, updatable=false) @org.hibernate.annotations.ForeignKey(name="fk_assay_plate_to_screen") public Screen getScreen() { return _screen; } private void setScreen(Screen screen) { _screen = screen; } @Transient public AssayPlateStatus getStatus() { if (getScreenResultDataLoading() != null) { // for now, if data is loaded, we also assume it has been analyzed return AssayPlateStatus.DATA_ANALYZED; } if (getLibraryScreening() != null) { return AssayPlateStatus.SCREENED; } // for now, if AssayPlate exists in the system, we assume it exists in reality and has been plated return AssayPlateStatus.PLATED; } }