org.openmrs.module.muzima.handler.XmlEncounterQueueDataHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.muzima.handler.XmlEncounterQueueDataHandler.java

Source

/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * 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.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.module.muzima.handler;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Concept;
import org.openmrs.Encounter;
import org.openmrs.EncounterType;
import org.openmrs.Form;
import org.openmrs.Location;
import org.openmrs.Obs;
import org.openmrs.Patient;
import org.openmrs.PatientIdentifier;
import org.openmrs.PatientIdentifierType;
import org.openmrs.PersonName;
import org.openmrs.User;
import org.openmrs.annotation.Handler;
import org.openmrs.api.context.Context;
import org.openmrs.module.muzima.api.service.MuzimaFormService;
import org.openmrs.module.muzima.api.service.RegistrationDataService;
import org.openmrs.module.muzima.exception.QueueProcessorException;
import org.openmrs.module.muzima.model.MuzimaForm;
import org.openmrs.module.muzima.model.QueueData;
import org.openmrs.module.muzima.model.RegistrationData;
import org.openmrs.module.muzima.model.handler.QueueDataHandler;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 */
@Component
@Handler(supports = QueueData.class, order = 2)
public class XmlEncounterQueueDataHandler implements QueueDataHandler {

    private static final String DISCRIMINATOR_VALUE = "xml-encounter";

    private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    private final Log log = LogFactory.getLog(XmlEncounterQueueDataHandler.class);

    private QueueProcessorException queueProcessorException;

    private Encounter encounter;

    @Override
    public void process(final QueueData queueData) throws QueueProcessorException {

        log.info("Processing registration form data: " + queueData.getUuid());
        encounter = new Encounter();
        try {
            if (validate(queueData)) {
                Context.getEncounterService().saveEncounter(encounter);
            }
        } catch (Exception e) {
            if (!e.getClass().equals(QueueProcessorException.class)) {
                queueProcessorException.addException(e);
            }
        } finally {
            if (queueProcessorException.anyExceptions()) {
                throw queueProcessorException;
            }
        }

    }

    @Override
    public boolean validate(QueueData queueData) {

        log.info("Processing encounter form data: " + queueData.getUuid());
        queueProcessorException = new QueueProcessorException();

        String payload = queueData.getPayload();

        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document document = db.parse(new InputSource(new ByteArrayInputStream(payload.getBytes("utf-8"))));

            Element element = document.getDocumentElement();
            element.normalize();

            // we need to get the form id to get the encounter type associated with this form from the form record.
            encounter.setEncounterType(Context.getEncounterService().getEncounterType(1));

            processPatient(encounter, document.getElementsByTagName("patient"));
            processEncounter(encounter, document.getElementsByTagName("encounter"));
            processObs(encounter, document.getElementsByTagName("obs"));
            return true;

        } catch (Exception e) {
            queueProcessorException.addException(e);
            return false;
        } finally {
            if (queueProcessorException.anyExceptions()) {
                throw queueProcessorException;
            }
        }
    }

    @Override
    public String getDiscriminator() {
        return DISCRIMINATOR_VALUE;
    }

    private void processPatient(final Encounter encounter, final NodeList patientNodeList)
            throws QueueProcessorException {
        Node patientNode = patientNodeList.item(0);
        NodeList patientElementNodes = patientNode.getChildNodes();

        Patient unsavedPatient = new Patient();
        PersonName personName = new PersonName();
        PatientIdentifier patientIdentifier = new PatientIdentifier();
        for (int i = 0; i < patientElementNodes.getLength(); i++) {
            Node patientElementNode = patientElementNodes.item(i);
            if (patientElementNode.getNodeType() == Node.ELEMENT_NODE) {
                Element patientElement = (Element) patientElementNode;
                if (patientElement.getTagName().equals("patient.middle_name")) {
                    personName.setMiddleName(patientElement.getTextContent());
                } else if (patientElement.getTagName().equals("patient.given_name")) {
                    personName.setGivenName(patientElement.getTextContent());
                } else if (patientElement.getTagName().equals("patient.family_name")) {
                    personName.setFamilyName(patientElement.getTextContent());
                } else if (patientElement.getTagName().equals("patient_identifier.identifier_type_id")) {
                    int identifierTypeId = Integer.parseInt(patientElement.getTextContent());
                    PatientIdentifierType identifierType = Context.getPatientService()
                            .getPatientIdentifierType(identifierTypeId);
                    patientIdentifier.setIdentifierType(identifierType);
                } else if (patientElement.getTagName().equals("patient.medical_record_number")) {
                    patientIdentifier.setIdentifier(patientElement.getTextContent());
                } else if (patientElement.getTagName().equals("patient.sex")) {
                    unsavedPatient.setGender(patientElement.getTextContent());
                } else if (patientElement.getTagName().equals("patient.birthdate")) {
                    Date dob = parseDate(patientElement.getTextContent());
                    unsavedPatient.setBirthdate(dob);
                } else if (patientElement.getTagName().equals("patient.uuid")) {
                    unsavedPatient.setUuid(patientElement.getTextContent());
                }
            }
        }

        unsavedPatient.addName(personName);
        unsavedPatient.addIdentifier(patientIdentifier);

        Patient candidatePatient;
        if (StringUtils.isNotEmpty(unsavedPatient.getUuid())) {
            candidatePatient = Context.getPatientService().getPatientByUuid(unsavedPatient.getUuid());
            if (candidatePatient == null) {
                String temporaryUuid = unsavedPatient.getUuid();
                RegistrationDataService dataService = Context.getService(RegistrationDataService.class);
                RegistrationData registrationData = dataService.getRegistrationDataByTemporaryUuid(temporaryUuid);
                candidatePatient = Context.getPatientService().getPatientByUuid(registrationData.getAssignedUuid());
            }
        } else if (!StringUtils.isBlank(patientIdentifier.getIdentifier())) {
            List<Patient> patients = Context.getPatientService().getPatients(patientIdentifier.getIdentifier());
            candidatePatient = findPatient(patients, unsavedPatient);
        } else {
            List<Patient> patients = Context.getPatientService()
                    .getPatients(unsavedPatient.getPersonName().getFullName());
            candidatePatient = findPatient(patients, unsavedPatient);
        }

        if (candidatePatient == null) {
            queueProcessorException
                    .addException(new Exception("Unable to uniquely identify patient for this encounter form data. "
                            + ToStringBuilder.reflectionToString(unsavedPatient)));
        }

        encounter.setPatient(candidatePatient);
    }

    private Patient findPatient(final List<Patient> patients, final Patient unsavedPatient) {
        String unsavedGivenName = unsavedPatient.getGivenName();
        String unsavedFamilyName = unsavedPatient.getFamilyName();
        PersonName unsavedPersonName = unsavedPatient.getPersonName();
        for (Patient patient : patients) {
            // match it using the person name and gender, what about the dob?
            PersonName savedPersonName = patient.getPersonName();
            if (StringUtils.isNotBlank(savedPersonName.getFullName())
                    && StringUtils.isNotBlank(unsavedPersonName.getFullName())) {
                String savedGivenName = savedPersonName.getGivenName();
                int givenNameEditDistance = StringUtils.getLevenshteinDistance(
                        StringUtils.lowerCase(savedGivenName), StringUtils.lowerCase(unsavedGivenName));
                String savedFamilyName = savedPersonName.getFamilyName();
                int familyNameEditDistance = StringUtils.getLevenshteinDistance(
                        StringUtils.lowerCase(savedFamilyName), StringUtils.lowerCase(unsavedFamilyName));
                if (givenNameEditDistance < 3 && familyNameEditDistance < 3) {
                    if (StringUtils.equalsIgnoreCase(patient.getGender(), unsavedPatient.getGender())) {
                        if (patient.getBirthdate() != null && unsavedPatient.getBirthdate() != null
                                && DateUtils.isSameDay(patient.getBirthdate(), unsavedPatient.getBirthdate())) {
                            return patient;
                        }
                    }
                }
            }
        }
        return null;
    }

    private Node findSubNode(final String name, final Node node) {
        if (!node.hasChildNodes()) {
            return null;
        }
        NodeList list = node.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            Node subNode = list.item(i);
            if (subNode.getNodeType() == Node.ELEMENT_NODE) {
                if (subNode.getNodeName().equals(name))
                    return subNode;
            }
        }
        return null;
    }

    private void processObs(final Encounter encounter, final NodeList obsNodeList) throws QueueProcessorException {
        Node obsNode = obsNodeList.item(0);
        NodeList obsElementNodes = obsNode.getChildNodes();
        for (int i = 0; i < obsElementNodes.getLength(); i++) {
            Node obsElementNode = obsElementNodes.item(i);
            // skip all top level obs nodes without child element or without attribute
            // no attribute: temporary elements
            // no child: element with no answer
            if (obsElementNode.hasAttributes() && obsElementNode.hasChildNodes()) {
                processObsNode(encounter, null, obsElementNode);
            }
        }
    }

    private void processObsNode(final Encounter encounter, final Obs parentObs, final Node obsElementNode) {
        Element obsElement = (Element) obsElementNode;
        String[] conceptElements = StringUtils.split(obsElement.getAttribute("concept"), "\\^");
        int conceptId = Integer.parseInt(conceptElements[0]);
        Concept concept = Context.getConceptService().getConcept(conceptId);

        if (concept == null) {
            log.info("Skipping obs creation, " + obsElement.getAttribute("concept")
                    + " is not valid or not available.");
            return;
        }

        if (concept.isSet()) {
            Obs obsGroup = new Obs();
            obsGroup.setConcept(concept);
            obsGroup.setCreator(encounter.getCreator());
            NodeList nodeList = obsElementNode.getChildNodes();
            for (int i = 0; i < nodeList.getLength(); i++) {
                Node subNode = nodeList.item(i);
                // only process sub node with attribute and it is a tag
                if (subNode.hasAttributes() && subNode.getNodeType() == Node.ELEMENT_NODE) {
                    // need to do recursive because we might have nested sets structure
                    encounter.addObs(obsGroup);
                    processObsNode(encounter, obsGroup, subNode);
                }
            }
        } else {
            Node valueNode = findSubNode("value", obsElementNode);
            if (valueNode != null) {
                String value = StringUtils.trim(valueNode.getTextContent());
                if (StringUtils.isNotEmpty(value)) {
                    Obs obs = new Obs();
                    obs.setConcept(concept);
                    obs.setEncounter(encounter);
                    obs.setPerson(encounter.getPatient());
                    obs.setObsDatetime(encounter.getEncounterDatetime());
                    obs.setLocation(encounter.getLocation());
                    obs.setCreator(encounter.getCreator());
                    // find the obs value :)
                    if (concept.getDatatype().isNumeric()) {
                        obs.setValueNumeric(Double.parseDouble(value));
                    } else if (concept.getDatatype().isDate() || concept.getDatatype().isTime()
                            || concept.getDatatype().isDateTime()) {
                        obs.setValueDatetime(parseDate(value));
                    } else if (concept.getDatatype().isCoded()) {
                        String[] valueCodedElements = StringUtils.split(value, "\\^");
                        int valueCodedId = Integer.parseInt(valueCodedElements[0]);
                        Concept valueCoded = Context.getConceptService().getConcept(valueCodedId);
                        if (valueCoded == null) {
                            queueProcessorException.addException(new Exception(
                                    "Unable to find concept for value coded with id: " + valueCodedId));
                        }
                        obs.setValueCoded(valueCoded);
                    } else if (concept.getDatatype().isText()) {
                        obs.setValueText(value);
                    }
                    // only add if the value is not empty :)
                    encounter.addObs(obs);
                    if (parentObs != null) {
                        parentObs.addGroupMember(obs);
                    }
                }
            } else {
                Node xformValuesNode = findSubNode("xforms_value", obsElementNode);
                if (xformValuesNode != null) {
                    String[] xformValues = StringUtils.split(StringUtils.trim(xformValuesNode.getTextContent()));
                    for (String xformValue : xformValues) {
                        Node xformValueNode = findSubNode(xformValue, obsElementNode);
                        if (xformValueNode != null && xformValueNode.hasAttributes()) {
                            Obs obs = new Obs();
                            obs.setConcept(concept);
                            obs.setEncounter(encounter);
                            obs.setPerson(encounter.getPatient());
                            obs.setObsDatetime(encounter.getEncounterDatetime());
                            obs.setLocation(encounter.getLocation());
                            obs.setCreator(encounter.getCreator());

                            Element xformValueElement = (Element) xformValueNode;
                            String[] valueCodedElements = StringUtils
                                    .split(xformValueElement.getAttribute("concept"), "\\^");
                            int valueCodedId = Integer.parseInt(valueCodedElements[0]);
                            Concept valueCoded = Context.getConceptService().getConcept(valueCodedId);
                            if (valueCoded == null) {
                                queueProcessorException.addException(new Exception(
                                        "Unable to find concept for value coded with id: " + valueCodedId));
                            }
                            obs.setValueCoded(valueCoded);

                            encounter.addObs(obs);
                            if (parentObs != null) {
                                parentObs.addGroupMember(obs);
                            }
                        }
                    }
                }
            }
        }
    }

    private void processEncounter(final Encounter encounter, final NodeList encounterNodeList)
            throws QueueProcessorException {
        Node encounterNode = encounterNodeList.item(0);
        NodeList encounterElementNodes = encounterNode.getChildNodes();
        for (int i = 0; i < encounterElementNodes.getLength(); i++) {
            Node encounterElementNode = encounterElementNodes.item(i);
            if (encounterElementNode.getNodeType() == Node.ELEMENT_NODE) {
                Element encounterElement = (Element) encounterElementNode;
                String encounterElementValue = encounterElement.getTextContent();
                if (encounterElement.getTagName().equals("encounter.encounter_datetime")) {
                    Date date = parseDate(encounterElementValue);
                    encounter.setEncounterDatetime(date);
                } else if (encounterElement.getTagName().equals("encounter.location_id")) {
                    int locationId = NumberUtils.toInt(encounterElementValue, -999);
                    Location location = Context.getLocationService().getLocation(locationId);
                    if (location == null) {
                        queueProcessorException.addException(new Exception(
                                "Unable to find encounter location using the id: " + encounterElementValue));
                    }
                    encounter.setLocation(location);
                } else if (encounterElement.getTagName().equals("encounter.provider_id")) {
                    User user = Context.getUserService().getUserByUsername(encounterElementValue);
                    if (user == null) {
                        queueProcessorException.addException(
                                new Exception("Unable to find user using the id: " + encounterElementValue));
                    }
                    encounter.setProvider(user);
                    encounter.setCreator(user);
                } else if (encounterElement.getTagName().equals("encounter.form_uuid")) {
                    Form form = Context.getFormService().getFormByUuid(encounterElementValue);
                    if (form == null) {
                        MuzimaFormService muzimaFormService = Context.getService(MuzimaFormService.class);
                        MuzimaForm muzimaForm = muzimaFormService.getFormByUuid(encounterElementValue);
                        if (muzimaForm != null) {
                            Form formDefinition = Context.getFormService().getFormByUuid(muzimaForm.getForm());
                            encounter.setForm(formDefinition);
                            encounter.setEncounterType(formDefinition.getEncounterType());
                        } else {
                            log.info("Unable to find form using the uuid: " + encounterElementValue
                                    + ". Setting the form field to null!");
                        }
                    } else {
                        encounter.setForm(form);
                        encounter.setEncounterType(form.getEncounterType());
                    }
                } else if (encounterElement.getTagName().equals("encounter.encounter_type")) {
                    if (encounter.getEncounterType() == null) {
                        int encounterTypeId = NumberUtils.toInt(encounterElementValue, -999);
                        EncounterType encounterType = Context.getEncounterService()
                                .getEncounterType(encounterTypeId);
                        if (encounterType == null) {
                            queueProcessorException.addException(new Exception(
                                    "Unable to find encounter type using the id: " + encounterElementValue));
                        }
                        encounter.setEncounterType(encounterType);
                    }
                }
            }
        }
    }

    private Date parseDate(final String dateValue) {
        Date date = null;
        try {
            date = dateFormat.parse(dateValue);
        } catch (ParseException e) {
            log.error("Unable to parse date data for encounter!", e);
        }
        return date;
    }

    @Override
    public boolean accept(final QueueData queueData) {
        return StringUtils.equals(DISCRIMINATOR_VALUE, queueData.getDiscriminator());
    }
}