package edu.ualberta.med.biobank.common.action.collectionEvent; import java.math.BigDecimal; import java.text.MessageFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import edu.ualberta.med.biobank.common.action.Action; import edu.ualberta.med.biobank.common.action.ActionContext; import edu.ualberta.med.biobank.common.action.ActionResult; import edu.ualberta.med.biobank.common.action.IdResult; import edu.ualberta.med.biobank.common.action.comment.CommentUtil; import edu.ualberta.med.biobank.common.action.exception.ActionCheckException; import edu.ualberta.med.biobank.common.action.exception.ActionException; import edu.ualberta.med.biobank.common.action.study.StudyEventAttrInfo; import edu.ualberta.med.biobank.common.action.study.StudyGetEventAttrInfoAction; import edu.ualberta.med.biobank.common.formatters.DateFormatter; import edu.ualberta.med.biobank.common.permission.Permission; import edu.ualberta.med.biobank.common.permission.collectionEvent.CollectionEventCreatePermission; import edu.ualberta.med.biobank.common.permission.collectionEvent.CollectionEventUpdatePermission; import edu.ualberta.med.biobank.common.util.SetDifference; import edu.ualberta.med.biobank.common.wrappers.EventAttrTypeEnum; import edu.ualberta.med.biobank.model.ActivityStatus; import edu.ualberta.med.biobank.model.Center; import edu.ualberta.med.biobank.model.CollectionEvent; import edu.ualberta.med.biobank.model.Comment; import edu.ualberta.med.biobank.model.EventAttr; import edu.ualberta.med.biobank.model.OriginInfo; import edu.ualberta.med.biobank.model.Patient; import edu.ualberta.med.biobank.model.Specimen; import edu.ualberta.med.biobank.model.SpecimenType; import edu.ualberta.med.biobank.model.Study; import edu.ualberta.med.biobank.model.StudyEventAttr; public class CollectionEventSaveAction implements Action<IdResult> { private static final long serialVersionUID = 1L; private Integer ceventId; private Integer patientId; private Integer visitNumber; private ActivityStatus activityStatus; private String commentText; public static class SaveCEventSpecimenInfo implements ActionResult { private static final long serialVersionUID = 1L; public Integer id; public String inventoryId; public Date createdAt; public ActivityStatus activityStatus; public Integer specimenTypeId; public Integer centerId; public List<String> comments = new ArrayList<String>(); public BigDecimal quantity; } public static class CEventAttrSaveInfo implements ActionResult { private static final long serialVersionUID = 1L; public Integer studyEventAttrId; public EventAttrTypeEnum type; public String value; } private Collection<SaveCEventSpecimenInfo> sourceSpecimenInfos; private List<CEventAttrSaveInfo> ceAttrList; public CollectionEventSaveAction(Integer ceventId, Integer patientId, Integer visitNumber, ActivityStatus activityStatus, String commentText, Collection<SaveCEventSpecimenInfo> sourceSpecs, List<CEventAttrSaveInfo> ceAttrList) { this.ceventId = ceventId; this.patientId = patientId; this.visitNumber = visitNumber; this.activityStatus = activityStatus; this.commentText = commentText; this.sourceSpecimenInfos = sourceSpecs; this.ceAttrList = ceAttrList; } public void setCeventId(Integer ceventId) { this.ceventId = ceventId; } public void setPatientId(Integer patientId) { this.patientId = patientId; } public void setVisitNumber(Integer visitNumber) { this.visitNumber = visitNumber; } public void setActivityStatus(ActivityStatus activityStatus) { this.activityStatus = activityStatus; } public void setCommentText(String commentText) { this.commentText = commentText; } public void setSourceSpecimenInfos( Collection<SaveCEventSpecimenInfo> sourceSpecimenInfos) { this.sourceSpecimenInfos = sourceSpecimenInfos; } public void setCeAttrList(List<CEventAttrSaveInfo> ceAttrList) { this.ceAttrList = ceAttrList; } @Override public boolean isAllowed(ActionContext context) { Permission permission; if (ceventId == null) { permission = new CollectionEventCreatePermission(patientId); } else { permission = new CollectionEventUpdatePermission(ceventId); } return permission.isAllowed(context); } @Override public IdResult run(ActionContext context) throws ActionException { CollectionEvent ceventToSave; if (ceventId == null) { ceventToSave = new CollectionEvent(); } else { ceventToSave = context.load(CollectionEvent.class, ceventId); } // FIXME Version check? Patient patient = context.load(Patient.class, patientId); ceventToSave.setPatient(patient); ceventToSave.setVisitNumber(visitNumber); ceventToSave.setActivityStatus(activityStatus); saveComment(context, ceventToSave); setSourceSpecimens(context, ceventToSave); setEventAttrs(context, patient.getStudy(), ceventToSave); context.getSession().saveOrUpdate(ceventToSave); return new IdResult(ceventToSave.getId()); } private void setSourceSpecimens(ActionContext context, CollectionEvent ceventToSave) { Set<Specimen> newOriginalSpecimens = new HashSet<Specimen>(); if (sourceSpecimenInfos != null) { OriginInfo oi = null; for (SaveCEventSpecimenInfo specInfo : sourceSpecimenInfos) { Specimen specimen; if (specInfo.id == null) { if (oi == null) { oi = new OriginInfo(); oi.setCenter(context.load(Center.class, specInfo.centerId)); context.getSession().saveOrUpdate(oi); } specimen = new Specimen(); specimen.setCurrentCenter(oi.getCenter()); specimen.setOriginInfo(oi); specimen.setTopSpecimen(specimen); } else { specimen = context.load(Specimen.class, specInfo.id); } specimen.setActivityStatus(specInfo.activityStatus); specimen.setCollectionEvent(ceventToSave); // cascade will save-update the specimens from this list: specimen.setOriginalCollectionEvent(ceventToSave); saveSpecimenComments(context, specimen, specInfo.comments); specimen.setCreatedAt(specInfo.createdAt); specimen.setInventoryId(specInfo.inventoryId); specimen.setQuantity(specInfo.quantity); specimen.setSpecimenType(context.load(SpecimenType.class, specInfo.specimenTypeId)); newOriginalSpecimens.add(specimen); } } Set<Specimen> oldOriginalSpecimens = ceventToSave.getOriginalSpecimens(); SetDifference<Specimen> originalSpecimensDiff = new SetDifference<Specimen>(oldOriginalSpecimens, newOriginalSpecimens); for (Specimen specimen : originalSpecimensDiff.getRemoveSet()) { removeOriginalSpecimen(context, specimen); } for (Specimen specimen : originalSpecimensDiff.getAddSet()) { updateCollectionEventOfChildren(context, specimen, ceventToSave); } } private void removeOriginalSpecimen(ActionContext context, Specimen specimen) { if (specimen.getChildSpecimens().isEmpty()) { specimen.getCollectionEvent().getAllSpecimens().remove(specimen); specimen.getCollectionEvent().getOriginalSpecimens() .remove(specimen); context.getSession().delete(specimen); } else { throw new ActionCheckException( MessageFormat .format( "Specimen {0} has children and cannot be deleted. Instead, move it to a different collection event.", specimen.getInventoryId())); } } private void updateCollectionEventOfChildren(ActionContext context, Specimen specimen, CollectionEvent collectionEvent) { specimen.setCollectionEvent(collectionEvent); collectionEvent.getAllSpecimens().add(specimen); for (Specimen child : specimen.getChildSpecimens()) { updateCollectionEventOfChildren(context, child, collectionEvent); } } public void setEventAttrs(ActionContext context, Study study, CollectionEvent cevent) throws ActionException { Map<Integer, StudyEventAttrInfo> studyEventList = new StudyGetEventAttrInfoAction( study.getId()).run(context).getMap(); Map<Integer, EventAttrInfo> ceventAttrList = new CollectionEventGetEventAttrInfoAction( ceventId).run(context).getMap(); if (ceAttrList != null) for (CEventAttrSaveInfo attrInfo : ceAttrList) { EventAttrInfo ceventAttrInfo = ceventAttrList .get(attrInfo.studyEventAttrId); StudyEventAttrInfo studyEventAttrInfo = studyEventList .get(attrInfo.studyEventAttrId); StudyEventAttr sAttr; if (ceventAttrInfo != null) { sAttr = ceventAttrInfo.attr.getStudyEventAttr(); } else { sAttr = studyEventAttrInfo == null ? null : studyEventAttrInfo.attr; if (sAttr == null) { throw new ActionException( "no StudyEventAttr found for id \"" //$NON-NLS-1$ + attrInfo.studyEventAttrId + "\""); //$NON-NLS-1$ } } if (ActivityStatus.ACTIVE != sAttr.getActivityStatus()) { throw new ActionException( "Attribute for \"" + sAttr.getGlobalEventAttr().getLabel() //$NON-NLS-1$ + "\" is locked, changes not permitted"); //$NON-NLS-1$ } if (attrInfo.value != null) { // validate the value attrInfo.value = attrInfo.value.trim(); if (attrInfo.value.length() > 0) { EventAttrTypeEnum type = attrInfo.type; List<String> permissibleSplit = null; if (type == EventAttrTypeEnum.SELECT_SINGLE || type == EventAttrTypeEnum.SELECT_MULTIPLE) { String permissible = sAttr.getPermissible(); if (permissible != null) { permissibleSplit = Arrays.asList(permissible .split(";")); //$NON-NLS-1$ } } if (type == EventAttrTypeEnum.SELECT_SINGLE) { if (!permissibleSplit.contains(attrInfo.value)) { throw new ActionException( "value " + attrInfo.value //$NON-NLS-1$ + "is invalid for label \"" + sAttr.getGlobalEventAttr().getLabel() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ } } else if (type == EventAttrTypeEnum.SELECT_MULTIPLE) { for (String singleVal : attrInfo.value.split(";")) { //$NON-NLS-1$ if (!permissibleSplit.contains(singleVal)) { throw new ActionException( "value " + singleVal + " (" //$NON-NLS-1$ //$NON-NLS-2$ + attrInfo.value + ") is invalid for label \"" + sAttr.getGlobalEventAttr().getLabel() //$NON-NLS-1$ + "\""); //$NON-NLS-1$ } } } else if (type == EventAttrTypeEnum.NUMBER) { Double.parseDouble(attrInfo.value); } else if (type == EventAttrTypeEnum.DATE_TIME) { try { DateFormatter.dateFormatter .parse(attrInfo.value); } catch (ParseException e) { throw new ActionException(e); } } else if (type == EventAttrTypeEnum.TEXT) { // do nothing } else { throw new ActionException( "type \"" + type + "\" not tested"); //$NON-NLS-1$ //$NON-NLS-2$ } } } EventAttr eventAttr; if (ceventAttrInfo == null) { eventAttr = new EventAttr(); cevent.getEventAttrs().add(eventAttr); eventAttr.setCollectionEvent(cevent); eventAttr.setStudyEventAttr(sAttr); } else { eventAttr = ceventAttrInfo.attr; } eventAttr.setValue(attrInfo.value); // FIXME need to remove attributes ? when they don't exist // anymore // in study maybe ? See previous code in wrapper ? } } private void saveComment(ActionContext context, CollectionEvent ceventToSave) { Comment comment = CommentUtil.create(context.getUser(), commentText); if (comment != null) { context.getSession().save(comment); ceventToSave.getComments().add(comment); } } private void saveSpecimenComments(ActionContext context, Specimen specimenToSave, List<String> comments) { List<Comment> completedComments = CommentUtil.createCommentsFromList(context.getUser(), comments); for (Comment comment : completedComments) { context.getSession().save(comment); specimenToSave.getComments().add(comment); } } }