/* * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations under * the License. * * The Original Code is OpenELIS code. * * Copyright (C) The Minnesota Department of Health. All Rights Reserved. * * Contributor(s): CIRG, University of Washington, Seattle WA. */ /** * C�te d'Ivoire * @author pahill * @since 2010-06-15 **/ package us.mn.state.health.lims.patient.saving; import org.apache.commons.beanutils.DynaBean; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.validator.GenericValidator; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionMessages; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.hibernate.Transaction; import us.mn.state.health.lims.analysis.dao.AnalysisDAO; import us.mn.state.health.lims.analysis.daoimpl.AnalysisDAOImpl; import us.mn.state.health.lims.analysis.valueholder.Analysis; import us.mn.state.health.lims.common.action.BaseActionForm; import us.mn.state.health.lims.common.action.IActionConstants; import us.mn.state.health.lims.common.exception.LIMSInvalidConfigurationException; import us.mn.state.health.lims.common.exception.LIMSRuntimeException; import us.mn.state.health.lims.common.formfields.FormFields; import us.mn.state.health.lims.common.formfields.FormFields.Field; import us.mn.state.health.lims.common.log.LogEvent; import us.mn.state.health.lims.common.services.NoteService; import us.mn.state.health.lims.common.services.StatusService; import us.mn.state.health.lims.common.services.StatusService.AnalysisStatus; import us.mn.state.health.lims.common.services.StatusService.OrderStatus; import us.mn.state.health.lims.common.services.StatusService.RecordStatus; import us.mn.state.health.lims.common.services.StatusService.SampleStatus; import us.mn.state.health.lims.common.services.StatusSet; import us.mn.state.health.lims.common.services.SampleAddService.SampleTestCollection; import us.mn.state.health.lims.common.util.DateUtil; import us.mn.state.health.lims.common.util.StringUtil; import us.mn.state.health.lims.common.util.SystemConfiguration; import us.mn.state.health.lims.common.util.validator.ActionError; import us.mn.state.health.lims.hibernate.HibernateUtil; import us.mn.state.health.lims.note.dao.NoteDAO; import us.mn.state.health.lims.note.daoimpl.NoteDAOImpl; import us.mn.state.health.lims.note.valueholder.Note; import us.mn.state.health.lims.observationhistory.dao.ObservationHistoryDAO; import us.mn.state.health.lims.observationhistory.daoimpl.ObservationHistoryDAOImpl; import us.mn.state.health.lims.observationhistory.valueholder.ObservationHistory; import us.mn.state.health.lims.observationhistorytype.ObservationHistoryTypeMap; import us.mn.state.health.lims.observationhistorytype.dao.ObservationHistoryTypeDAO; import us.mn.state.health.lims.observationhistorytype.daoImpl.ObservationHistoryTypeDAOImpl; import us.mn.state.health.lims.observationhistorytype.valueholder.ObservationHistoryType; import us.mn.state.health.lims.organization.dao.OrganizationDAO; import us.mn.state.health.lims.organization.daoimpl.OrganizationDAOImpl; import us.mn.state.health.lims.organization.valueholder.Organization; import us.mn.state.health.lims.patient.dao.PatientDAO; import us.mn.state.health.lims.patient.daoimpl.PatientDAOImpl; import us.mn.state.health.lims.patient.util.PatientUtil; import us.mn.state.health.lims.patient.valueholder.ObservationData; import us.mn.state.health.lims.patient.valueholder.Patient; import us.mn.state.health.lims.patientidentity.dao.PatientIdentityDAO; import us.mn.state.health.lims.patientidentity.daoimpl.PatientIdentityDAOImpl; import us.mn.state.health.lims.patientidentity.valueholder.PatientIdentity; import us.mn.state.health.lims.patientidentitytype.util.PatientIdentityTypeMap; import us.mn.state.health.lims.person.dao.PersonDAO; import us.mn.state.health.lims.person.daoimpl.PersonDAOImpl; import us.mn.state.health.lims.person.valueholder.Person; import us.mn.state.health.lims.project.dao.ProjectDAO; import us.mn.state.health.lims.project.daoimpl.ProjectDAOImpl; import us.mn.state.health.lims.project.valueholder.Project; import us.mn.state.health.lims.referencetables.daoimpl.ReferenceTablesDAOImpl; import us.mn.state.health.lims.sample.action.util.SamplePatientUpdateData; import us.mn.state.health.lims.sample.dao.SampleDAO; import us.mn.state.health.lims.sample.daoimpl.SampleDAOImpl; import us.mn.state.health.lims.sample.form.ProjectData; import us.mn.state.health.lims.sample.util.CI.BaseProjectFormMapper; import us.mn.state.health.lims.sample.util.CI.BaseProjectFormMapper.TypeOfSampleTests; import us.mn.state.health.lims.sample.util.CI.IProjectFormMapper; import us.mn.state.health.lims.sample.util.CI.ProjectForm; import us.mn.state.health.lims.sample.util.CI.ProjectFormMapperFactory; import us.mn.state.health.lims.sample.valueholder.Sample; import us.mn.state.health.lims.samplehuman.dao.SampleHumanDAO; import us.mn.state.health.lims.samplehuman.daoimpl.SampleHumanDAOImpl; import us.mn.state.health.lims.samplehuman.valueholder.SampleHuman; import us.mn.state.health.lims.sampleitem.dao.SampleItemDAO; import us.mn.state.health.lims.sampleitem.daoimpl.SampleItemDAOImpl; import us.mn.state.health.lims.sampleitem.valueholder.SampleItem; import us.mn.state.health.lims.sampleorganization.dao.SampleOrganizationDAO; import us.mn.state.health.lims.sampleorganization.daoimpl.SampleOrganizationDAOImpl; import us.mn.state.health.lims.sampleorganization.valueholder.SampleOrganization; import us.mn.state.health.lims.sampleproject.dao.SampleProjectDAO; import us.mn.state.health.lims.sampleproject.daoimpl.SampleProjectDAOImpl; import us.mn.state.health.lims.sampleproject.valueholder.SampleProject; import us.mn.state.health.lims.test.dao.TestDAO; import us.mn.state.health.lims.test.daoimpl.TestDAOImpl; import us.mn.state.health.lims.test.valueholder.Test; import us.mn.state.health.lims.typeofsample.valueholder.TypeOfSample; import java.lang.reflect.InvocationTargetException; import java.sql.Timestamp; import java.util.*; import java.util.Map.Entry; import static us.mn.state.health.lims.sample.util.CI.ProjectForm.EID; import static us.mn.state.health.lims.sample.util.CI.ProjectForm.SPECIAL_REQUEST; import java.lang.reflect.InvocationTargetException; /** * Update/Creates, as needed, a Sample and Patient and all associated parts in * order to enter that patient in the system. To use one of these to accession, * you should: * <nl> * <li>Provide a constructor (which probably includes whatever structure comes * from your UI form, so it can be used below). * <li>set the expected new RecordStatus for the patient and sample (maybe in * the constructor), but you can change that later. * <li>implement canAccession() to decide if this is the right time to try this * accession algorithm. * <li>implement validate* and match* methods to make sure all the entities * created and found hang together. * <li>implement the missing populate* methods (including providing any empty * implementation). * <li>possibly override the persist* methods, if there is more to do than * simply saving what has been built (delete any old ones? Update instead?). * </nl> * Use:<br/> * if (myAccessioner1.canAccession()) { if (!myAccession1.accession(...)) { * errors = myAccession1.getMessages(); saveErrors(request, errors); * request.setAttribute(Globals.ERROR_KEY, errors); return * mapping.findForward(FWD_FAIL); } else { return * mapping.findForward(FWD_SUCCESS); } } else (myAccession2.canAccession() { ... * } * * PAH 07/2010 This object is still a work in progress. For example, we have a * member for projectFormMapper, but all its use in the subclasses at this time * . Maybe the projectFormMapper should just be injected after creation? It is * also the case that there are form field property names listed in these * various acccesioning classes when the formFieldMapper class is really the * class that should know the right place to find values on the submitted form. * * @author pahill */ public abstract class Accessioner { /** * a set of possible analysis status that means an analysis is done */ private Set<String> analysisDone = new HashSet<String>(); { analysisDone.add(StatusService.getInstance().getStatusID(StatusService.AnalysisStatus.Finalized)); analysisDone.add(StatusService.getInstance().getStatusID(StatusService.AnalysisStatus.NonConforming_depricated)); analysisDone.add(StatusService.getInstance().getStatusID(StatusService.AnalysisStatus.Canceled)); } /** * Sample or Patient Entry always mark the type of an analysis as a MANUAL * type. */ private static final String DEFAULT_ANALYSIS_TYPE = IActionConstants.ANALYSIS_TYPE_MANUAL; // private static String OBSERVATION_HISTORY_YES_ID = null; private static String SAMPLE_TABLE_ID = null; static { SAMPLE_TABLE_ID = new ReferenceTablesDAOImpl().getReferenceTableByName("sample").getId(); // OBSERVATION_HISTORY_YES_ID = new DictionaryDAOImpl().getDictionaryByDictEntry("Demographic Response Yes (in Yes or No)").getId(); } /** * Find the projectFormName where ever we normally store it. This is for * that could which needs this information before creating a * projectFormMapper * * @param form * @return the current projectFormName * @throws IllegalAccessException * @throws InvocationTargetException * @throws NoSuchMethodException */ public static String findProjectFormName(DynaBean form) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { return ((ObservationData) (PropertyUtils.getProperty(form, "observations"))).getProjectFormName(); } /** * @param dynaBean * @return * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws LIMSRuntimeException */ public static IProjectFormMapper getProjectFormMapper(DynaBean dynaBean) throws LIMSRuntimeException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { return new ProjectFormMapperFactory().getProjectInitializer(findProjectFormName(dynaBean), (BaseActionForm) dynaBean); } /** * @param dynaBean * @return */ public static IProjectFormMapper getProjectFormMapper(String projectFormName, DynaBean dynaBean) { return new ProjectFormMapperFactory().getProjectInitializer(projectFormName, (BaseActionForm) dynaBean); } protected String accessionNumber; protected String patientIdentifier; protected String patientSiteSubjectNo; protected StatusSet statusSet; ActionMessages messages = new ActionMessages(); protected java.sql.Date today; protected String todayAsText; protected String sysUserId; protected ProjectForm projectForm; protected Patient patientInDB; private List<PatientIdentity> patientIdentities = new ArrayList<PatientIdentity>(); protected List<ObservationHistory> observationHistories = new ArrayList<ObservationHistory>(); Map<String, List<ObservationHistory>> observationHistoryLists = null; protected Sample sample; protected List<SampleItemAnalysisCollection> sampleItemsAnalysis = new ArrayList<SampleItemAnalysisCollection>(); protected List<SampleOrganization> sampleOrganizations = new ArrayList<SampleOrganization>(); protected SampleHuman sampleHuman; protected SampleProject sampleProject; protected RecordStatus newPatientStatus; protected RecordStatus newSampleStatus; protected PatientDAO patientDAO = new PatientDAOImpl(); protected PersonDAO personDAO = new PersonDAOImpl(); protected PatientIdentityDAO identityDAO = new PatientIdentityDAOImpl(); protected SampleDAO sampleDAO = new SampleDAOImpl(); protected SampleHumanDAO sampleHumanDAO = new SampleHumanDAOImpl(); protected SampleProjectDAO sampleProjectDAO = new SampleProjectDAOImpl(); protected ObservationHistoryDAO observationHistoryDAO = new ObservationHistoryDAOImpl(); protected ProjectDAO projectDAO = new ProjectDAOImpl(); protected OrganizationDAO organizationDAO = new OrganizationDAOImpl(); protected SampleOrganizationDAO sampleOrganizationDAO = new SampleOrganizationDAOImpl(); protected SampleItemDAO sampleItemDAO = new SampleItemDAOImpl(); protected TestDAO testDAO = new TestDAOImpl(); protected AnalysisDAO analysisDAO = new AnalysisDAOImpl(); private NoteDAO noteDAO = new NoteDAOImpl(); protected boolean existingSample; protected boolean existingPatient; protected IProjectFormMapper projectFormMapper; protected Patient patientToDelete; protected List<Analysis> analysisToUpdate = new ArrayList<Analysis>(); protected List<Analysis> analysisToDelete = new ArrayList<Analysis>(); protected List<SampleItem> sampleItemsToDelete = new ArrayList<SampleItem>(); private boolean aDifferentPatientRecord = false; private ProjectData projectData; protected Accessioner(String sampleIdentifier, String patientIdentifier, String siteSubjectNo, String sysUserId) { this(); this.accessionNumber = sampleIdentifier; this.patientIdentifier = patientIdentifier; this.patientSiteSubjectNo = siteSubjectNo; this.sysUserId = sysUserId; } public Accessioner() { today = new java.sql.Date(System.currentTimeMillis()); todayAsText = DateUtil.formatDateAsText(today); } /** * Primary entry point for processing a patient/sample combination Check the * messages, on true there may have been errors * * @return TRUE => errors FALSE => did not do it, had a problem doing it. * @throws Exception * @throws Exception */ public String save() throws Exception { Transaction tx = null; try { if (!canAccession()) { return null; } existingPatient = findPatient(); if (existingPatient && !validateFoundPatient()) { return IActionConstants.FWD_FAIL; } existingSample = findSample(); if (existingSample && !validateFoundSample()) { return IActionConstants.FWD_FAIL; } if (!matchPatientAndSample()) { return IActionConstants.FWD_FAIL; } populatePatientData(); populateSampleData(); populateSampleHuman(); populateObservationHistory(); tx = HibernateUtil.getSession().beginTransaction(); // all of the following methods are assumed to only write when // necessary persistPatient(); persistIdentityTypes(); persistSampleData(); persistSampleHuman(); persistObservationHistory(); // persistObservationHistoryLists();// no more running name has been changed to persistObservationHistoryLists2 persistObservationHistoryLists2(); persistRecordStatus(); deleteOldPatient(); populateAndPersistUnderInvestigationNote(); tx.commit(); return IActionConstants.FWD_SUCCESS; } catch (Exception e) { if (null != tx) { tx.rollback(); } logAndAddMessage("save()", "errors.InsertException", e); return IActionConstants.FWD_FAIL; } finally { HibernateUtil.closeSession(); } } private void populateAndPersistUnderInvestigationNote() { // N.B. The notes is being attached to the Sample table rather than the // observation history because the observation history table // is being updated by inserting new observation histories and then // deleting the old ones. Any references to the old observation history // under investigation row are being lost. Until we fix that the notes // will be attached to the sample with note type of "UnderInvestigation" if (//OBSERVATION_HISTORY_YES_ID.equals(observationData.getUnderInvestigation()) && <-- not sure of the business rules around this !GenericValidator.isBlankOrNull(projectData.getUnderInvestigationNote())) { Note note = new Note(); note.setNoteType(Note.EXTERNAL); note.setReferenceId(sample.getId()); note.setReferenceTableId(SAMPLE_TABLE_ID); List<Note> noteList = noteDAO.getNotesByNoteTypeRefIdRefTable(note); if (noteList != null && !noteList.isEmpty()) { note = noteList.get(0); note.setText(note.getText() + "<br/>" + getActionLabel() + ": " + projectData.getUnderInvestigationNote()); } else { note.setText(getActionLabel() + ": " + projectData.getUnderInvestigationNote()); note.setSubject("UnderInvestigation"); } note.setSysUserId(sysUserId); note.setSystemUser( NoteService.createSystemUser( sysUserId )); if (note.getId() == null) { noteDAO.insertData(note); } else { noteDAO.updateData(note); } } } protected void persistSampleData() throws Exception { persistSample(); persistSampleProject(); persistSampleOrganization(); persisteSampleItemsChanged(); persistSampleItemsAndAnalysis(); } /** * This is probably the result of Sample(Second)Entry, so nothing to due by * default */ private void persisteSampleItemsChanged() { analysisDAO.deleteData(analysisToDelete); sampleItemDAO.deleteData(sampleItemsToDelete); for (Analysis analysis : this.analysisToUpdate) { analysisDAO.updateData(analysis); } } /** * This method is called when this object contains a patient from the * database found by ID. Sometimes we need to validate some of the fields * with the values from the input. Place an error in the messages list to * return an error. * * @return TRUE => all is well with the patient we have found in the * database vs. the data coming from the input. */ protected boolean validateFoundPatient() { return true; } /** * This method is called when this object contains a sample from the * database found by ID. Sometimes we need to validate some of the fields * with the values from the input. Place an error in the messages list to * return an error. * * @return TRUE => all is well with the SAMPLE we have found in the database * vs. the data coming from the input. */ protected boolean validateFoundSample() { return true; } /** * This method is called when there is something to check to make sure the * patient and sample go together correctly. * * @return TRUE => all looks good. */ protected boolean matchPatientAndSample() { return true; } /** * Use appropriate means to come up with a patient and its person record. * * @return TRUE => patient was found in the database, FALSE => patient not * found, simply created. */ protected boolean findPatient() { String patientId = this.statusSet.getPatientId(); String sampleId = this.statusSet.getSampleId(); String unknownPatient = PatientUtil.getUnknownPatient().getId(); if (sampleId == null) { // no sample => nothing to leverage so build up from what we have return createPatientByIdentifiers(); } if (patientId.equals(unknownPatient)) { // the UNKNOWN patient, so we'll create a new one setADifferentPatient(true); return createNewPatient(); } // the patient is already associated with the existing sample and its // not the unknown one patientInDB = patientDAO.readPatient(patientId); if (patientIdentifiersDoNotMatch()) { Patient otherPatient = findPatientByIndentifiers(); if (otherPatient == null) { // unknown int otherSamplesOnCurrentPatient = sampleHumanDAO.getSamplesForPatient(patientId).size() - 1; if (otherSamplesOnCurrentPatient == 0) { // stick with the existing patient of the sample, update it return true; } else { // it's a new patient, so we might have to move some old // pointers around. setADifferentPatient(true); return createNewPatient(); } } else { // it's another existing patient, so use that and don't forget // we're doing that. patientInDB = otherPatient; setADifferentPatient(false); return true; } } return true; } private boolean patientIdentifiersDoNotMatch() { String existingSubjectNo = StringUtil.replaceNullWithEmptyString(patientInDB.getNationalId()).trim(); if (!GenericValidator.isBlankOrNull(this.patientIdentifier) && this.patientIdentifier.equals(existingSubjectNo)) { return false; } String existingSiteSubjectNo = StringUtil.replaceNullWithEmptyString(patientInDB.getExternalId()).trim(); return !(!GenericValidator.isBlankOrNull(this.patientSiteSubjectNo) && this.patientSiteSubjectNo.equals(existingSiteSubjectNo)); } /** * Either find it by the primary or secondary identifier or return a new one */ private boolean createPatientByIdentifiers() { patientInDB = findPatientByIndentifiers(); return patientInDB != null || createNewPatient(); } private Patient findPatientByIndentifiers() { Patient aPatient; if (this.patientIdentifier != "") { aPatient = patientDAO.getPatientByNationalId(this.patientIdentifier); } else { String externalId = this.projectFormMapper.getSiteSubjectNumber(); aPatient = patientDAO.getPatientByExternalId(externalId); } return aPatient; } /** * @return FALSE since it doesn't already exist */ protected boolean createNewPatient() { patientInDB = new Patient(); patientInDB.setPerson(new Person()); return false; } /** * * @return true = existing sample * @throws LIMSRuntimeException * @throws LIMSInvalidConfigurationException */ protected boolean findSample() throws LIMSRuntimeException, LIMSInvalidConfigurationException { sample = sampleDAO.getSampleByAccessionNumber(accessionNumber); String sampleId = this.statusSet.getSampleId(); // if there is sample for the given acc. number is an existing sample it // had better be same one we found when we loading the sampleSet if (sample != null && !isNewSample()) { if (!sample.getId().equals(sampleId)) { messages.add(ActionErrors.GLOBAL_MESSAGE, new ActionError("errors.may_not_reuse_accession_number", accessionNumber)); throw new RuntimeException("You can not re-use an existing accessionNumber " + accessionNumber); } return true; } else { // the accession number is unknown, so load/build the sample using // the sampleStatus ID sample = new Sample(); if (sampleId == null) { return false; // it brand new, no existing accessionNumber in // use, not known sampleId provided } else { sample = knownSampleTemplate(); sampleDAO.getData(sample); sample.setAccessionNumber(accessionNumber); return true; // it existed previously, but we have a new // (edited) accesionNumber } } } private Sample knownSampleTemplate() { Sample sample = new Sample(); sample.setId(this.statusSet.getSampleId()); return sample; } /** * @return if the sample we are working with is NOT in the database this * object is about to create it. */ protected boolean isNewSample() { return sample.getId() == null; } /** * @return TRUE if the patient is not the known patient already associated * with the known sample. */ protected boolean isADifferentPatient() { return this.aDifferentPatientRecord; } /** * Call this to record that when we started, the patient record associated * with the original sample record is NOT the record we are now working * with. We record this because, once we updating records to write, we can * longer compare IDs to come up with the answer. */ protected void setADifferentPatient(boolean force) { //TODO 'force' was added as a safety because aDifferentPatientRecourd was not being set to true when it should // have. The original test for aDifferentPatientRecord may still be needed, in the two places force is set // to true aDifferentPatientRecord = (force || patientInDB == null || statusSet.getPatientId() != patientInDB.getId()); } /** * Test this object to see if we should even begin. This is intended for * checking existing patient/sample record status. * * @return TRUE => all is well; FALSE (Default) => this particular version * of the accession process is not appropriate for the status * combination. */ abstract public boolean canAccession(); /** * load up this object with any new observation history records, including * lists */ protected void populateObservationHistory() { projectFormMapper.getDynaBean(); ObservationData observationData = (ObservationData) (projectFormMapper.getDynaBean().get("observations")); this.observationHistories = projectFormMapper.readObservationHistories(observationData); this.observationHistoryLists = projectFormMapper.readObservationHistoryLists(observationData); } public StatusSet findStatusSet() { if (statusSet == null) { String sampleId = projectFormMapper.getSampleId(); if (GenericValidator.isBlankOrNull(sampleId)) { statusSet = StatusService.getInstance().getStatusSetForAccessionNumber(accessionNumber); } else { statusSet = StatusService.getInstance().getStatusSetForSampleId(sampleId); } } return statusSet; } /** * Move data from the form to the sample and sample related objects, include * SampleOrganization but not to any of the entities which tie a patient to * a sample; don't include ObservationHistory and SampleHuman * * @throws Exception * if things go wrong. */ abstract protected void populateSampleData() throws Exception; /** * Create any appropriate sample human entity * * @throws Exception */ protected void populateSampleHuman() throws Exception { if (isNewSample()) { sample.setStatusId(StatusService.getInstance().getStatusID(OrderStatus.Entered)); sampleHuman = new SampleHuman(); } else { if (isNewPatient() || isADifferentPatient()) { sampleHuman = new SampleHuman(); sampleHuman.setSampleId(sample.getId()); sampleHumanDAO.getDataBySample(sampleHuman); } } if (isADifferentPatient()) { int size = sampleHumanDAO.getSamplesForPatient(statusSet.getPatientId()).size(); if (size == 1) { // if there is only one sample on the OLD patient of this sample // we can delete it. patientToDelete = knownPatientTemplate(); patientDAO.getData(patientToDelete); } } } private boolean isNewPatient() { return patientInDB.getId() == null; } /** * Build an example Patient record with the ID only for the current patient * of the sample * * @return */ private Patient knownPatientTemplate() { Patient patient = new Patient(); patient.setId(statusSet.getPatientId()); return patient; } protected void populateSample(Timestamp receivedDateForDisplay, Timestamp collectionDateForDisplay) throws Exception { sample.setAccessionNumber(accessionNumber); sample.setReceivedTimestamp(receivedDateForDisplay); sample.setCollectionDate(collectionDateForDisplay); // and all the administration fields. if (isNewSample()) { sample.setEnteredDateForDisplay(todayAsText); sample.setEnteredDate(today); } sample.setDomain(SystemConfiguration.getInstance().getHumanDomain()); } protected void populateSampleProject() { if (!isSampleInProject()) { Project project = projectForm.getProject(); sampleProject = new SampleProject(); sampleProject.setProject(project); } } /** * @return TRUE only if the sample is already associated with a project. */ private boolean isSampleInProject() { if (isNewSample()) { return false; } SampleProject oldSampleProject = sampleProjectDAO.getSampleProjectBySampleId(this.sample.getId()); return oldSampleProject != null; } /** * create a sampleOrganization record, finding any old one to delete. While * there can be more than one organization per sample, this code only * supports one. * * @param organizationId * if none null, use it; otherwise do nothing */ protected void populateSampleOrganization(String organizationId) { if (GenericValidator.isBlankOrNull(organizationId) || organizationId.equals(BaseProjectFormMapper.ORGANIZATION_ID_NONE)) { return; } Organization org = new Organization(); org.setId(organizationId); organizationDAO.getData(org); if (null == org.getId()) { throw new LIMSRuntimeException("Undefined organization name = " + organizationId + ". Unable to create sample to organization mapping."); } SampleOrganization oldSampleOrg; if (!isNewSample()) { oldSampleOrg = new SampleOrganization(); oldSampleOrg.setSample(sample); sampleOrganizationDAO.getDataBySample(oldSampleOrg); if (oldSampleOrg.getOrganization() != null) { // there may not be an // organiztion // attached yet String oldOrganizationId = oldSampleOrg.getOrganization().getId(); if (oldOrganizationId.equals(organizationId)) { return; // we have an existing sample with the right // organization already, so don't bother with delete // or insert } oldSampleOrg.setSysUserId(sysUserId); oldSampleOrg.setOrganization(org); sampleOrganizations.add(oldSampleOrg); } } SampleOrganization so = new SampleOrganization(); so.setOrganization(org); // sampleOrganization.setSampleOrganizationType("?"); // nothing // important so.setSysUserId(sysUserId); sampleOrganizations.add(so); } /** * Add to the list of patient identities which will be saved. * * @param patientIdentityTypeName * @param paramValue * a value for a particular patient identity; if null, do * nothing. */ protected void addPatientIdentity(String patientIdentityTypeName, String paramValue) { if (paramValue == null) { return; } String typeID = PatientIdentityTypeMap.getInstance().getIDForType(patientIdentityTypeName); PatientIdentity identity = new PatientIdentity(); identity.setPatientId(patientInDB.getId()); identity.setIdentityTypeId(typeID); identity.setIdentityData(paramValue); patientIdentities.add(identity); } /** * Assume all the fields on the dynaForm have the right names (see code) and * @throws IllegalAccessException * @throws InvocationTargetException * @throws NoSuchMethodException */ protected void populatePatientData() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { DynaBean dynaForm = projectFormMapper.getDynaBean(); Timestamp lastupdated1 = patientInDB.getLastupdated(); PropertyUtils.copyProperties(patientInDB, dynaForm); patientInDB.setLastupdated(lastupdated1); Person person = patientInDB.getPerson(); PropertyUtils.copyProperties(person, dynaForm); patientInDB.setNationalId(convertEmptyToNull((String) dynaForm.get("subjectNumber"))); patientInDB.setExternalId(convertEmptyToNull((String) dynaForm.get("siteSubjectNumber"))); populatePatientBirthDate((String) dynaForm.get("birthDateForDisplay")); projectData = (ProjectData) dynaForm.get("ProjectData"); if (projectData != null) { person.setHomePhone(convertEmptyToNull(projectData.getPhoneNumber())); person.setFax(convertEmptyToNull(projectData.getFaxNumber())); person.setEmail(convertEmptyToNull(projectData.getEmail())); person.setStreetAddress(convertEmptyToNull(projectData.getAddress())); } } /** * @param value * - string to test * @return if the string is null or empty "", then return null */ private String convertEmptyToNull(String value) { return (GenericValidator.isBlankOrNull(value)) ? null : value; } /** * Given a possibly ambiguous birthday date string, get the right values * into both (1) the patient birthdate and (2) make sure we create a * patient_identity for any ambiguous value. */ protected void populatePatientBirthDate(String birthDateForDisplay) { patientInDB.setBirthDateForDisplay(birthDateForDisplay); } protected void populateSampleItems(List<TypeOfSampleTests> typeofSampleTestList, Timestamp collectionDate) throws Exception { sampleItemsAnalysis = new ArrayList<SampleItemAnalysisCollection>(); if (typeofSampleTestList.size() == 0) { messages.add(ActionErrors.GLOBAL_MESSAGE, new ActionError("errors.no.tests")); throw new Exception("No tests selected."); } for (TypeOfSampleTests typeofSampleTest : typeofSampleTestList) { TypeOfSample toSample = typeofSampleTest.typeOfSample; List<Test> tests = typeofSampleTest.tests; SampleItem item = buildSampleItem(sample, toSample, collectionDate); sampleItemsAnalysis.add(new SampleItemAnalysisCollection(item, tests)); } } protected SampleItem buildSampleItem(Sample sample, TypeOfSample typeofsample, Timestamp collectionDate) { SampleItem item = new SampleItem(); item.setTypeOfSample(typeofsample); item.setSortOrder(Integer.toString(0)); item.setStatusId(StatusService.getInstance().getStatusID(SampleStatus.Entered)); item.setCollectionDate(collectionDate); return item; } protected final class SampleItemAnalysisCollection { public SampleItem item; public List<Test> tests; public String collectionDate; public SampleItemAnalysisCollection(SampleItem item, List<Test> tests) throws Exception { // Currently we allow a sampleItem w/o any tests requested, // elsewhere we check that at least one test is ordered somewhere. // if (tests.size() == 0) { // messages.add(ActionErrors.GLOBAL_MESSAGE, new // ActionError("errors.samples.with.no.tests")); // throw new // Exception("A least one sample was defined without any corresponding test selected."); // } this.item = item; this.tests = tests; } } /** * Delete any new ones and remove any old ones. */ protected void persistSampleOrganization() { for (SampleOrganization so : sampleOrganizations) { sampleDAO.getData(sample); so.setSample(sample); if( so.getId() == null){ sampleOrganizationDAO.insertData(so); }else{ sampleOrganizationDAO.updateData(so); } } } protected void persistSampleItemsAndAnalysis() throws Exception { if (0 == sampleItemsAnalysis.size()) { return; } // Find all the already created sample items for this sample Map<String, SampleItem> itemsByType = findExistingSampleTypeItems(); int nextSortOrder = calcLastSortOrder(itemsByType) + 1; String analysisRevision = SystemConfiguration.getInstance().getAnalysisDefaultRevision(); boolean newAnalysis = false; for (SampleItemAnalysisCollection sampleTestPair : sampleItemsAnalysis) { // create new or find existing sample item for testing. SampleItem item = sampleTestPair.item; SampleItem existingSampleItem = itemsByType.get(item.getTypeOfSampleId()); if (existingSampleItem == null) { item.setSample(sample); item.setSortOrder(Integer.toString(nextSortOrder++)); item.setSysUserId(sysUserId); sampleItemDAO.insertData(item); } else { sampleTestPair.item = item = existingSampleItem; } List<String> existingTests = findDefinedTestsForItem(item); // insert any missing analysis for (Test newTest : sampleTestPair.tests) { testDAO.getData(newTest); if (!existingTests.contains(newTest.getId())) { Analysis analysis = buildAnalysis(analysisRevision, sampleTestPair, newTest); analysisDAO.insertData(analysis, false); newAnalysis = true; } } } // if we didn't create any analysis, we need to check if the sample is // completely done. if (!newAnalysis) { completeSample(); } } /** * @param itemsByType * @return the maximum value which has already been used in the set of * SampleItems */ private int calcLastSortOrder(Map<String, SampleItem> itemsByType) { int max = 0; for (Entry<String, SampleItem> entry : itemsByType.entrySet()) { SampleItem sampleItem = entry.getValue(); int curr = Integer.valueOf(sampleItem.getSortOrder()); if (max < curr) { max = curr; } } return max; } /** * Find all tests of the item which have had analysis already ordered * (defined). * * @param item * @return a list of test IDs */ private List<String> findDefinedTestsForItem(SampleItem item) { List<Analysis> analysesForItem = analysisDAO.getAnalysesBySampleItem(item); List<String> testIds = new ArrayList<String>(); for (Analysis analysis : analysesForItem) { Test test = analysis.getTest(); testIds.add(test.getId()); } return testIds; } private Map<String, SampleItem> findExistingSampleTypeItems() { List<SampleItem> itemsForSample = sampleItemDAO.getSampleItemsBySampleId(sample.getId()); Map<String, SampleItem> itemsByType = new HashMap<String, SampleItem>(); for (SampleItem sampleItem : itemsForSample) { String id = sampleItem.getTypeOfSampleId(); itemsByType.put(id, sampleItem); } return itemsByType; } /** * If during entry we find (1) all the analysis are finished, and (2) the * sample is not already been declared bad, then we're ready to mark the * sample as done. * * @throws Exception */ public void completeSample() throws Exception { if (isAllAnalysisDone() && !StatusService.getInstance().getStatusID(OrderStatus.NonConforming_depricated).equals(sample.getStatus())) { sample.setStatusId(StatusService.getInstance().getStatusID(OrderStatus.Finished)); sample.setSysUserId(sysUserId); sampleDAO.updateData(sample); } } /** * The question is whether we are ready to update the sample status. TODO * Pahill maybe we could move this to StatusService.getInstance()? */ private boolean isAllAnalysisDone() { List<Analysis> analyses = analysisDAO.getAnalysesBySampleId(sample.getId()); for (Analysis analysis : analyses) { if (!analysisDone.contains(analysis.getStatusId())) { return false; } } return true; } private Analysis buildAnalysis(String analysisRevision, SampleItemAnalysisCollection sampleTestCollection, Test test) { java.sql.Date collectionDateTime = DateUtil.convertStringDateToSqlDate(sampleTestCollection.collectionDate); Analysis analysis = new Analysis(); analysis.setTest(test); analysis.setIsReportable(test.getIsReportable()); analysis.setAnalysisType(DEFAULT_ANALYSIS_TYPE); analysis.setSampleItem(sampleTestCollection.item); analysis.setRevision(analysisRevision); analysis.setStartedDate(collectionDateTime); analysis.setStatusId(StatusService.getInstance().getStatusID(AnalysisStatus.NotStarted)); analysis.setTestSection(test.getTestSection()); analysis.setSysUserId(sysUserId); return analysis; } /** * save patient */ protected void persistPatient() { if (patientInDB != null) { Person person = patientInDB.getPerson(); person.setSysUserId(sysUserId); patientInDB.setSysUserId(sysUserId); if (patientInDB.getId() == null) { personDAO.insertData(person); patientDAO.insertData(patientInDB); } else { // The reason for the explicit person update is to capture the // history. personDAO.updateData(person); patientDAO.updateData(patientInDB); } } } /** * Save whatever patient identities have been created. * * @throws LIMSRuntimeException */ public void persistIdentityTypes() throws LIMSRuntimeException { if (0 == patientIdentities.size()) { return; } for (PatientIdentity identity : patientIdentities) { identity.setPatientId(patientInDB.getId()); identity.setSysUserId(sysUserId); identityDAO.insertData(identity); } } protected void persistSample() throws LIMSRuntimeException, Exception { if (null != sample) { sample.setSysUserId(sysUserId); if (sample.getId() != null) { sampleDAO.updateData(sample); HibernateUtil.getSession().evict(sample); } else { sampleDAO.insertDataWithAccessionNumber(sample); } } } protected void persistSampleProject() throws LIMSRuntimeException { if (null != sampleProject) { sampleProject.setSample(sample); sampleProject.setSysUserId(sysUserId); sampleProjectDAO.insertData(sampleProject); } } /** * Persist any old or new sampleHuman we're currently holding. */ protected void persistSampleHuman() { if (sampleHuman != null) { sampleHuman.setPatientId(patientInDB.getId()); sampleHuman.setSampleId(sample.getId()); // we do not store any doctor name as a provider in SampleHuman sampleHuman.setSysUserId(sysUserId); if (null == sampleHuman.getId()) { sampleHumanDAO.insertData(sampleHuman); } else { sampleHumanDAO.updateData(sampleHuman); } } } /** * Persist any simple observations histories in this accession object. */ protected void persistObservationHistory() { if (isADifferentPatient()) { List<ObservationHistory> oldOHes = observationHistoryDAO.getAll(knownPatientTemplate(), knownSampleTemplate()); for (ObservationHistory oldOh : oldOHes) { oldOh.setSampleId(sample.getId()); oldOh.setPatientId(patientInDB.getId()); oldOh.setSysUserId(sysUserId); observationHistoryDAO.updateData(oldOh); } } for (ObservationHistory newOh : observationHistories) { List<ObservationHistory> existingTypeOHes = observationHistoryDAO.getAll(patientInDB, sample, newOh.getObservationHistoryTypeId()); List<ObservationHistory> deleteTypeOHes = new ArrayList<ObservationHistory>(); // delete any matching old ones, but only when there are both same // Ob. History type AND the same value type. Why because the // database allows more than one of the same type, // and we store e.g. nationality twice as two types, D=dictionary // for dropdown value, L=value used when there the menu value is // other, and there is a literal with text for other. String newValueType = newOh.getValueType(); for (ObservationHistory oh : existingTypeOHes) { if (oh.getValueType().equals(newValueType)) { oh.setSysUserId(sysUserId); deleteTypeOHes.add(oh); } } if (deleteTypeOHes.size() > 0) { observationHistoryDAO.delete(deleteTypeOHes); } newOh.setSampleId(sample.getId()); newOh.setPatientId(patientInDB.getId()); newOh.setSysUserId(sysUserId); observationHistoryDAO.insertData(newOh); } } //Note -- this version does not do the delete insert model of updates but we are putting off // rolling it out because of the cost of testing /* protected void persistObservationHistory() { if (isADifferentPatient()) { List<ObservationHistory> oldOHes = observationHistoryDAO.getAll(knownPatientTemplate(), knownSampleTemplate()); for (ObservationHistory oldOh : oldOHes) { oldOh.setSampleId(sample.getId()); oldOh.setPatientId(patientInDB.getId()); oldOh.setSysUserId(sysUserId); observationHistoryDAO.updateData(oldOh); } } for (ObservationHistory newOh : observationHistories) { boolean machedInDB = false; List<ObservationHistory> existingTypeOHes = observationHistoryDAO.getAll(patientInDB, sample, newOh.getObservationHistoryTypeId()); List<ObservationHistory> deleteTypeOHes = new ArrayList<ObservationHistory>(); // update any matching old ones, but only when there are both same // Ob. History type AND the same value type. Why because the // database allows more than one of the same type, // and we store e.g. nationality twice as two types, D=dictionary // for dropdown value, L=value used when there the menu value is // other, and there is a literal with text for other. String newValueType = newOh.getValueType(); for (ObservationHistory oh : existingTypeOHes) { if (oh.getValueType().equals(newValueType)) { machedInDB = true; if (oh.getValue() != null && !oh.getValue().equals(newOh.getValue())) { oh.setSysUserId(sysUserId); oh.setValue(newOh.getValue()); observationHistoryDAO.updateData(oh); } } } // if (deleteTypeOHes.size() > 0) { // observationHistoryDAO.delete(deleteTypeOHes); // } if (!machedInDB) { newOh.setSampleId(sample.getId()); newOh.setPatientId(patientInDB.getId()); newOh.setSysUserId(sysUserId); observationHistoryDAO.insertData(newOh); } } } */ /** * */ protected void persistObservationHistoryLists() { System.out.println("FUNCTION NAME PROHIBITED !"); } protected void persistObservationHistoryLists2() { if (observationHistoryLists == null) { return; } for (String listType : this.observationHistoryLists.keySet()) { // throw away the old list Map<String, ObservationHistory> oldOHes = findExistingObservationHistories(listType); // System.out.println(); // System.out.println(listType + " oldOHes.size = " // +oldOHes.size()); for (ObservationHistory oh : oldOHes.values()) { oh.setSysUserId(sysUserId); } observationHistoryDAO.delete(new ArrayList<ObservationHistory>(oldOHes.values())); // insert the new List<ObservationHistory> newOHes = observationHistoryLists.get(listType); // System.out.println(listType + " newOHes.size = " // +newOHes.size()); for (ObservationHistory newOH : newOHes) { newOH.setSysUserId(sysUserId); newOH.setPatientId(patientInDB.getId()); newOH.setSampleId(sample.getId()); observationHistoryDAO.insertData(newOH); } } } private Map<String, ObservationHistory> findExistingObservationHistories(String nameKey) { Map<String, ObservationHistory> existing = new HashMap<String, ObservationHistory>(); String ohTypeId = ObservationHistoryTypeMap.getInstance().getIDForType(nameKey); List<ObservationHistory> existingOHes = observationHistoryDAO.getAll(patientInDB, sample, ohTypeId); for (ObservationHistory oh : existingOHes) { existing.put(oh.getValue(), oh); } return existing; } protected void persistRecordStatus() { // Special Request and EID don't have a patient entry form, so we move // the patient record status when we move the sample record status. if (projectForm == SPECIAL_REQUEST || projectForm == EID) { newPatientStatus = newSampleStatus; } StatusService.getInstance().persistRecordStatusForSample(sample, newSampleStatus, patientInDB, newPatientStatus, sysUserId); } protected void deleteOldPatient() { if (patientToDelete != null) { List<PatientIdentity> oldIdentities = identityDAO.getPatientIdentitiesForPatient(patientToDelete.getId()); for (PatientIdentity listIdentity : oldIdentities) { identityDAO.delete(listIdentity.getId(), sysUserId); } Person personToDelete = patientToDelete.getPerson(); patientToDelete.setSysUserId(sysUserId); patientDAO.deleteData(Arrays.asList(patientToDelete)); personToDelete.setSysUserId(sysUserId); personDAO.deleteData(Arrays.asList(personToDelete)); } } protected List<ObservationHistory> getObservationHistories() { return observationHistories; } protected SampleDAO getSimpleSampleDAO() { return new SampleDAOImpl(); } public ActionMessages getMessages() { return messages; } public void setMessages(ActionMessages messages) { this.messages = messages; } /**** * Record a thrown exception * * @param methodName * where it happened. * @param messageKey * default message if there is not already a error recorded. * @param e * the thrown exception of which to print the stack trace. */ public void logAndAddMessage(String methodName, String messageKey, Exception e) { e.printStackTrace(); LogEvent.logError(this.getClass().getSimpleName(), methodName, e.toString()); if (messages.size() == 0) { ActionError error = new ActionError(messageKey, null, null); messages.add(ActionMessages.GLOBAL_MESSAGE, error); } } protected abstract String getActionLabel(); protected void persistInitialSampleConditions(){ if(!FormFields.getInstance().useField(Field.InitialSampleCondition)) return; try { String xml=(String)projectFormMapper.getDynaBean().get( "sampleXML" ); //System.out.println("AMANI:"+xml); Document sampleDom = DocumentHelper.parseText(xml); for (Iterator i = sampleDom.getRootElement().elementIterator("sample"); i.hasNext();) { Element sampleItem = (Element) i.next(); String initialSampleConditionIdString = sampleItem.attributeValue("initialConditionIds"); String sampleItemId = sampleItem.attributeValue("sampleID"); ObservationHistoryDAO ohDAO = new ObservationHistoryDAOImpl(); ObservationHistory observation=new ObservationHistory(); if (!GenericValidator.isBlankOrNull(initialSampleConditionIdString)) { String[] initialSampleConditionIds = initialSampleConditionIdString.split(","); for(int j=0;j<initialSampleConditionIds.length;j++){ observation=new ObservationHistory(); observation.setValue(initialSampleConditionIds[j]); observation.setValueType(ObservationHistory.ValueType.DICTIONARY); observation.setObservationHistoryTypeId(getObservationHistoryTypeId(new ObservationHistoryTypeDAOImpl(), "initialSampleCondition")); observation.setSampleId(sample.getId()); observation.setSampleItemId(sampleItemId); observation.setPatientId(patientInDB.getId()); observation.setSysUserId(sysUserId); ohDAO.insertData(observation); } } } } catch (DocumentException e) { e.printStackTrace(); } // dynaForm.set("orbservations", observations); } private static String getObservationHistoryTypeId(ObservationHistoryTypeDAO ohtDAO, String name) { ObservationHistoryType oht; oht = ohtDAO.getByName(name); if (oht != null) { return oht.getId(); } return null; } }