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.EnumSet; import java.util.List; import edu.ualberta.med.biobank.common.exception.BiobankCheckException; import edu.ualberta.med.biobank.common.exception.BiobankException; import edu.ualberta.med.biobank.common.formatters.DateFormatter; import edu.ualberta.med.biobank.common.peer.CenterPeer; import edu.ualberta.med.biobank.common.peer.ContainerPeer; import edu.ualberta.med.biobank.common.peer.ContainerTypePeer; import edu.ualberta.med.biobank.common.peer.SpecimenPeer; import edu.ualberta.med.biobank.common.peer.SpecimenPositionPeer; import edu.ualberta.med.biobank.common.peer.SpecimenTypePeer; import edu.ualberta.med.biobank.common.util.DispatchSpecimenState; import edu.ualberta.med.biobank.common.util.DispatchState; import edu.ualberta.med.biobank.common.util.RowColPos; 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.Is; import edu.ualberta.med.biobank.common.wrappers.actions.UpdateChildrensTopSpecimenAction; import edu.ualberta.med.biobank.common.wrappers.base.SpecimenBaseWrapper; import edu.ualberta.med.biobank.common.wrappers.checks.SpecimenPostPersistChecks; import edu.ualberta.med.biobank.common.wrappers.internal.SpecimenPositionWrapper; import edu.ualberta.med.biobank.common.wrappers.loggers.SpecimenLogProvider; import edu.ualberta.med.biobank.common.wrappers.tasks.NoActionWrapperQueryTask; import edu.ualberta.med.biobank.common.wrappers.util.LazyMessage; import edu.ualberta.med.biobank.common.wrappers.util.LazyMessage.LazyArg; import edu.ualberta.med.biobank.model.ActivityStatus; import edu.ualberta.med.biobank.model.Specimen; import edu.ualberta.med.biobank.model.SpecimenType; 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 SpecimenWrapper extends SpecimenBaseWrapper { private static final String BAD_SAMPLE_TYPE_MSG = Messages .getString("SpecimenWrapper.bad.specimen.type.msg"); //$NON-NLS-1$ private static final String DISPATCHS_CACHE_KEY = "dispatchs"; //$NON-NLS-1$ private static final SpecimenLogProvider LOG_PROVIDER = new SpecimenLogProvider(); private boolean topSpecimenChanged = false; public SpecimenWrapper(WritableApplicationService appService, Specimen wrappedObject) { super(appService, wrappedObject); } public SpecimenWrapper(WritableApplicationService appService) { super(appService); } @Override protected Specimen getNewObject() throws Exception { Specimen newObject = super.getNewObject(); // by default, any newly created Specimen will have a null parent, so // its top is itself. newObject.setTopSpecimen(newObject); return newObject; } public void checkInventoryIdUnique() throws BiobankException, ApplicationException { checkNoDuplicates(Specimen.class, SpecimenPeer.INVENTORY_ID.getName(), getInventoryId(), Messages.getString("SpecimenWrapper.spec.with.id.text")); //$NON-NLS-1$ } public String getFormattedCreatedAt() { return DateFormatter.formatAsDateTime(getCreatedAt()); } public ContainerWrapper getParentContainer() { SpecimenPositionWrapper pos = getSpecimenPosition(); return pos == null ? null : pos.getParent(); } public void setParent(ContainerWrapper container, RowColPos position) { if (container == null) { setSpecimenPosition(null); } else { getOrCreatePosition().setParent(container, position); } } public ContainerWrapper getTop() { ContainerWrapper top = getParentContainer(); if (top != null) { top = top.getTopContainer(); } return top; } public boolean hasParent() { return getParentContainer() != null; } public RowColPos getPosition() { SpecimenPositionWrapper pos = getSpecimenPosition(); return pos == null ? null : pos.getPosition(); } private SpecimenPositionWrapper getOrCreatePosition() { SpecimenPositionWrapper specimenPosition = getSpecimenPosition(); if (specimenPosition == null) { specimenPosition = new SpecimenPositionWrapper(appService); setSpecimenPosition(specimenPosition); } return specimenPosition; } public String getPositionString() { return getPositionString(true, true); } public String getCenterString() { CenterWrapper<?> center = getCurrentCenter(); if (center != null) { return center.getNameShort(); } // TODO should never see that ? should never retrieve a Specimen which // site cannot be displayed ? return "CANNOT DISPLAY INFORMATION"; //$NON-NLS-1$ } @Override public SpecimenLogProvider getLogProvider() { return LOG_PROVIDER; } /** * Set the position in the given container using the positionString */ public void setParentFromPositionString(String positionString, ContainerWrapper parentContainer) throws Exception { RowColPos rcp = parentContainer.getContainerType() .getRowColFromPositionString( positionString.replaceFirst(parentContainer.getLabel(), "")); //$NON-NLS-1$ if ((rcp.getRow() > -1) && (rcp.getCol() > -1)) { setParent(parentContainer, rcp); } else { throw new Exception( MessageFormat.format( Messages .getString("SpecimenWrapper.position.not.valid.msg"), positionString)); //$NON-NLS-1$ } } public String getPositionString(boolean fullString, boolean addTopParentShortName) { RowColPos position = getPosition(); if (position == null) { return null; } if (!fullString) { return getPositionStringInParent(position, getParentContainer()); } ContainerWrapper directParent = getParentContainer(); // ContainerPathWrapper path = directParent.getContainerPath(); String nameShort = directParent.getTopContainer().getContainerType() .getNameShort(); if (addTopParentShortName && nameShort != null) return directParent.getLabel() + getPositionStringInParent(position, directParent) + " (" //$NON-NLS-1$ + nameShort + ")"; //$NON-NLS-1$ return directParent.getLabel() + getPositionStringInParent(position, directParent); } private String getPositionStringInParent(RowColPos position, ContainerWrapper parent) { if (parent != null) { return parent.getContainerType().getPositionString(position); } return null; } public void setQuantityFromType() { if (getSpecimenType() == null) return; CollectionEventWrapper cevent = getCollectionEvent(); StudyWrapper study = cevent.getPatient().getStudy(); Collection<AliquotedSpecimenWrapper> aliquotedSpecimenCollection = study .getAliquotedSpecimenCollection(false); for (AliquotedSpecimenWrapper as : aliquotedSpecimenCollection) { if (getSpecimenType().equals(as.getSpecimenType())) { setQuantity(as.getVolume()); return; } } } private static final String Specimen_QRY = "from " //$NON-NLS-1$ + Specimen.class.getName() + " where " //$NON-NLS-1$ + SpecimenPeer.INVENTORY_ID.getName() + " = ?"; //$NON-NLS-1$ /** * search in all Specimens list. No matter which site added it. */ public static SpecimenWrapper getSpecimen( WritableApplicationService appService, String inventoryId) throws ApplicationException, BiobankCheckException { HQLCriteria criteria = new HQLCriteria(Specimen_QRY, Arrays.asList(new Object[] { inventoryId })); List<Specimen> specimens = appService.query(criteria); if (specimens == null || specimens.size() == 0) return null; if (specimens.size() == 1) return new SpecimenWrapper(appService, specimens.get(0)); throw new BiobankCheckException("Error retrieving specimens: found " //$NON-NLS-1$ + specimens.size() + " results."); //$NON-NLS-1$ } private static final String SPECIMENS_NON_ACTIVE_QRY = "from " //$NON-NLS-1$ + Specimen.class.getName() + " spec where spec." //$NON-NLS-1$ + Property.concatNames(SpecimenPeer.CURRENT_CENTER, CenterPeer.ID) + " = ? and activityStatus != ?"; //$NON-NLS-1$ public static List<SpecimenWrapper> getSpecimensNonActiveInCenter( WritableApplicationService appService, CenterWrapper<?> center) throws ApplicationException { HQLCriteria criteria = new HQLCriteria(SPECIMENS_NON_ACTIVE_QRY, Arrays.asList(new Object[] { center.getId(), ActivityStatus.ACTIVE })); List<Specimen> Specimens = appService.query(criteria); List<SpecimenWrapper> list = new ArrayList<SpecimenWrapper>(); for (Specimen Specimen : Specimens) { list.add(new SpecimenWrapper(appService, Specimen)); } return list; } public static List<SpecimenWrapper> getSpecimensInSiteWithPositionLabel( WritableApplicationService appService, SiteWrapper site, String positionString) throws ApplicationException, BiobankException { List<ContainerWrapper> possibleContainers = ContainerWrapper .getPossibleParents(appService, positionString, site, null); List<SpecimenWrapper> Specimens = new ArrayList<SpecimenWrapper>(); for (ContainerWrapper container : possibleContainers) { RowColPos rcp = null; try { rcp = container.getContainerType().getRowColFromPositionString( positionString.replaceFirst(container.getLabel(), "")); //$NON-NLS-1$ } catch (Exception e) { // Should never happen: it has been already tested in // getPossibleParentsMethod assert false; } if (rcp != null) { if ((rcp.getRow() > -1) && (rcp.getCol() > -1)) { SpecimenWrapper Specimen = container.getSpecimen( rcp.getRow(), rcp.getCol()); if (Specimen != null) { Specimens.add(Specimen); } } } } return Specimens; } @Override public int compareTo(ModelWrapper<Specimen> o) { if (o instanceof SpecimenWrapper) { String s1 = getPositionString(true, true); String s2 = ((SpecimenWrapper) o).getPositionString(true, true); if (s1 == null || s2 == null) return getInventoryId().compareTo( ((SpecimenWrapper) o).getInventoryId()); return s1.compareTo(s2); } return 0; } @Override public String toString() { return getInventoryId(); } @SuppressWarnings("unchecked") public List<DispatchWrapper> getDispatches() { List<DispatchWrapper> dispatchs = (List<DispatchWrapper>) cache .get(DISPATCHS_CACHE_KEY); if (dispatchs == null) { List<DispatchSpecimenWrapper> dsaList = getDispatchSpecimenCollection(); if (dsaList != null) { dispatchs = new ArrayList<DispatchWrapper>(); for (DispatchSpecimenWrapper dsa : dsaList) { dispatchs.add(dsa.getDispatch()); } cache.put(DISPATCHS_CACHE_KEY, dispatchs); } } return dispatchs; } public boolean isActive() { ActivityStatus status = getActivityStatus(); return ActivityStatus.ACTIVE == status; } public boolean isFlagged() { ActivityStatus status = getActivityStatus(); return ActivityStatus.FLAGGED == status; } public List<DispatchSpecimenWrapper> getDispatchSpecimenCollection() { return getWrapperCollection(SpecimenPeer.DISPATCH_SPECIMENS, DispatchSpecimenWrapper.class, false); } public boolean isUsedInDispatch() { return isUsedInDispatch(null); } public boolean isUsedInDispatch(DispatchWrapper excludedShipment) { List<DispatchSpecimenWrapper> dsas = getDispatchSpecimenCollection(); if (dsas != null) for (DispatchSpecimenWrapper dsa : dsas) { DispatchWrapper dispatch = dsa.getDispatch(); if (!dispatch.equals(excludedShipment) && (EnumSet.of(DispatchState.CREATION, DispatchState.IN_TRANSIT, DispatchState.RECEIVED) .contains(dispatch.getDispatchState()))) { if (DispatchSpecimenState.MISSING.equals(dsa .getSpecimenState())) { return false; } return true; } } return false; } @Override public void setParentSpecimen(SpecimenBaseWrapper specimen) { super.setParentSpecimen(specimen); // keep the top Specimen up-to-date whenever the parent is set; however, // only update this top Specimen, not the children's top Specimen. This // is so that the children/ descendants will only be updated if the // Specimen whose parent Specimen was changed is persisted. SpecimenWrapper topSpecimen = specimen == null ? this : specimen .getTopSpecimen(); setTopSpecimenInternal(topSpecimen, true); } /** * Call {@code setParentSpecimen(SpecimenWrapper parent)} instead of this * method to change the top {@code Specimen}. The top {@code Specimen} will * be automatically updated. */ @Override @Deprecated public void setTopSpecimen(SpecimenBaseWrapper specimen) { throw new UnsupportedOperationException( "Not allowed to directly set the top Specimen. Set the parent Specimen instead."); //$NON-NLS-1$ } protected void setTopSpecimenInternal(SpecimenWrapper specimen, boolean checkDatabase) { super.setTopSpecimen(specimen); if (equals(specimen)) { setOriginalCollectionEvent(getCollectionEvent()); } // this is overly cautious, assuming that whenever the top Specimen is // set that it is changed. Could be improved to check if the value has // actually changed, but would probably require lazy loading. if (!isNew() && checkDatabase) { // TODO: actually check the database. Get the current // topSpecimen through an HQL query and compare it against the // one set. topSpecimenChanged = true; // TODO: may want to set to false if set back to the original? } else { topSpecimenChanged = true; } } /** * Return the top {@code Specimen} of the top loaded {@code Specimen}. This * will give the correct "in memory" answer of who the top {@code Specimen} * is (whereas super.getTopSpecimen() will give the value from the * underlying model). */ @Override public SpecimenWrapper getTopSpecimen() { // if parent is cached, return their top specimen, otherwise get and // return mine (from super). if (isPropertyCached(SpecimenPeer.PARENT_SPECIMEN) && getParentSpecimen() != null) { return getParentSpecimen().getTopSpecimen(); } return super.getTopSpecimen(); } private void addTasksToPostCheckLegalSampleType(TaskList tasks) { LazyArg containerLabel = LazyMessage.newArg(this, SpecimenPeer.SPECIMEN_POSITION.to(SpecimenPositionPeer.CONTAINER .to(ContainerPeer.LABEL))); LazyArg specimenType = LazyMessage.newArg(this, SpecimenPeer.SPECIMEN_TYPE.to(SpecimenTypePeer.NAME_SHORT)); LazyMessage badSampleTypeMsg = new LazyMessage(BAD_SAMPLE_TYPE_MSG, containerLabel, specimenType); Property<Collection<SpecimenType>, Specimen> pathToLegalSpecimenTypeOptions = SpecimenPeer.SPECIMEN_POSITION .to(SpecimenPositionPeer.CONTAINER .to(ContainerPeer.CONTAINER_TYPE .to(ContainerTypePeer.SPECIMEN_TYPES))); BiobankSessionAction checkLegalSampleType = check().legalOption( pathToLegalSpecimenTypeOptions, SpecimenPeer.SPECIMEN_TYPE, badSampleTypeMsg); tasks.add(check().ifProperty( SpecimenPeer.SPECIMEN_POSITION.to(SpecimenPositionPeer.ID), Is.NOT_NULL, checkLegalSampleType)); } private void addTasksToUpdateChildren(TaskList tasks) { if (topSpecimenChanged) { SpecimenWrapper topSpecimen = getTopSpecimen(); if (isPropertyCached(SpecimenPeer.CHILD_SPECIMENS)) { // if the children have already been loaded, then update their // top specimen so that they update their children, etc. so that // the entire subtree is consistent. List<SpecimenWrapper> children = getChildSpecimenCollection(false); for (SpecimenWrapper child : children) { child.setTopSpecimenInternal(topSpecimen, false); // Save children whether they're are new or not, because the // children's children could be already persistent and need // to be updated (but would then need their parent to be // persisted first). child.addPersistTasks(tasks); } } else { // Use HQL to update all descendants of this Specimen because // they are not loaded and loading them would be unnecessary. tasks.add(new UpdateChildrensTopSpecimenAction(this)); } tasks.add(new ResetTopSpecimenChangedQueryTask(this)); } } @Deprecated @Override protected void addPersistTasks(TaskList tasks) { tasks.add(check().notNull(SpecimenPeer.SPECIMEN_TYPE)); tasks.add(check().notNull(SpecimenPeer.INVENTORY_ID)); tasks.add(check().unique(SpecimenPeer.INVENTORY_ID)); tasks.deleteRemovedValue(this, SpecimenPeer.SPECIMEN_POSITION); super.addPersistTasks(tasks); tasks.persist(this, SpecimenPeer.SPECIMEN_POSITION); addTasksToPostCheckLegalSampleType(tasks); addTasksToUpdateChildren(tasks); tasks.add(new SpecimenPostPersistChecks(this)); } @Deprecated @Override protected void addDeleteTasks(TaskList tasks) { tasks.add(check().empty(SpecimenPeer.CHILD_SPECIMENS)); // Either Hibernate must delete this object (via the defined cascade) or // do it here, but not both. If both are done, then a // StaleStateException is thrown because an attempt is made to delete an // already deleted object. // tasks.delete(this, SpecimenPeer.SPECIMEN_POSITION); super.addDeleteTasks(tasks); } @Override protected void resetInternalFields() { topSpecimenChanged = false; } private static class ResetTopSpecimenChangedQueryTask extends NoActionWrapperQueryTask<SpecimenWrapper> { public ResetTopSpecimenChangedQueryTask(SpecimenWrapper specimen) { super(specimen); } @Override public void afterExecute(SDKQueryResult result) { getWrapper().topSpecimenChanged = false; } } /** * return a string with collection date (different from created at if it is * an aliquoted specimen) + the collection center */ public String getCollectionInfo() { return getTopSpecimen().getFormattedCreatedAt() + " in " //$NON-NLS-1$ + getTopSpecimen().getOriginInfo().getCenter().getNameShort() + " (visit #" + getCollectionEvent().getVisitNumber() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ } public boolean hasUnknownImportType() { return getSpecimenType() != null && getSpecimenType().isUnknownImport(); } /** * return true if the user can edit this object */ }