// $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.model.users; import java.util.SortedSet; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import org.hibernate.annotations.Immutable; import org.hibernate.annotations.Parameter; import org.hibernate.annotations.Sort; import org.hibernate.annotations.SortType; import org.hibernate.annotations.Type; import org.joda.time.LocalDate; import edu.harvard.med.screensaver.model.AbstractEntityVisitor; import edu.harvard.med.screensaver.model.AuditedAbstractEntity; import edu.harvard.med.screensaver.model.DataModelViolationException; import edu.harvard.med.screensaver.model.activities.AdministrativeActivity; import edu.harvard.med.screensaver.model.annotations.ToMany; import edu.harvard.med.screensaver.model.annotations.ToOne; import edu.harvard.med.screensaver.model.meta.Cardinality; import edu.harvard.med.screensaver.model.meta.RelationshipPath; /** * A ChecklistItemEvent is used to either 1) mark a non-expirable ChecklistItem * as "completed" or 2) mark an expirable ChecklistItem as either "activated" or * "expired". All ChecklistItemEvents have an associated * AdministrativeActivity that records the administrator that entered the event * and the date it was entered. * * @author <a mailto="andrew_tolopko@hms.harvard.edu">Andrew Tolopko</a> * @author <a mailto="john_sullivan@hms.harvard.edu">John Sullivan</a> */ @Entity @Immutable @org.hibernate.annotations.Proxy @edu.harvard.med.screensaver.model.annotations.ContainedEntity(containingEntityClass = ScreeningRoomUser.class) public class ChecklistItemEvent extends AuditedAbstractEntity<Integer> implements Comparable<ChecklistItemEvent> { private static final long serialVersionUID = 0L; public static final RelationshipPath<ChecklistItemEvent> screeningRoomUser = RelationshipPath.from(ChecklistItemEvent.class).to("screeningRoomUser", Cardinality.TO_ONE); public static final RelationshipPath<ChecklistItemEvent> checklistItem = RelationshipPath.from(ChecklistItemEvent.class).to("checklistItem", Cardinality.TO_ONE); private ChecklistItem _checklistItem; private ScreeningRoomUser _screeningRoomUser; private boolean _isExpiration; private boolean _isNotApplicable; private LocalDate _datePerformed; @Override public Object acceptVisitor(AbstractEntityVisitor visitor) { return visitor.visit(this); } /** * Get the id for the checklist item. * * @return the id for the checklist item */ @Id @org.hibernate.annotations.GenericGenerator(name = "checklist_item_event_id_seq", strategy = "sequence", parameters = { @Parameter(name = "sequence", value = "checklist_item_event_id_seq") }) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "checklist_item_event_id_seq") public Integer getChecklistItemEventId() { return getEntityId(); } /** * Get the checklist item. * * @return the checklist item */ @ManyToOne(fetch = FetchType.LAZY, cascade = {}) @JoinColumn(name = "checklistItemId", nullable = false, updatable = false) //@org.hibernate.annotations.Immutable @org.hibernate.annotations.ForeignKey(name = "fk_checklist_item_event_to_checklist_item") @org.hibernate.annotations.LazyToOne(org.hibernate.annotations.LazyToOneOption.PROXY) @org.hibernate.annotations.Cascade({}) @edu.harvard.med.screensaver.model.annotations.ToOne(unidirectional = true) public ChecklistItem getChecklistItem() { return _checklistItem; } // TODO: we never intend to use this, but need it to allow unit tests to pass @ManyToMany(fetch = FetchType.LAZY, cascade={ CascadeType.PERSIST, CascadeType.MERGE }) @JoinTable(name="checklistItemEventUpdateActivity", joinColumns=@JoinColumn(name="checklistItemEventId", nullable=false, updatable=false), inverseJoinColumns=@JoinColumn(name="updateActivityId", nullable=false, updatable=false, unique=true)) @org.hibernate.annotations.Cascade(value={org.hibernate.annotations.CascadeType.SAVE_UPDATE}) @Sort(type=SortType.NATURAL) @ToMany(singularPropertyName="updateActivity", hasNonconventionalMutation=true /* model testing framework doesn't understand this is a containment relationship, and so requires addUpdateActivity() method*/) @Override public SortedSet<AdministrativeActivity> getUpdateActivities() { return _updateActivities; } /** * For checklist items that are {@link ChecklistItem#isExpirable() expirable}, * get whether this checklist item event represents the activation or the * expiration event. */ @Column(nullable = false, updatable = false, name = "isExpiration") public boolean isExpiration() { return _isExpiration; } /** * If set, indicates that this checklist item is "not applicable" */ @Column(nullable = false, updatable = false, name = "isNotApplicable") public boolean isNotApplicable() { return _isNotApplicable; } /** * Get the screening room user. * * @return the screening room user */ @ManyToOne(fetch = FetchType.LAZY, cascade = { /*CascadeType.PERSIST, CascadeType.MERGE*/ }) @JoinColumn(name = "screeningRoomUserId", nullable = false, updatable = false) @org.hibernate.annotations.ForeignKey(name = "fk_checklist_item_event_to_screening_room_user") @org.hibernate.annotations.Cascade({ /*org.hibernate.annotations.CascadeType.SAVE_UPDATE*/ }) @org.hibernate.annotations.LazyToOne(org.hibernate.annotations.LazyToOneOption.PROXY) @ToOne(unidirectional=true) public ScreeningRoomUser getScreeningRoomUser() { return _screeningRoomUser; } /** * @return the date the checklist item was performed by the user or otherwise * enacted */ @Column(updatable=false) @Type(type = "edu.harvard.med.screensaver.db.usertypes.LocalDateType") //@Immutable public LocalDate getDatePerformed() { return _datePerformed; } /** * Set the date of the event. * * @param datePerformed the new date of the event */ private void setDatePerformed(LocalDate datePerformed) { _datePerformed = datePerformed; } /** * Construct an initialized "activation" or "completed" <code>ChecklistItemEvent</code> * <p> * Intended only for use by {@link ScreeningRoomUser}. * * @param checklistItem the checklist item * @param screeningRoomUser the screening room user to which this checklist * item event applies * @param datePerformed the date the checklist item event was performed by the user * or otherwise enacted */ ChecklistItemEvent(ChecklistItem checklistItem, ScreeningRoomUser screeningRoomUser, LocalDate datePerformed, AdministratorUser recordedBy) { super(recordedBy); if (checklistItem == null || screeningRoomUser == null || datePerformed == null || recordedBy == null) { throw new NullPointerException(); } _checklistItem = checklistItem; _screeningRoomUser = screeningRoomUser; _isExpiration = false; _isNotApplicable = false; _datePerformed = datePerformed; } /** * Construct an initialized "not applicable" <code>ChecklistItemEvent</code> * <p> * Intended only for use by {@link ScreeningRoomUser}. **/ ChecklistItemEvent(ChecklistItem checklistItem, ScreeningRoomUser screeningRoomUser, LocalDate datePerformed, AdministratorUser recordedBy, boolean isNotApplicable) { this(checklistItem, screeningRoomUser, datePerformed, recordedBy); _isNotApplicable = isNotApplicable; } public ChecklistItemEvent createChecklistItemExpirationEvent(LocalDate datePerformed, AdministratorUser recordedBy) { if (!getChecklistItem().isExpirable()) { throw new DataModelViolationException("cannot expire checklist item " + getChecklistItem().getItemName()); } SortedSet<ChecklistItemEvent> checklistItemEvents = getScreeningRoomUser().getChecklistItemEvents(getChecklistItem()); if (checklistItemEvents.isEmpty()) { throw new DataModelViolationException("cannot add checklist item expiration when checklist item has not yet been activated"); } ChecklistItemEvent previousEvent = checklistItemEvents.last(); if (!previousEvent.equals(this)) { throw new DataModelViolationException("can only apply expiration to the last checklist event of this type"); } if (previousEvent.isExpiration()) { throw new DataModelViolationException("can only expire a previously active checklist item"); } if (previousEvent.isNotApplicable()) { throw new DataModelViolationException("can not expire a checklist item that has been marked \"not applicable\""); } if (datePerformed.compareTo(previousEvent.getDatePerformed()) < 0) { throw new DataModelViolationException("checklist item expiration date must be on or after the previous activation date"); } ChecklistItemEvent checklistItemExpiration = new ChecklistItemEvent(this.getChecklistItem(), this.getScreeningRoomUser(), datePerformed, recordedBy); checklistItemExpiration._isExpiration = true; this.getScreeningRoomUser().getChecklistItemEvents().add(checklistItemExpiration); return checklistItemExpiration; } /** * Construct an uninitialized <code>ChecklistItem</code> object. * * @motivation for hibernate and proxy/concrete subclass constructors * @motivation for user interface entity creation */ public ChecklistItemEvent() {} // private methods /** * @motivation for hibernate */ private void setChecklistItemEventId(Integer checklistItemEventId) { setEntityId(checklistItemEventId); } /** * @motivation for hibernate */ private void setChecklistItem(ChecklistItem checklistItem) { _checklistItem = checklistItem; } /** * @motivation for hibernate */ private void setScreeningRoomUser(ScreeningRoomUser screeningRoomUser) { _screeningRoomUser = screeningRoomUser; } private void setExpiration(boolean isExpiration) { _isExpiration = isExpiration; } private void setNotApplicable(boolean value) { _isNotApplicable = value; } private static ChecklistItemEventComparator _checklistItemEventComparator = new ChecklistItemEventComparator(); public int compareTo(ChecklistItemEvent other) { return _checklistItemEventComparator.compare(this, other); } }