/** * This Source Code Form is subject to the terms of the Mozilla Public License, * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. * * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS * graphic logo is a trademark of OpenMRS Inc. */ package org.openmrs.module.xforms; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openmrs.Location; import org.openmrs.Patient; import org.openmrs.PatientIdentifier; import org.openmrs.PatientIdentifierType; import org.openmrs.PersonAddress; import org.openmrs.PersonAttribute; import org.openmrs.PersonAttributeType; import org.openmrs.PersonName; import org.openmrs.User; import org.openmrs.api.APIException; import org.openmrs.api.PatientService; import org.openmrs.api.context.Context; import org.openmrs.hl7.HL7InQueue; import org.openmrs.module.xforms.formentry.FormEntryQueue; import org.openmrs.module.xforms.formentry.FormEntryQueueProcessor; import org.openmrs.module.xforms.formentry.FormEntryWrapper; import org.openmrs.module.xforms.formentry.HL7InQueueProcessor; import org.openmrs.module.xforms.model.PersonRepeatAttribute; import org.openmrs.module.xforms.util.DOMUtil; import org.openmrs.module.xforms.util.XformsUtil; import org.openmrs.util.OpenmrsConstants.PERSON_TYPE; import org.openmrs.util.OpenmrsUtil; import org.springframework.transaction.annotation.Transactional; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; /** * Processes Xforms Queue entries. * When the processing is successful, the queue entry is submitted to the FormEntry Queue. * For unsuccessful processing, the queue entry is put in the Xforms error folder. * * @author Daniel Kayiwa * @version 1.0 */ @Transactional public class XformsQueueProcessor { private static final Log log = LogFactory.getLog(XformsQueueProcessor.class); private static Boolean isRunning = false; // allow only one running private static final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); private DocumentBuilder db; // Instance of form entry processor private FormEntryQueueProcessor formEntryProcessor = null; // Instance of hl7 processor private static HL7InQueueProcessor hl7Processor = null; public XformsQueueProcessor(){ if (formEntryProcessor == null) formEntryProcessor = new FormEntryQueueProcessor(); if (hl7Processor == null) hl7Processor = new HL7InQueueProcessor(); try{ db = dbf.newDocumentBuilder(); } catch(Exception e){ log.error(Context.getMessageSourceService().getMessage("xforms.problemDocumentBuilder"), e); } } /** * Starts up a thread to process all existing xforms queue entries */ public void processXformsQueue() throws APIException { synchronized (isRunning) { if (isRunning) { log.warn(Context.getMessageSourceService().getMessage("xforms.problemXformsQueue")); return; } isRunning = true; } try { File queueDir = XformsUtil.getXformsQueueDir(); for (File file : queueDir.listFiles()) { try{ processXForm(XformsUtil.readFile(file.getAbsolutePath()), file.getAbsolutePath(), false, null); } catch(Exception e){ log.error(Context.getMessageSourceService().getMessage("xforms.problemProcessingXform") + file.getAbsolutePath(), e); } } } catch(Exception e){ log.error(Context.getMessageSourceService().getMessage("xforms.problemProcessingQueue"), e); } finally { isRunning = false; } } /** * Saves obs entered during new patient registration (if any). * * @param patient the patient whose obs to save. * @param root * @param pathName * @param propagateErrors * @throws Exception */ private void saveNewPatientEncounterIfAny(Patient patient, Element root, String pathName, boolean propagateErrors) throws Exception { NodeList elemList = root.getElementsByTagName("form"); if (!(elemList != null && elemList.getLength() > 0)) return; Element formNode = (Element)elemList.item(0); String id = formNode.getAttribute("id"); String name = formNode.getAttribute("name"); if(!(id != null && name != null && id.trim().length() > 0 && name.trim().length() > 0)) return; setNodeValue(formNode, XformBuilder.NODE_PATIENT_PATIENT_ID, patient.getPatientId().toString()); setNodeValue(formNode, XformBuilder.NODE_ENCOUNTER_LOCATION_ID, patient.getIdentifiers().iterator().next().getLocation().getLocationId().toString()); String xml = XformsUtil.doc2String(formNode); if(isRemoteFormEntry()){ FormEntryWrapper.createFormEntryQueue(xml); } else{ processDoc(xml, pathName, propagateErrors); } } /** * Processes an xforms model. * * @param xml the xml of the xforms model. * @param pathName the full path and name of file form which this xform model has been read. null can be passed if the form does not come from a file. */ public void processXForm(String xml, String pathName, boolean propagateErrors,HttpServletRequest request) throws Exception { String xmlOriginal = xml; Patient patient = null; try{ Document doc = db.parse(IOUtils.toInputStream(xml,XformConstants.DEFAULT_CHARACTER_ENCODING)); Element root = doc.getDocumentElement(); //Check if new patient doc if(DOMUtil.isPatientDoc(doc)){ patient = saveNewPatient(root,getCreator(doc),propagateErrors,request); if(patient == null) saveFormInError(xml,pathName, null); else{ saveNewPatientEncounterIfAny(patient, root, pathName, propagateErrors); if(!isRemoteFormEntry()){ saveFormInArchive(xml,pathName); } } } //Check if encounter doc else if(DOMUtil.isEncounterDoc(doc)) submitXForm(doc,xml,pathName,true,propagateErrors); else{ //Must be combined doc (new patient and encounter) where doc node is openmrs_data //<?xml version="1.0" encoding="UTF-8" ?> //<openmrs_data> // <patient/> // <form/> // <form/> // < etc.../> //</openmrs_data> NodeList list = doc.getDocumentElement().getChildNodes(); for(int index = 0; index < list.getLength(); index++){ Node node = list.item(index); if(node.getNodeType() != Node.ELEMENT_NODE) continue; //Assuming patient node is the first in the combined document, such that we get the patient id. if(DOMUtil.isPatientElementDoc((Element)node)){ patient = saveNewPatient((Element)node,getCreator(doc),propagateErrors,request); if(patient == null){ saveFormInError(xml,pathName, null); return; } } else{ setNewPatientId((Element)node,patient.getPatientId()); Document encounterDoc = createNewDocFromNode(db,(Element)node); xml = XformsUtil.doc2String(encounterDoc); submitXForm(encounterDoc,xml,pathName,false,propagateErrors); } } saveFormInArchive(xmlOriginal,pathName); } } catch (Exception e) { log.error(e.getMessage(), e); //If we created a new patient, remove them. XFRM-135 if (patient != null) Context.getPatientService().purgePatient(patient); //TODO Joaquin had a problem where there were errors but form was not saved in error folder //so lets enforce this for now regardless of the error flag //if(!propagateErrors) saveFormInError(xmlOriginal,pathName, null); //else throw e; } } private Document createNewDocFromNode(DocumentBuilder db, Element element){ Document doc = db.newDocument(); doc.appendChild(doc.adoptNode(element)); return doc; } /** * Sets the patientid node to the value of the patient_id as got from the server. * * @param root - the root element of the patientid node to set. * @return - true if set, else false. */ private void setNewPatientId(Element root, Integer patientId){ /*try{ NodeList elemList = root.getElementsByTagName(XformBuilder.NODE_PATIENT_PATIENT_ID); if (!(elemList != null && elemList.getLength() > 0)) return; elemList.item(0).setTextContent(patientId.toString()); } catch(Exception e){ log.error(e.getMessage(),e); }*/ setNodeValue(root, XformBuilder.NODE_PATIENT_PATIENT_ID, patientId.toString()); } private void setNodeValue(Element root, String name, String value){ try{ NodeList elemList = root.getElementsByTagName(name); if (!(elemList != null && elemList.getLength() > 0)) return; elemList.item(0).setTextContent(value); } catch(Exception e){ log.error(e.getMessage(),e); } } private boolean isRemoteFormEntry(){ return Context.getAdministrationService().getGlobalProperty("xforms.isRemoteFormEntry","false").equals("true"); } /** * Submits a form to the form entry queue for further processing. * * @param doc * @param xml * @param pathName * @param archive */ private void submitXForm(Document doc, String xml, String pathName, boolean archive, boolean propagateErrors) throws Exception { String xmlOriginal = xml; try{ fillPatientIdIfMissing(doc); saveComplexObs(doc,true); setMultipleSelectValues(doc.getDocumentElement()); xml = XformsUtil.doc2String(doc); if(isRemoteFormEntry()){ FormEntryWrapper.createFormEntryQueue(xml); } else{ processDoc(xml, pathName, propagateErrors); String patientid = DOMUtil.getElementValue(doc, XformBuilder.NODE_PATIENT_PATIENT_ID); Patient patient = XformObsPatientEdit.updatePatientDemographics(patientid, xml); RelativeSubmission.submit(xml, patient); if(archive) saveFormInArchive(xmlOriginal, pathName); } } catch (Exception e) { log.error(e.getMessage(), e); //TODO Joaquin had a problem where there were errors but form was not saved in error folder //so lets enforce this for now regardless of the error flag //if(!propagateErrors) saveFormInError(xmlOriginal,pathName, e); //else throw e; } } private void processDoc(String xml, String pathName, boolean propagateErrors) throws Exception { FormEntryQueue formEntryQueue = new FormEntryQueue(); formEntryQueue.setCreator(Context.getAuthenticatedUser()); formEntryQueue.setDateCreated(new Date()); formEntryQueue.setFormData(xml); formEntryQueue.setFileSystemUrl(pathName); HL7InQueue hl7InQueue = formEntryProcessor.transformFormEntryQueue(formEntryQueue, propagateErrors); hl7Processor.processHL7InQueue(hl7InQueue,propagateErrors); } /** * Archives a submitted form after processing. * * @param xml - the form data. * @param folder - the folder to save in. * @param queuePathName - the path and name of this file in the queue. If you dont supply this, * a new radom file is created in this folder, else the a file with the * same name as the queued one is created in this folder. */ private String saveForm(String xml,File folder,String queuePathName){ String pathName;// = folder.getAbsolutePath()+File.separatorChar+XformsUtil.getRandomFileName()+XformConstants.XML_FILE_EXTENSION; if(queuePathName == null) pathName = OpenmrsUtil.getOutFile(folder, new Date(), Context.getAuthenticatedUser()).getAbsolutePath(); else pathName = folder.getAbsolutePath()+File.separatorChar+queuePathName.substring(queuePathName.lastIndexOf(File.separatorChar)+1); try{ FileWriter writter = new FileWriter(pathName, false); writter.write(xml); writter.close(); if(queuePathName != null){ try{ File file = new File(queuePathName); if(!file.delete()) file.deleteOnExit(); }catch(Exception e){ log.error(e.getMessage(),e); } } } catch(Exception e){ log.error(e.getMessage(),e); } return pathName; } /** * Saves an xform in the xforms archive. * * @param xml - the xml of the xform. * @param queuePathName - the queue full path and file name of this xform. * @return - the archive full path and file name. */ private String saveFormInArchive(String xml,String queuePathName){ return saveForm(xml,XformsUtil.getXformsArchiveDir(new Date()),queuePathName); } /** * Saves an xform in the errors folder. * * @param xml - the xml of the xform. * @param queuePathName - the queue full path and file name of this xform. * @param exception TODO * @return - the error full path and file name. */ private String saveFormInError(String xml,String queuePathName, Exception exception){ String errorPath = saveForm(xml,XformsUtil.getXformsErrorDir(),queuePathName); Context.getService(XformsService.class).sendStacktraceToAdminByEmail(Context.getMessageSourceService().getMessage("xforms.problemFailedProcessForm") + errorPath, exception); return errorPath; } /** * Creates a new patient from an xform create new patient document. * * @param doc - the document. * @param creator - the logged on user. * @return - true if the patient is created successfully, else false. */ private Patient saveNewPatient(Element root, User creator, boolean propagateErrors, HttpServletRequest request) throws Exception{ PatientService patientService = Context.getPatientService(); XformsService xformsService = (XformsService)Context.getService(XformsService.class); Patient pt = new Patient(); pt.setCreator(creator); pt.setDateCreated(new Date()); PersonName pn = new PersonName(); pn.setGivenName(DOMUtil.getElementValue(root,XformBuilder.NODE_GIVEN_NAME)); pn.setFamilyName(DOMUtil.getElementValue(root,XformBuilder.NODE_FAMILY_NAME)); pn.setMiddleName(DOMUtil.getElementValue(root,XformBuilder.NODE_MIDDLE_NAME)); pn.setDegree(DOMUtil.getElementValue(root, XformBuilder.NODE_DEGREE)); pn.setFamilyName2(DOMUtil.getElementValue(root, XformBuilder.NODE_FAMILY_NAME2)); pn.setFamilyNamePrefix(DOMUtil.getElementValue(root, XformBuilder.NODE_FAMILY_NAME_PREFIX)); pn.setFamilyNameSuffix(DOMUtil.getElementValue(root, XformBuilder.NODE_FAMILY_NAME_SUFFIX)); pn.setPrefix(DOMUtil.getElementValue(root, XformBuilder.NODE_PREFIX)); pn.setPreferred(true); pn.setCreator(creator); pn.setDateCreated(pt.getDateCreated()); pt.addName(pn); pt.setBirthdateEstimated("true".equals(DOMUtil.getElementValue(root, XformBuilder.NODE_BIRTH_DATE_ESTIMATED))); String val = DOMUtil.getElementValue(root,XformBuilder.NODE_BIRTH_DATE); if(val != null && val.length() > 0) try{ pt.setBirthdate(XformsUtil.fromSubmitString2Date(val)); } catch(Exception e){log.error(val,e); } pt.setGender(DOMUtil.getElementValue(root,XformBuilder.NODE_GENDER)); PatientIdentifier identifier = new PatientIdentifier(); identifier.setCreator(creator); identifier.setDateCreated(pt.getDateCreated()); identifier.setIdentifier(DOMUtil.getElementValue(root,XformBuilder.NODE_IDENTIFIER)); int id = Integer.parseInt(DOMUtil.getElementValue(root,XformBuilder.NODE_IDENTIFIER_TYPE_ID)); PatientIdentifierType identifierType = patientService.getPatientIdentifierType(id); identifier.setIdentifierType(identifierType); identifier.setLocation(getLocation(DOMUtil.getElementValue(root,XformBuilder.NODE_LOCATION_ID))); identifier.setPreferred(true); pt.addIdentifier(identifier); addPersonAttributes(pt, root, xformsService, creator); addPersonAddresses(pt, root, creator); addOtherIdentifiers(pt, root, creator, patientService, propagateErrors, request); Patient pt2 = patientService.identifierInUse(identifier.getIdentifier(),identifier.getIdentifierType(),pt); if(pt2 == null){ pt = patientService.savePatient(pt); addPersonRepeatAttributes(pt,root,xformsService); if(request != null) request.setAttribute(XformConstants.REQUEST_ATTRIBUTE_ID_PATIENT_ID, pt.getPatientId().toString()); return pt; } else if(rejectExistingPatientCreation()){ String message = Context.getMessageSourceService().getMessage("xforms.problemPatientExists")+identifier.getIdentifier()+Context.getMessageSourceService().getMessage("xforms.accepted"); log.error(message); if(request != null) request.setAttribute(XformConstants.REQUEST_ATTRIBUTE_ID_ERROR_MESSAGE, message); if(propagateErrors) throw new Exception(message); return null; } else{ String message = Context.getMessageSourceService().getMessage("xforms.problemPatientExists")+identifier.getIdentifier()+Context.getMessageSourceService().getMessage("xforms.accepted"); log.warn(message); if(request != null) request.setAttribute(XformConstants.REQUEST_ATTRIBUTE_ID_ERROR_MESSAGE, message); if(propagateErrors) throw new Exception(message); return pt; } } private void addPersonAttributes(Patient pt, Element root,XformsService xformsService, User creator) throws Exception{ //First translate complex obs to file pointers; saveComplexObs(root.getOwnerDocument(),false); // look for person attributes in the xml doc and save to person List<PersonAttributeType> personAttributeTypes = Context.getPersonService().getPersonAttributeTypes(PERSON_TYPE.PERSON, null); for (PersonAttributeType type : personAttributeTypes) { NodeList nodes = root.getElementsByTagName("person_attribute"+type.getPersonAttributeTypeId()); if(nodes == null || nodes.getLength() == 0) continue; String value = ((Element)nodes.item(0)).getTextContent(); if(value == null || value.length() == 0) continue; PersonAttribute pa = new PersonAttribute(type, value); pa.setCreator(creator); pa.setDateCreated(pt.getDateCreated()); pt.addAttribute(pa); } } private void addPersonAddresses(Patient pt, Element root, User creator) throws Exception{ PersonAddress pa = new PersonAddress(); pa.setCreator(creator); pa.setDateCreated(pt.getDateCreated()); pa.setPreferred(true); addPersonAddressValue(XformBuilder.NODE_NAME_ADDRESS1, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_ADDRESS2, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_CITY_VILLAGE, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_STATE_PROVINCE, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_POSTAL_CODE, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_COUNTRY, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_LATITUDE, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_LONGITUDE, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_COUNTY_DISTRICT, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_NEIGHBORHOOD_CELL, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_REGION, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_SUBREGION, pa, root); addPersonAddressValue(XformBuilder.NODE_NAME_TOWNSHIP_DIVISION, pa, root); pt.addAddress(pa); } private void addPersonAddressValue(String name, PersonAddress pa, Element root) throws Exception{ NodeList nodes = root.getElementsByTagName(XformBuilder.NODE_NAME_PREFIX_PERSON_ADDRESS + name); if(nodes == null || nodes.getLength() == 0) return; String value = ((Element)nodes.item(0)).getTextContent(); if(value == null || value.length() == 0) return; if(name.equals(XformBuilder.NODE_NAME_ADDRESS1)) pa.setAddress1(value); else if(name.equals(XformBuilder.NODE_NAME_ADDRESS2)) pa.setAddress2(value); else if(name.equals(XformBuilder.NODE_NAME_CITY_VILLAGE)) pa.setCityVillage(value); else if(name.equals(XformBuilder.NODE_NAME_STATE_PROVINCE)) pa.setStateProvince(value); else if(name.equals(XformBuilder.NODE_NAME_POSTAL_CODE)) pa.setPostalCode(value); else if(name.equals(XformBuilder.NODE_NAME_COUNTRY)) pa.setCountry(value); else if(name.equals(XformBuilder.NODE_NAME_LATITUDE)) pa.setLatitude(value); else if(name.equals(XformBuilder.NODE_NAME_LONGITUDE)) pa.setLongitude(value); else if(name.equals(XformBuilder.NODE_NAME_COUNTY_DISTRICT)) pa.setCountyDistrict(value); else if(name.equals(XformBuilder.NODE_NAME_NEIGHBORHOOD_CELL)) pa.setNeighborhoodCell(value); else if(name.equals(XformBuilder.NODE_NAME_REGION)) pa.setRegion(value); else if(name.equals(XformBuilder.NODE_NAME_SUBREGION)) pa.setSubregion(value); else if(name.equals(XformBuilder.NODE_NAME_TOWNSHIP_DIVISION)) pa.setTownshipDivision(value); } private void addPersonRepeatAttributes(Patient pt, Element root,XformsService xformsService){ NodeList nodes = root.getChildNodes(); if(nodes == null) return; for(int index = 0; index < nodes.getLength(); index++){ Node node = nodes.item(index); if(node.getNodeType() != Node.ELEMENT_NODE) continue; String name = node.getNodeName(); if(name.startsWith("person_attribute_repeat_section")){ String attributeId = name.substring("person_attribute_repeat_section".length()); addPersonRepeatAttribute(pt,node,attributeId,xformsService); } } } private void addPersonRepeatAttribute(Patient pt,Node repeatNode,String attributeId,XformsService xformsService){ NodeList nodes = repeatNode.getChildNodes(); if(repeatNode == null) return; for(int index = 0; index < nodes.getLength(); index++){ Node node = nodes.item(index); if(node.getNodeType() != Node.ELEMENT_NODE) continue; String name = node.getNodeName(); if(name.startsWith("person_attribute")) addPersonRepeatAttributeValues(pt,node,attributeId,xformsService,index+1); } } private void addPersonRepeatAttributeValues(Patient pt,Node repeatNode,String attributeId,XformsService xformsService, int displayOrder){ if(repeatNode == null) return; NodeList nodes = repeatNode.getChildNodes(); for(int index = 0; index < nodes.getLength(); index++){ Node node = nodes.item(index); if(node.getNodeType() != Node.ELEMENT_NODE) continue; String name = node.getNodeName(); if(name.startsWith("person_attribute_concept")){ String valueId = name.substring("person_attribute_concept".length()); PersonRepeatAttribute personRepeatAttribute = new PersonRepeatAttribute(); personRepeatAttribute.setPersonId(pt.getPersonId()); personRepeatAttribute.setCreator(Context.getAuthenticatedUser().getUserId()); personRepeatAttribute.setDateCreated(pt.getDateCreated()); personRepeatAttribute.setValue(node.getTextContent()); personRepeatAttribute.setValueId(Integer.parseInt(valueId)); personRepeatAttribute.setValueIdType(PersonRepeatAttribute.VALUE_ID_TYPE_CONCEPT); personRepeatAttribute.setValueDisplayOrder(displayOrder); personRepeatAttribute.setAttributeTypeId(Integer.parseInt(attributeId)); xformsService.savePersonRepeatAttribute(personRepeatAttribute); } } } /** * Check if we are to reject forms for patients considered new when they already exist, * by virture of patient identifier. * @return true if we are to reject, else false. */ private boolean rejectExistingPatientCreation(){ String reject = Context.getAdministrationService().getGlobalProperty(XformConstants.GLOBAL_PROP_KEY_REJECT_EXIST_PATIENT_CREATE,XformConstants.DEFAULT_REJECT_EXIST_PATIENT_CREATE); return !("false".equalsIgnoreCase(reject)); } /** * Gets a location object given a locaton id * * @param locationId - the id. * @return */ private Location getLocation(String locationId){ return Context.getLocationService().getLocation(Integer.parseInt(locationId)); } private User getCreator(Document doc){ //return Context.getAuthenticatedUser(); NodeList elemList = doc.getElementsByTagName(XformConstants.NODE_ENTERER); if (elemList != null && elemList.getLength() > 0) { String s = ((Element)elemList.item(0)).getTextContent(); User user = Context.getUserService().getUser(Integer.valueOf(s.substring(0,s.indexOf('^')))); return user; } return null; } /** * Converts xforms multiple select answer values to the format expected by * the openmrs form model. * * @param parentNode - the parent node of the document. */ private void setMultipleSelectValues(Node parentNode){ NodeList nodes = parentNode.getChildNodes(); for(int i=0; i<nodes.getLength(); i++){ Node node = nodes.item(i); if(node.getNodeType() != Node.ELEMENT_NODE) continue; if(isMultipleSelectNode(node)) setMultipleSelectNodeValues(node); setMultipleSelectValues(node); } } /** * Gets the values of a multiple select node. * * @param parentNode- the node * @return - a sting with values separated by space. */ private String getMultipleSelectNodeValue(Node parentNode){ String value = null; NodeList nodes = parentNode.getChildNodes(); for(int i=0; i<nodes.getLength(); i++){ Node node = nodes.item(i); if(node.getNodeType() != Node.ELEMENT_NODE) continue; String name = node.getNodeName(); if(name != null && name.equalsIgnoreCase(XformBuilder.NODE_XFORMS_VALUE)){ value = node.getTextContent(); parentNode.removeChild(node); break; } } return value; } /** * Sets the values of an openmrs multiple select node. * * @param parentNode - the node. */ private void setMultipleSelectNodeValues(Node parentNode){ String values = getMultipleSelectNodeValue(parentNode); if(values == null || values.length() == 0) return; String[] valueArray = values.split(XformBuilder.MULTIPLE_SELECT_VALUE_SEPARATOR); NodeList nodes = parentNode.getChildNodes(); for(int i=0; i<nodes.getLength(); i++){ Node node = nodes.item(i); if(node.getNodeType() != Node.ELEMENT_NODE) continue; String name = node.getNodeName(); if(name.equalsIgnoreCase(XformBuilder.NODE_DATE) || name.equalsIgnoreCase(XformBuilder.NODE_TIME) || name.equalsIgnoreCase(XformBuilder.NODE_VALUE) || name.equalsIgnoreCase(XformBuilder.NODE_XFORMS_VALUE)) continue; setMultipleSelectNodeValue(node,valueArray); } } /** * Sets the value of an openmrs multiple select node. * * @param node - the multiple select node. * @param valueArray - an array of selected values. */ private void setMultipleSelectNodeValue(Node node,String[] valueArray){ for(String value : valueArray){ if(!value.equalsIgnoreCase(node.getNodeName())) continue; node.setTextContent(XformBuilder.VALUE_TRUE); return; } node.setTextContent(XformBuilder.VALUE_FALSE); } /** * Checks if a node is multiple select. * * @param node - the node to check. * @return - true if it is a multiple select node, else false. */ private boolean isMultipleSelectNode(Node node){ boolean multipSelect = false; NamedNodeMap attributes = node.getAttributes(); if(attributes != null){ Node multipleValue = attributes.getNamedItem(XformBuilder.ATTRIBUTE_MULTIPLE); if(attributes.getNamedItem(XformBuilder.ATTRIBUTE_OPENMRS_CONCEPT) != null && multipleValue != null && multipleValue.getNodeValue().equals("1")) multipSelect = true; } return multipSelect; } private void fillPatientIdIfMissing(Document doc) throws Exception{ String patientid = DOMUtil.getElementValue(doc,XformBuilder.NODE_PATIENT_PATIENT_ID); if(patientid != null && patientid.trim().length() > 0) return; //patient id is properly filled. may need to check if the patient exists //Check if patient identifier is filled. String patientIdentifier = getPatientIdentifier(doc);; if(patientIdentifier == null || patientIdentifier.trim().length() == 0) throw new Exception(Context.getMessageSourceService().getMessage(".expectedPatientID")); List<Patient> patients = Context.getPatientService().getPatients(null, patientIdentifier, null); if(patients != null && patients.size() > 1) throw new Exception(Context.getMessageSourceService().getMessage("xformsmoreThanOneID") + patientIdentifier); if(patients != null && patients.size() == 1){ DOMUtil.setElementValue(doc.getDocumentElement(), XformBuilder.NODE_PATIENT_PATIENT_ID, patients.get(0).getPatientId().toString()); return; } //Check if patient identifier type is filled String identifierType = DOMUtil.getElementValue(doc, XformBuilder.NODE_PATIENT_IDENTIFIER_TYPE); if(identifierType == null || identifierType.trim().length() == 0){ identifierType = DOMUtil.getElementValue(doc, XformBuilder.NODE_PATIENT_IDENTIFIER_TYPE_ID); if(identifierType == null || identifierType.trim().length() == 0){ identifierType = Context.getAdministrationService().getGlobalProperty("xforms.new_patient_identifier_type_id", null); if(identifierType == null || identifierType.trim().length() == 0) throw new Exception(Context.getMessageSourceService().getMessage("xforms.expectedPatientIDType")); } } //Check if family name is filled. String familyName = DOMUtil.getElementValue(doc, XformBuilder.NODE_PATIENT_FAMILY_NAME); if(familyName == null || familyName.trim().length() == 0) throw new Exception(Context.getMessageSourceService().getMessage("xforms.expectedFamilyName")); //Check if gender is filled String gender = DOMUtil.getElementValue(doc, XformBuilder.NODE_PATIENT_GENDER); if(gender == null || gender.trim().length() == 0) throw new Exception(Context.getMessageSourceService().getMessage("xforms.expectedGender")); //Check if birth date is filled String birthDate = DOMUtil.getElementValue(doc, XformBuilder.NODE_PATIENT_BIRTH_DATE); if(birthDate == null || birthDate.trim().length() == 0) throw new Exception(Context.getMessageSourceService().getMessage("xforms.expectedBirthdate")); Patient patient = new Patient(); patient.setCreator(getCreator(doc)); patient.setDateCreated(new Date()); patient.setGender(gender); PersonName pn = new PersonName(); pn.setFamilyName(familyName); pn.setGivenName(DOMUtil.getElementValue(doc, XformBuilder.NODE_PATIENT_GIVEN_NAME)); pn.setMiddleName(DOMUtil.getElementValue(doc, XformBuilder.NODE_PATIENT_MIDDLE_NAME)); pn.setCreator(patient.getCreator()); pn.setDateCreated(patient.getDateCreated()); patient.addName(pn); PatientIdentifier identifier = new PatientIdentifier(); identifier.setCreator(patient.getCreator()); identifier.setDateCreated(patient.getDateCreated()); identifier.setIdentifier(patientIdentifier.toString()); int id = Integer.parseInt(identifierType); PatientIdentifierType idtfType = Context.getPatientService().getPatientIdentifierType(id); if(idtfType == null) throw new Exception(Context.getMessageSourceService().getMessage("xforms.expectedIdentifier")); identifier.setIdentifierType(idtfType); identifier.setLocation(getLocation(DOMUtil.getElementValue(doc, XformBuilder.NODE_ENCOUNTER_LOCATION_ID))); identifier.setPreferred(true); patient.addIdentifier(identifier); patient.setBirthdate(XformsUtil.fromSubmitString2Date(birthDate.toString())); patient.setBirthdateEstimated("true".equals(DOMUtil.getElementValue(doc, XformBuilder.NODE_PATIENT_BIRTH_DATE_ESTIMATED))); addPersonAttributes(patient, doc.getDocumentElement(), Context.getService(XformsService.class), patient.getCreator()); addPersonAddresses(patient, doc.getDocumentElement(), patient.getCreator()); Context.getPatientService().savePatient(patient); DOMUtil.setElementValue(doc.getDocumentElement(), XformBuilder.NODE_PATIENT_PATIENT_ID, patient.getPatientId().toString()); //TODO May need to call addPersonRepeatAttributes(pt,root,xformsService); } private String getPatientIdentifier(Document doc){ NodeList elemList = doc.getDocumentElement().getElementsByTagName("patient"); if (!(elemList != null && elemList.getLength() > 0)) return null; Element patientNode = (Element)elemList.item(0); NodeList children = patientNode.getChildNodes(); int len = patientNode.getChildNodes().getLength(); for(int index=0; index<len; index++){ Node child = children.item(index); if(child.getNodeType() != Node.ELEMENT_NODE) continue; if("patient_identifier".equalsIgnoreCase(((Element)child).getAttribute("openmrs_table")) && "identifier".equalsIgnoreCase(((Element)child).getAttribute("openmrs_attribute"))) return child.getTextContent(); } return null; } private void saveComplexObs(Document doc, boolean useValueNode) throws Exception { List<String> names = DOMUtil.getModelComplexObsNodeNames(doc.getDocumentElement().getAttribute("id")); for(String name : names) saveComplexObsValue(DOMUtil.getElement(doc, name),useValueNode); } private void saveComplexObsValue(Element element, boolean useValueNode) throws Exception { String value = null; if(useValueNode) value = DOMUtil.getElementValue(element, "value"); else value = element.getTextContent(); if(value == null || value.trim().length() == 0) return; byte[] bytes = Base64.decode(value); String path = element.getOwnerDocument().getDocumentElement().getAttribute("name"); path += File.separatorChar + element.getNodeName(); File file = OpenmrsUtil.getOutFile(XformsUtil.getXformsComplexObsDir(path), new Date(), Context.getAuthenticatedUser()); FileOutputStream writter = new FileOutputStream(file); writter.write(bytes); writter.close(); if(useValueNode) DOMUtil.setElementValue(element, "value", file.getAbsolutePath()); else element.setTextContent(file.getAbsolutePath()); //System.out.println("complex obs value = " + file.getAbsolutePath()); } private void addOtherIdentifiers(Patient pt, Element root, User creator, PatientService patientService, boolean propagateErrors, HttpServletRequest request) throws Exception{ NodeList nodes = root.getElementsByTagName(XformBuilder.NODE_NAME_OTHER_IDENTIFIERS); if(nodes == null || nodes.getLength() == 0) return; for(int index = 0; index < nodes.getLength(); index++){ addOtherIdentifier((Element)nodes.item(index), creator, pt, patientService, propagateErrors, request); } } private void addOtherIdentifier(Element root, User creator, Patient pt, PatientService patientService, boolean propagateErrors, HttpServletRequest request) throws Exception { //Look for identifier value. NodeList nodes = root.getElementsByTagName(XformBuilder.NODE_NAME_OTHER_IDENTIFIER); if(nodes == null || nodes.getLength() == 0) return; //no identifier node found, possibly deleted. String identifierValue = nodes.item(0).getTextContent(); if(identifierValue == null || identifierValue.trim().length() == 0) return; //no identifier value found. //Look for identifier type id nodes = root.getElementsByTagName(XformBuilder.NODE_NAME_OTHER_IDENTIFIER_TYPE_ID); if(nodes == null || nodes.getLength() == 0) reportError(Context.getMessageSourceService().getMessage("xforms.formShouldHaveID") + identifierValue, propagateErrors, request); String identifierTypeId = nodes.item(0).getTextContent(); if(identifierTypeId == null || identifierTypeId.trim().length() == 0) reportError(Context.getMessageSourceService().getMessage("xforms.selectIdentifierType") + identifierValue, propagateErrors, request); //Look for identifier location id nodes = root.getElementsByTagName(XformBuilder.NODE_NAME_OTHER_IDENTIFIER_LOCATION_ID); if(nodes == null || nodes.getLength() == 0) reportError(Context.getMessageSourceService().getMessage("xforms.shouldHaveLocationType") + identifierValue, propagateErrors, request); String identifierLocationId = nodes.item(0).getTextContent(); if(identifierLocationId == null || identifierLocationId.trim().length() == 0) reportError(Context.getMessageSourceService().getMessage("xforms.selectLocationType") + identifierValue, propagateErrors, request); //Now try add the identifier. PatientIdentifier identifier = new PatientIdentifier(); identifier.setCreator(creator); identifier.setDateCreated(pt.getDateCreated()); identifier.setIdentifier(identifierValue); PatientIdentifierType identifierType = patientService.getPatientIdentifierType(Integer.parseInt(identifierTypeId)); identifier.setIdentifierType(identifierType); identifier.setLocation(getLocation(identifierLocationId)); identifier.setPreferred(false); for(PatientIdentifier existingIdentifier : pt.getIdentifiers()){ if(existingIdentifier.getIdentifierType() == identifierType){ reportError(Context.getMessageSourceService().getMessage("xforms.patientHasIdentifier") + identifierType.getName(), propagateErrors, request); } } pt.addIdentifier(identifier); Patient pt2 = patientService.identifierInUse(identifier.getIdentifier(),identifier.getIdentifierType(), pt); if(pt2 != null){ reportError(Context.getMessageSourceService().getMessage("xforms.triedCreatePatientAlreadyExists") + identifier.getIdentifier(), propagateErrors, request); } } private void reportError(String message, boolean propagateErrors, HttpServletRequest request) throws Exception { log.error(message); if(request != null) request.setAttribute(XformConstants.REQUEST_ATTRIBUTE_ID_ERROR_MESSAGE, message); if(propagateErrors) throw new Exception(message); } }