package edu.ualberta.med.biobank.common.wrappers; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import edu.ualberta.med.biobank.common.exception.BiobankCheckException; import edu.ualberta.med.biobank.common.formatters.DateFormatter; import edu.ualberta.med.biobank.common.peer.CenterPeer; import edu.ualberta.med.biobank.common.peer.CollectionEventPeer; import edu.ualberta.med.biobank.common.peer.DispatchPeer; import edu.ualberta.med.biobank.common.peer.DispatchSpecimenPeer; import edu.ualberta.med.biobank.common.peer.ShipmentInfoPeer; import edu.ualberta.med.biobank.common.peer.SpecimenPeer; import edu.ualberta.med.biobank.common.util.DispatchSpecimenState; import edu.ualberta.med.biobank.common.util.DispatchState; import edu.ualberta.med.biobank.common.wrappers.WrapperTransaction.TaskList; import edu.ualberta.med.biobank.common.wrappers.actions.BiobankSessionAction; import edu.ualberta.med.biobank.common.wrappers.actions.IfAction; import edu.ualberta.med.biobank.common.wrappers.actions.IfAction.Is; import edu.ualberta.med.biobank.common.wrappers.base.DispatchBaseWrapper; import edu.ualberta.med.biobank.common.wrappers.base.DispatchSpecimenBaseWrapper; import edu.ualberta.med.biobank.common.wrappers.checks.NotNullPreCheck; import edu.ualberta.med.biobank.common.wrappers.checks.UniqueCheck; import edu.ualberta.med.biobank.common.wrappers.loggers.DispatchLogProvider; import edu.ualberta.med.biobank.common.wrappers.tasks.NoActionWrapperQueryTask; import edu.ualberta.med.biobank.model.Dispatch; import edu.ualberta.med.biobank.model.DispatchSpecimen; import gov.nih.nci.system.applicationservice.ApplicationException; import gov.nih.nci.system.applicationservice.WritableApplicationService; import gov.nih.nci.system.query.SDKQueryResult; import gov.nih.nci.system.query.hibernate.HQLCriteria; public class DispatchWrapper extends DispatchBaseWrapper { private static final DispatchLogProvider LOG_PROVIDER = new DispatchLogProvider(); private static final Property<String, Dispatch> WAYBILL_PROPERTY = DispatchPeer.SHIPMENT_INFO .to(ShipmentInfoPeer.WAYBILL); private static final Collection<Property<?, ? super Dispatch>> UNIQUE_WAYBILL_PER_SENDER_PROPERTIES = new ArrayList<Property<?, ? super Dispatch>>(); static { UNIQUE_WAYBILL_PER_SENDER_PROPERTIES.add(WAYBILL_PROPERTY); UNIQUE_WAYBILL_PER_SENDER_PROPERTIES.add(DispatchPeer.SENDER_CENTER); } private final Map<DispatchSpecimenState, List<DispatchSpecimenWrapper>> dispatchSpecimenMap = new HashMap<DispatchSpecimenState, List<DispatchSpecimenWrapper>>(); private boolean hasNewSpecimens = false; private boolean hasSpecimenStatesChanged = false; // TODO: Not sure if it's a good idea to maintain a list like this // internally. It can result in unwanted changes being persisted. private List<DispatchSpecimenWrapper> dispatchSpecimensToPersist = new ArrayList<DispatchSpecimenWrapper>(); public DispatchWrapper(WritableApplicationService appService) { super(appService); } public DispatchWrapper(WritableApplicationService appService, Dispatch dispatch) { super(appService, dispatch); } @Override public Dispatch getNewObject() throws Exception { Dispatch newObject = super.getNewObject(); newObject.setState(DispatchState.CREATION.getId()); return newObject; } public String getStateDescription() { DispatchState state = DispatchState .getState(getProperty(DispatchPeer.STATE)); if (state == null) return ""; //$NON-NLS-1$ return state.getLabel(); } public DispatchState getDispatchState() { return DispatchState.getState(getState()); } public String getFormattedPackedAt() { if (getShipmentInfo() != null) return DateFormatter.formatAsDateTime(getShipmentInfo() .getPackedAt()); return null; } public String getFormattedReceivedAt() { if (getShipmentInfo() != null) return DateFormatter.formatAsDateTime(getShipmentInfo() .getReceivedAt()); return null; } public boolean hasErrors() { return !getDispatchSpecimenCollectionWithState( DispatchSpecimenState.MISSING, DispatchSpecimenState.EXTRA) .isEmpty(); } public Map<DispatchSpecimenState, List<DispatchSpecimenWrapper>> getMap() { return dispatchSpecimenMap; } private List<DispatchSpecimenWrapper> getDispatchSpecimenCollectionWithState( DispatchSpecimenState... states) { return getDispatchSpecimenCollectionWithState(dispatchSpecimenMap, getDispatchSpecimenCollection(false), states); } private List<DispatchSpecimenWrapper> getDispatchSpecimenCollectionWithState( Map<DispatchSpecimenState, List<DispatchSpecimenWrapper>> map, List<DispatchSpecimenWrapper> list, DispatchSpecimenState... states) { if (map.isEmpty()) { for (DispatchSpecimenState state : DispatchSpecimenState.values()) { map.put(state, new ArrayList<DispatchSpecimenWrapper>()); } for (DispatchSpecimenWrapper wrapper : list) { map.get(wrapper.getSpecimenState()).add(wrapper); } } if (states.length == 1) { return map.get(states[0]); } List<DispatchSpecimenWrapper> tmp = new ArrayList<DispatchSpecimenWrapper>(); for (DispatchSpecimenState state : states) { tmp.addAll(map.get(state)); } return tmp; } public List<SpecimenWrapper> getSpecimenCollection(boolean sort) { List<SpecimenWrapper> list = new ArrayList<SpecimenWrapper>(); for (DispatchSpecimenWrapper da : getDispatchSpecimenCollection(false)) { list.add(da.getSpecimen()); } if (sort) { Collections.sort(list); } return list; } public void addSpecimens(List<SpecimenWrapper> newSpecimens, DispatchSpecimenState state) throws BiobankCheckException { if (newSpecimens == null) return; // already added dsa List<DispatchSpecimenWrapper> currentDaList = getDispatchSpecimenCollection(false); List<DispatchSpecimenWrapper> newDispatchSpecimens = new ArrayList<DispatchSpecimenWrapper>(); List<SpecimenWrapper> currentSpecimenList = new ArrayList<SpecimenWrapper>(); for (DispatchSpecimenWrapper dsa : currentDaList) { currentSpecimenList.add(dsa.getSpecimen()); } // new specimens added for (SpecimenWrapper specimen : newSpecimens) { if (specimen.getCurrentCenter().equals(getSenderCenter()) || isInReceivedState()) { // in received state, let any specimen to be added just in case // it received something wrong if (!currentSpecimenList.contains(specimen)) { DispatchSpecimenWrapper dsa = new DispatchSpecimenWrapper( appService); dsa.setSpecimen(specimen); dsa.setDispatch(this); dsa.setDispatchSpecimenState(state); if (state == DispatchSpecimenState.EXTRA) { specimen.setCurrentCenter(getReceiverCenter()); // remove position in case it has one in the previous // center. specimen.setParent(null, null); dispatchSpecimensToPersist.add(dsa); } newDispatchSpecimens.add(dsa); hasNewSpecimens = true; } } else throw new BiobankCheckException( MessageFormat.format( Messages .getString("DispatchWrapper.specimen.add.sender.error.msg"), //$NON-NLS-1$ specimen.getInventoryId())); } addToDispatchSpecimenCollection(newDispatchSpecimens); resetMap(); } @Override public void removeFromDispatchSpecimenCollection( List<? extends DispatchSpecimenBaseWrapper> dasToRemove) { super.removeFromDispatchSpecimenCollection(dasToRemove); resetMap(); } public void removeSpecimens(List<SpecimenWrapper> spcs) { if (spcs == null) { throw new NullPointerException(); } if (spcs.isEmpty()) return; List<DispatchSpecimenWrapper> removeDispatchSpecimens = new ArrayList<DispatchSpecimenWrapper>(); for (DispatchSpecimenWrapper dsa : getDispatchSpecimenCollection(false)) { if (spcs.contains(dsa.getSpecimen())) { removeDispatchSpecimens.add(dsa); } } removeFromDispatchSpecimenCollection(removeDispatchSpecimens); } public void removeDispatchSpecimens(List<DispatchSpecimenWrapper> dsaList) { if (dsaList == null) { throw new NullPointerException(); } if (dsaList.isEmpty()) return; List<DispatchSpecimenWrapper> currentDaList = getDispatchSpecimenCollection(false); List<DispatchSpecimenWrapper> removeDispatchSpecimens = new ArrayList<DispatchSpecimenWrapper>(); for (DispatchSpecimenWrapper dsa : currentDaList) { if (dsaList.contains(dsa)) { removeDispatchSpecimens.add(dsa); } } removeFromDispatchSpecimenCollection(removeDispatchSpecimens); } // TODO: IMHO methods like this shouldn't even exist in the wrapper. It // should be a static method in some DispatchUtilFunctions class that keeps // track of what was added, then persists them immediately. Having all this // tracking done in the wrapper just creates potential problems if several // methods that do tracking are called without persisting after - JMF public void receiveSpecimens(List<SpecimenWrapper> specimensToReceive) { List<DispatchSpecimenWrapper> nonProcessedSpecimens = getDispatchSpecimenCollectionWithState(DispatchSpecimenState.NONE); for (DispatchSpecimenWrapper ds : nonProcessedSpecimens) { if (specimensToReceive.contains(ds.getSpecimen())) { hasSpecimenStatesChanged = true; ds.setDispatchSpecimenState(DispatchSpecimenState.RECEIVED); ds.getSpecimen().setCurrentCenter(getReceiverCenter()); dispatchSpecimensToPersist.add(ds); } } resetMap(); } public boolean isInCreationState() { return getDispatchState() == null || DispatchState.CREATION.equals(getDispatchState()); } public boolean isInTransitState() { return DispatchState.IN_TRANSIT.equals(getDispatchState()); } public boolean isInReceivedState() { return DispatchState.RECEIVED.equals(getDispatchState()); } public boolean hasBeenReceived() { return EnumSet.of(DispatchState.RECEIVED, DispatchState.CLOSED) .contains(getDispatchState()); } public boolean isInClosedState() { return DispatchState.CLOSED.equals(getDispatchState()); } public boolean isInLostState() { return DispatchState.LOST.equals(getDispatchState()); } public void setState(DispatchState ds) { setState(ds.getId()); } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append(getSenderCenter() == null ? "" : getSenderCenter() //$NON-NLS-1$ .getNameShort() + "/"); //$NON-NLS-1$ sb.append(getReceiverCenter() == null ? "" : getReceiverCenter() //$NON-NLS-1$ .getNameShort() + "/"); //$NON-NLS-1$ sb.append(getShipmentInfo() == null ? "" : getShipmentInfo() //$NON-NLS-1$ .getFormattedDateReceived()); return sb.toString(); } public boolean canBeSentBy(UserWrapper user) { return canUpdate(user, user.getCurrentWorkingCenter(), null) && getSenderCenter().equals(user.getCurrentWorkingCenter()) && isInCreationState() && hasDispatchSpecimens(); } public boolean hasDispatchSpecimens() { return getSpecimenCollection(false) != null && !getSpecimenCollection(false).isEmpty(); } public boolean canBeReceivedBy(UserWrapper user) { return canUpdate(user, user.getCurrentWorkingCenter(), null) && getReceiverCenter().equals(user.getCurrentWorkingCenter()) && isInTransitState(); } public DispatchSpecimenWrapper getDispatchSpecimen(String inventoryId) { for (DispatchSpecimenWrapper dsa : getDispatchSpecimenCollection(false)) { if (dsa.getSpecimen().getInventoryId().equals(inventoryId)) return dsa; } return null; } public List<DispatchSpecimenWrapper> getNonProcessedDispatchSpecimenCollection() { return getDispatchSpecimenCollectionWithState(DispatchSpecimenState.NONE); } public List<DispatchSpecimenWrapper> getExtraDispatchSpecimens() { return getDispatchSpecimenCollectionWithState(DispatchSpecimenState.EXTRA); } public List<DispatchSpecimenWrapper> getMissingDispatchSpecimens() { return getDispatchSpecimenCollectionWithState(DispatchSpecimenState.MISSING); } public List<DispatchSpecimenWrapper> getReceivedDispatchSpecimens() { return getDispatchSpecimenCollectionWithState(DispatchSpecimenState.RECEIVED); } @SuppressWarnings("unused") private static final String FAST_DISPATCH_SPECIMEN_QRY = "select ra from " //$NON-NLS-1$ + DispatchSpecimen.class.getName() + " ra inner join fetch ra." //$NON-NLS-1$ + DispatchSpecimenPeer.SPECIMEN.getName() + " as spec inner join fetch spec." //$NON-NLS-1$ + SpecimenPeer.SPECIMEN_TYPE.getName() + " inner join fetch spec." //$NON-NLS-1$ + SpecimenPeer.COLLECTION_EVENT.getName() + " as cevent inner join fetch cevent." //$NON-NLS-1$ + CollectionEventPeer.PATIENT.getName() + " inner join fetch spec." //$NON-NLS-1$ + SpecimenPeer.ACTIVITY_STATUS.getName() + " where ra." //$NON-NLS-1$ + Property.concatNames(DispatchSpecimenPeer.DISPATCH, DispatchPeer.ID) + " = ?"; //$NON-NLS-1$ public boolean canBeClosedBy(UserWrapper user) { return isInReceivedState() && canUpdate(user, user.getCurrentWorkingCenter(), null); } @Override protected void resetInternalFields() { resetMap(); hasNewSpecimens = false; hasSpecimenStatesChanged = false; dispatchSpecimensToPersist.clear(); } public void resetMap() { dispatchSpecimenMap.clear(); } @Override public DispatchLogProvider getLogProvider() { return LOG_PROVIDER; } private static final String DISPATCH_HQL_STRING = "from " //$NON-NLS-1$ + Dispatch.class.getName() + " as d inner join fetch d." //$NON-NLS-1$ + DispatchPeer.SHIPMENT_INFO.getName() + " as s "; //$NON-NLS-1$ /** * Search for shipments in the site with the given waybill */ public static List<DispatchWrapper> getDispatchesByWaybill( WritableApplicationService appService, String waybill) throws ApplicationException { StringBuilder qry = new StringBuilder(DISPATCH_HQL_STRING + " where s." //$NON-NLS-1$ + ShipmentInfoPeer.WAYBILL.getName() + " = ?"); //$NON-NLS-1$ HQLCriteria criteria = new HQLCriteria(qry.toString(), Arrays.asList(new Object[] { waybill })); List<Dispatch> origins = appService.query(criteria); List<DispatchWrapper> shipments = ModelWrapper.wrapModelCollection( appService, origins, DispatchWrapper.class); return shipments; } private static final String DISPATCHES_BY_DATE_RECEIVED_QRY = DISPATCH_HQL_STRING + " where s." //$NON-NLS-1$ + ShipmentInfoPeer.RECEIVED_AT.getName() + " >=? and s." //$NON-NLS-1$ + ShipmentInfoPeer.RECEIVED_AT.getName() + " <? and (d." //$NON-NLS-1$ + Property.concatNames(DispatchPeer.RECEIVER_CENTER, CenterPeer.ID) + "= ? or d." //$NON-NLS-1$ + Property.concatNames(DispatchPeer.SENDER_CENTER, CenterPeer.ID) + " = ?)"; //$NON-NLS-1$ /** * Search for shipments in the site with the given date received. Don't use * hour and minute. */ public static List<DispatchWrapper> getDispatchesByDateReceived( WritableApplicationService appService, Date dateReceived, CenterWrapper<?> center) throws ApplicationException { Integer centerId = center.getId(); HQLCriteria criteria = new HQLCriteria( DISPATCHES_BY_DATE_RECEIVED_QRY.toString(), Arrays.asList(new Object[] { startOfDay(dateReceived), endOfDay(dateReceived), centerId, centerId })); List<Dispatch> origins = appService.query(criteria); List<DispatchWrapper> shipments = ModelWrapper.wrapModelCollection( appService, origins, DispatchWrapper.class); return shipments; } private static final String DISPATCHED_BY_DATE_SENT_QRY = DISPATCH_HQL_STRING + " where s." //$NON-NLS-1$ + ShipmentInfoPeer.PACKED_AT.getName() + " >= ? and s." //$NON-NLS-1$ + ShipmentInfoPeer.PACKED_AT.getName() + " < ? and (d." //$NON-NLS-1$ + Property.concatNames(DispatchPeer.RECEIVER_CENTER, CenterPeer.ID) + "= ? or d." //$NON-NLS-1$ + Property.concatNames(DispatchPeer.SENDER_CENTER, CenterPeer.ID) + " = ?)"; //$NON-NLS-1$ public static List<DispatchWrapper> getDispatchesByDateSent( WritableApplicationService appService, Date dateSent, CenterWrapper<?> center) throws ApplicationException { Integer centerId = center.getId(); HQLCriteria criteria = new HQLCriteria(DISPATCHED_BY_DATE_SENT_QRY, Arrays.asList(new Object[] { startOfDay(dateSent), endOfDay(dateSent), centerId, centerId })); List<Dispatch> origins = appService.query(criteria); List<DispatchWrapper> shipments = ModelWrapper.wrapModelCollection( appService, origins, DispatchWrapper.class); return shipments; } @Override public List<? extends CenterWrapper<?>> getSecuritySpecificCenters() { List<CenterWrapper<?>> centers = new ArrayList<CenterWrapper<?>>(); if (getSenderCenter() != null) centers.add(getSenderCenter()); if (getReceiverCenter() != null) centers.add(getReceiverCenter()); return centers; } public boolean hasNewSpecimens() { return hasNewSpecimens; } @Deprecated @Override protected void addPersistTasks(TaskList tasks) { tasks.add(check().notNull(DispatchPeer.SENDER_CENTER)); tasks.add(check().notNull(DispatchPeer.RECEIVER_CENTER)); tasks.add(new NotNullPreCheck<Dispatch>(this, DispatchPeer.SENDER_CENTER)); tasks.deleteRemoved(this, DispatchPeer.DISPATCH_SPECIMENS); removeSpecimensFromParents(tasks); persistSpecimens(tasks); super.addPersistTasks(tasks); tasks.persistAdded(this, DispatchPeer.DISPATCH_SPECIMENS); BiobankSessionAction checkWaybill = new UniqueCheck<Dispatch>(this, UNIQUE_WAYBILL_PER_SENDER_PROPERTIES); tasks.add(new IfAction<Dispatch>(this, WAYBILL_PROPERTY, Is.NOT_NULL, checkWaybill)); tasks.add(new ResetInternalStateQueryTask(this)); } @Deprecated private void persistSpecimens(TaskList tasks) { for (DispatchSpecimenWrapper dispatchSpecimen : dispatchSpecimensToPersist) { SpecimenWrapper specimen = dispatchSpecimen.getSpecimen(); specimen.addPersistTasks(tasks); } } @Deprecated private void removeSpecimensFromParents(TaskList tasks) { if (DispatchState.IN_TRANSIT.equals(getDispatchState())) { Collection<DispatchSpecimenWrapper> dispatchSpecimens = getDispatchSpecimenCollection(false); for (DispatchSpecimenWrapper dispatchSpecimen : dispatchSpecimens) { SpecimenWrapper specimen = dispatchSpecimen.getSpecimen(); specimen.setSpecimenPosition(null); specimen.addPersistTasks(tasks); } } } public boolean hasSpecimenStatesChanged() { return hasSpecimenStatesChanged; } private static class ResetInternalStateQueryTask extends NoActionWrapperQueryTask<DispatchWrapper> { public ResetInternalStateQueryTask(DispatchWrapper dispatch) { super(dispatch); } @Override public void afterExecute(SDKQueryResult result) { getWrapper().hasNewSpecimens = false; getWrapper().hasSpecimenStatesChanged = false; getWrapper().dispatchSpecimensToPersist.clear(); } } public void reloadDispatchSpecimens() throws Exception { for (DispatchSpecimenWrapper ds : getDispatchSpecimenCollection(false)) { ds.reload(); } resetMap(); dispatchSpecimensToPersist.clear(); } }