package edu.ualberta.med.biobank.forms; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.viewers.ComboViewer; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.forms.widgets.Section; import edu.ualberta.med.biobank.SessionManager; import edu.ualberta.med.biobank.common.action.collectionEvent.CollectionEventGetInfoAction; import edu.ualberta.med.biobank.common.action.collectionEvent.CollectionEventGetInfoAction.CEventInfo; import edu.ualberta.med.biobank.common.action.collectionEvent.CollectionEventSaveAction; import edu.ualberta.med.biobank.common.action.collectionEvent.CollectionEventSaveAction.CEventAttrSaveInfo; import edu.ualberta.med.biobank.common.action.collectionEvent.CollectionEventSaveAction.SaveCEventSpecimenInfo; import edu.ualberta.med.biobank.common.action.collectionEvent.EventAttrInfo; import edu.ualberta.med.biobank.common.action.patient.PatientGetSimpleCollectionEventInfosAction; import edu.ualberta.med.biobank.common.action.patient.PatientGetSimpleCollectionEventInfosAction.SimpleCEventInfo; import edu.ualberta.med.biobank.common.action.patient.PatientNextVisitNumberAction; import edu.ualberta.med.biobank.common.action.specimen.SpecimenInfo; import edu.ualberta.med.biobank.common.action.specimenType.SpecimenTypeGetAllAction; import edu.ualberta.med.biobank.common.action.study.StudyEventAttrInfo; import edu.ualberta.med.biobank.common.action.study.StudyGetEventAttrInfoAction; import edu.ualberta.med.biobank.common.action.study.StudyGetSourceSpecimensAction; import edu.ualberta.med.biobank.common.formatters.DateFormatter; import edu.ualberta.med.biobank.common.peer.CollectionEventPeer; import edu.ualberta.med.biobank.common.wrappers.CollectionEventWrapper; import edu.ualberta.med.biobank.common.wrappers.CommentWrapper; import edu.ualberta.med.biobank.common.wrappers.EventAttrTypeEnum; import edu.ualberta.med.biobank.common.wrappers.ModelWrapper; import edu.ualberta.med.biobank.gui.common.BgcPlugin; import edu.ualberta.med.biobank.gui.common.widgets.BgcBaseText; import edu.ualberta.med.biobank.gui.common.widgets.BgcEntryFormWidgetListener; import edu.ualberta.med.biobank.gui.common.widgets.DateTimeWidget; import edu.ualberta.med.biobank.gui.common.widgets.MultiSelectEvent; import edu.ualberta.med.biobank.gui.common.widgets.utils.ComboSelectionUpdate; import edu.ualberta.med.biobank.model.ActivityStatus; import edu.ualberta.med.biobank.model.CollectionEvent; import edu.ualberta.med.biobank.model.Comment; import edu.ualberta.med.biobank.model.EventAttrCustom; import edu.ualberta.med.biobank.model.SourceSpecimen; import edu.ualberta.med.biobank.model.SpecimenType; import edu.ualberta.med.biobank.treeview.patient.CollectionEventAdapter; import edu.ualberta.med.biobank.validators.DoubleNumberValidator; import edu.ualberta.med.biobank.validators.IntegerNumberValidator; import edu.ualberta.med.biobank.widgets.ComboAndQuantityWidget; import edu.ualberta.med.biobank.widgets.SelectMultipleWidget; import edu.ualberta.med.biobank.widgets.infotables.CommentsInfoTable; import edu.ualberta.med.biobank.widgets.infotables.NewSpecimenInfoTable.ColumnsShown; import edu.ualberta.med.biobank.widgets.infotables.entry.CEventSpecimenEntryInfoTable; import edu.ualberta.med.biobank.widgets.infotables.entry.CommentedSpecimenInfo; import edu.ualberta.med.biobank.widgets.utils.GuiUtil; import gov.nih.nci.system.applicationservice.ApplicationException; public class CollectionEventEntryForm extends BiobankEntryForm { public static final String ID = "edu.ualberta.med.biobank.forms.CollectionEventEntryForm"; public static final String MSG_NEW_PATIENT_VISIT_OK = "Creating a new patient visit record."; public static final String MSG_PATIENT_VISIT_OK = "Editing an existing patient visit record."; private CollectionEvent ceventCopy; private static class FormPvCustomInfo extends EventAttrCustom { private Control control; } private List<FormPvCustomInfo> pvCustomInfoList; private BgcEntryFormWidgetListener listener = new BgcEntryFormWidgetListener() { @Override public void selectionChanged(MultiSelectEvent event) { setDirty(true); } }; private ComboViewer activityStatusComboViewer; private CEventSpecimenEntryInfoTable specimensTable; private BgcBaseText visitNumberText; private DateTimeWidget timeDrawnWidget; private List<SpecimenInfo> sourceSpecimens; private CEventInfo ceventInfo; private CommentsInfoTable commentEntryTable; private CommentWrapper comment = new CommentWrapper( SessionManager.getAppService()); Map<Integer, StudyEventAttrInfo> studyAttrInfos; private CollectionEventWrapper cevent = new CollectionEventWrapper( SessionManager.getAppService()); @Override public void init() throws Exception { Assert.isTrue(adapter instanceof CollectionEventAdapter, "Invalid editor input: object of type " + adapter.getClass().getName()); ceventCopy = new CollectionEvent(); if (adapter.getId() == null) { ceventInfo = new CEventInfo(); ceventInfo.cevent = new CollectionEvent(); ceventInfo.cevent.setPatient(((CollectionEventAdapter) adapter) .getPatient()); } else { ceventInfo = SessionManager.getAppService().doAction( new CollectionEventGetInfoAction(adapter.getId())); } studyAttrInfos = SessionManager.getAppService().doAction( new StudyGetEventAttrInfoAction(ceventInfo.cevent .getPatient().getStudy().getId())).getMap(); copyCEvent(); String tabName; if (adapter.getId() == null) { tabName = "New collection event"; } else { tabName = NLS.bind("Collection Event - #{0}", ceventCopy.getVisitNumber()); } setPartName(tabName); } private void copyCEvent() throws Exception { // only value created when is a new cevent. ceventCopy.setPatient(ceventInfo.cevent.getPatient()); if (adapter.getId() == null) { ceventCopy.setVisitNumber(SessionManager .getAppService() .doAction( new PatientNextVisitNumberAction(ceventInfo.cevent .getPatient().getId())).getNextVisitNumber()); ceventCopy.setActivityStatus(ActivityStatus.ACTIVE); sourceSpecimens = new ArrayList<SpecimenInfo>(); } else { ceventCopy.setId(ceventInfo.cevent.getId()); ceventCopy.setVisitNumber(ceventInfo.cevent.getVisitNumber()); ceventCopy.setActivityStatus(ceventInfo.cevent.getActivityStatus()); ceventCopy.setComments(ceventInfo.cevent.getComments()); sourceSpecimens = new ArrayList<SpecimenInfo>( ); for (SpecimenInfo info : ceventInfo.sourceSpecimenInfos) sourceSpecimens.add(new CommentedSpecimenInfo(info)); } cevent.setWrappedObject(ceventCopy); comment.setWrappedObject(new Comment()); } @Override protected void createFormContent() throws Exception { form.setText("Collection Event Information"); form.setMessage(getOkMessage(), IMessageProvider.NONE); page.setLayout(new GridLayout(1, false)); createMainSection(); createCommentSection(); createSpecimensSection(); if (adapter.getId() == null) { setDirty(true); } } private void createCommentSection() { Composite client = createSectionWithClient("Comments"); GridLayout gl = new GridLayout(2, false); client.setLayout(gl); commentEntryTable = new CommentsInfoTable(client, cevent.getCommentCollection(true)); GridData gd = new GridData(); gd.horizontalSpan = 2; gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = SWT.FILL; commentEntryTable.setLayoutData(gd); createBoundWidgetWithLabel(client, BgcBaseText.class, SWT.MULTI, "Add a comment", null, comment, "message", null); } private void createMainSection() throws Exception { Composite client = toolkit.createComposite(page); GridLayout layout = new GridLayout(2, false); layout.horizontalSpacing = 10; client.setLayout(layout); client.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); toolkit.paintBordersFor(client); createReadOnlyLabelledField(client, SWT.NONE, "Study", cevent .getPatient().getStudy().getName()); createReadOnlyLabelledField(client, SWT.NONE, "Patient", cevent .getPatient().getPnumber()); visitNumberText = (BgcBaseText) createBoundWidgetWithLabel( client, BgcBaseText.class, SWT.NONE, "Visit#", null, cevent, CollectionEventPeer.VISIT_NUMBER.getName(), new IntegerNumberValidator( "Visit must have a number", false)); visitNumberText.addSelectionChangedListener(listener); setFirstControl(visitNumberText); activityStatusComboViewer = createComboViewer( client, "Activity status", ActivityStatus.valuesList(), cevent.getActivityStatus(), "Patient visit must have an activity status", new ComboSelectionUpdate() { @Override public void doSelection(Object selectedObject) { if (selectedObject != cevent.getActivityStatus()) { setDirty(true); } cevent .setActivityStatus((ActivityStatus) selectedObject); } }); widgetCreator.createLabel(client, "Time drawn to use \non new specimens"); timeDrawnWidget = new DateTimeWidget(client, SWT.DATE | SWT.TIME, new Date()); toolkit.adapt(timeDrawnWidget); createEventAttrSection(client); } private void createEventAttrSection(Composite client) throws Exception { pvCustomInfoList = new ArrayList<FormPvCustomInfo>(); for (Entry<Integer, StudyEventAttrInfo> entry : studyAttrInfos .entrySet()) { FormPvCustomInfo pvCustomInfo = new FormPvCustomInfo(); pvCustomInfo.setStudyEventAttrId(entry.getValue().attr.getId()); pvCustomInfo.setLabel(entry.getValue().attr.getGlobalEventAttr() .getLabel()); pvCustomInfo.setType(entry.getValue().type); pvCustomInfo.setAllowedValues(entry.getValue() .getStudyEventAttrPermissible()); // FIXME ugly EventAttrInfo eventAttrInfo = (adapter.getId() == null) ? null : ceventInfo.eventAttrs.get(entry.getKey()); String origValue = (eventAttrInfo == null) ? "" : eventAttrInfo.attr.getValue(); pvCustomInfo.setValue(origValue); pvCustomInfo.setOrigValue(origValue); pvCustomInfo.control = getControlForLabel(client, pvCustomInfo); pvCustomInfoList.add(pvCustomInfo); } } private void createSpecimensSection() { Section section = createSection("Source specimens"); specimensTable = new CEventSpecimenEntryInfoTable(section, sourceSpecimens, ceventCopy, ColumnsShown.CEVENT_SOURCE_SPECIMENS); specimensTable.adaptToToolkit(toolkit, true); specimensTable.addSelectionChangedListener(listener); try { final List<SpecimenType> allSpecimenTypes = SessionManager.getAppService() .doAction(new SpecimenTypeGetAllAction()).getList(); final Set<SourceSpecimen> studySourceSpecimens = SessionManager .getAppService() .doAction( new StudyGetSourceSpecimensAction(ceventInfo.cevent .getPatient().getStudy().getId())).getSet(); specimensTable.addEditSupport(studySourceSpecimens, allSpecimenTypes); addSectionToolbar(section, "Add specimens", new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { form.setFocus(); specimensTable.addOrEditSpecimen(true, null, studySourceSpecimens, allSpecimenTypes, ceventCopy, timeDrawnWidget.getDate()); } }); } catch (ApplicationException e) { BgcPlugin.openAsyncError( "Error retrieving source specimens", e); } section.setClient(specimensTable); } private Control getControlForLabel(Composite client, FormPvCustomInfo pvCustomInfo) { Control control; if (EventAttrTypeEnum.NUMBER == pvCustomInfo.getType()) { control = createBoundWidgetWithLabel( client, BgcBaseText.class, SWT.NONE, pvCustomInfo.getLabel(), null, pvCustomInfo, FormPvCustomInfo.VALUE_BIND_STRING, new DoubleNumberValidator( "You should select a valid number")); } else if (EventAttrTypeEnum.TEXT == pvCustomInfo.getType()) { control = createBoundWidgetWithLabel(client, BgcBaseText.class, SWT.NONE, pvCustomInfo.getLabel(), null, pvCustomInfo, FormPvCustomInfo.VALUE_BIND_STRING, null); } else if (EventAttrTypeEnum.DATE_TIME == pvCustomInfo.getType()) { control = createDateTimeWidget(client, pvCustomInfo.getLabel(), DateFormatter.parseToDateTime(pvCustomInfo.getValue()), null, null); } else if (EventAttrTypeEnum.SELECT_SINGLE == pvCustomInfo.getType()) { control = createBoundWidgetWithLabel(client, Combo.class, SWT.NONE, pvCustomInfo.getLabel(), pvCustomInfo.getAllowedValues(), pvCustomInfo, FormPvCustomInfo.VALUE_BIND_STRING, null); } else if (EventAttrTypeEnum.SELECT_MULTIPLE == pvCustomInfo.getType()) { widgetCreator.createLabel(client, pvCustomInfo.getLabel()); SelectMultipleWidget s = new SelectMultipleWidget(client, SWT.BORDER, pvCustomInfo.getAllowedValues(), selectionListener); s.adaptToToolkit(toolkit, true); if (pvCustomInfo.getValue() != null) { s.setSelections(pvCustomInfo.getValue().split( FormPvCustomInfo.VALUE_MULTIPLE_SEPARATOR)); } control = s; } else { Assert.isTrue(false, "Invalid pvInfo type: " + pvCustomInfo.getType()); return null; } GridData gd = new GridData(GridData.FILL_HORIZONTAL); control.setLayoutData(gd); return control; } private void updateControlForLabel(FormPvCustomInfo pvCustomInfo) { if ((EventAttrTypeEnum.NUMBER == pvCustomInfo.getType()) || (EventAttrTypeEnum.TEXT == pvCustomInfo.getType())) ((BgcBaseText) pvCustomInfo.control).setText(pvCustomInfo .getOrigValue()); else if (EventAttrTypeEnum.DATE_TIME == pvCustomInfo.getType()) ((DateTimeWidget) pvCustomInfo.control).setDate(DateFormatter .parseToDateTime(pvCustomInfo.getOrigValue())); else if (EventAttrTypeEnum.SELECT_SINGLE == pvCustomInfo.getType()) ((Combo) pvCustomInfo.control).setText(pvCustomInfo.getOrigValue()); else if (EventAttrTypeEnum.SELECT_MULTIPLE == pvCustomInfo.getType()) { ((SelectMultipleWidget) pvCustomInfo.control) .setSelections(pvCustomInfo.getOrigValue().split(";")); } else { Assert.isTrue(false, "Invalid pvInfo type: " + pvCustomInfo.getType()); } } @Override protected String getOkMessage() { return (adapter.getId() == null) ? MSG_NEW_PATIENT_VISIT_OK : MSG_PATIENT_VISIT_OK; } @Override protected void doBeforeSave() throws Exception { for (FormPvCustomInfo combinedPvInfo : pvCustomInfoList) { // set the value from the widget savePvInfoValueFromControlType(combinedPvInfo); } } @Override protected void saveForm() throws Exception { Assert.isNotNull(SessionManager.getUser().getCurrentWorkingCenter()); List<SaveCEventSpecimenInfo> cevents = new ArrayList<CollectionEventSaveAction.SaveCEventSpecimenInfo>(); for (SpecimenInfo o : specimensTable.getList()) { CommentedSpecimenInfo specInfo = (CommentedSpecimenInfo) o; SaveCEventSpecimenInfo ceSpecInfo = new SaveCEventSpecimenInfo(); ceSpecInfo.comments = specInfo.comments; ceSpecInfo.id = specInfo.specimen.getId(); ceSpecInfo.inventoryId = specInfo.specimen.getInventoryId(); ceSpecInfo.quantity = specInfo.specimen.getQuantity(); ceSpecInfo.specimenTypeId = specInfo.specimen.getSpecimenType().getId(); ceSpecInfo.activityStatus = specInfo.specimen.getActivityStatus(); ceSpecInfo.createdAt = specInfo.specimen.getCreatedAt(); ceSpecInfo.centerId = SessionManager.getUser().getCurrentWorkingCenter().getId(); cevents.add(ceSpecInfo); } List<CEventAttrSaveInfo> ceventAttrList = new ArrayList<CollectionEventSaveAction.CEventAttrSaveInfo>(); for (FormPvCustomInfo combinedPvInfo : pvCustomInfoList) { CEventAttrSaveInfo ceventAttr = new CEventAttrSaveInfo(); ceventAttr.studyEventAttrId = combinedPvInfo.getStudyEventAttrId(); ceventAttr.value = combinedPvInfo.getValue(); ceventAttr.type = combinedPvInfo.getType(); ceventAttrList.add(ceventAttr); } // save the collection event Integer savedCeventId = SessionManager .getAppService() .doAction( new CollectionEventSaveAction(ceventCopy.getId(), ceventCopy.getPatient().getId(), ceventCopy .getVisitNumber(), ceventCopy.getActivityStatus(), comment.getMessage(), cevents, ceventAttrList)).getId(); PatientGetSimpleCollectionEventInfosAction action = new PatientGetSimpleCollectionEventInfosAction(ceventCopy .getPatient().getId()); Map<Integer, SimpleCEventInfo> infos = SessionManager.getAppService().doAction(action).getMap(); ((CollectionEventAdapter) adapter).setValue(infos.get(savedCeventId)); } @Override protected boolean openViewAfterSaving() { return true; } private void savePvInfoValueFromControlType(FormPvCustomInfo pvCustomInfo) { // for text and combo, the databinding is used if (pvCustomInfo.control instanceof DateTimeWidget) { pvCustomInfo.setValue(((DateTimeWidget) pvCustomInfo.control) .getText()); } else if (pvCustomInfo.control instanceof ComboAndQuantityWidget) { pvCustomInfo .setValue(((ComboAndQuantityWidget) pvCustomInfo.control) .getText()); } else if (pvCustomInfo.control instanceof SelectMultipleWidget) { String[] values = ((SelectMultipleWidget) pvCustomInfo.control).getSelections(); pvCustomInfo.setValue(StringUtils.join(values, FormPvCustomInfo.VALUE_MULTIPLE_SEPARATOR)); } } @Override public String getNextOpenedFormId() { return CollectionEventViewForm.ID; } @Override public void setValues() throws Exception { GuiUtil .reset(activityStatusComboViewer, cevent.getActivityStatus()); specimensTable.reload(sourceSpecimens); commentEntryTable.setList(ModelWrapper.wrapModelCollection( SessionManager.getAppService(), ceventInfo.cevent.getComments(), CommentWrapper.class)); timeDrawnWidget.setDate(new Date()); resetPvCustomInfo(); } private void resetPvCustomInfo() throws Exception { for (FormPvCustomInfo pvCustomInfo : pvCustomInfoList) { updateControlForLabel(pvCustomInfo); } } }