org.jembi.rhea.orchestration.SaveEncounterORU_R01ValidatorAndEnricher.java Source code

Java tutorial

Introduction

Here is the source code for org.jembi.rhea.orchestration.SaveEncounterORU_R01ValidatorAndEnricher.java

Source

/* 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/. */
package org.jembi.rhea.orchestration;

import java.io.IOException;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathException;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jembi.rhea.Constants;
import org.jembi.rhea.RestfulHttpRequest;
import org.jembi.rhea.RestfulHttpResponse;
import org.jembi.rhea.orchestration.exceptions.ClientValidationException;
import org.jembi.rhea.orchestration.exceptions.EncounterEnrichmentException;
import org.jembi.rhea.orchestration.exceptions.LocationValidationException;
import org.jembi.rhea.orchestration.exceptions.ProviderValidationException;
import org.mule.api.MuleContext;
import org.mule.api.MuleEventContext;
import org.mule.api.MuleException;
import org.mule.api.MuleMessage;
import org.mule.api.lifecycle.Callable;
import org.mule.module.client.MuleClient;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.DataTypeException;
import ca.uhn.hl7v2.model.Message;
import ca.uhn.hl7v2.model.v25.datatype.CX;
import ca.uhn.hl7v2.model.v25.message.ORU_R01;
import ca.uhn.hl7v2.model.v25.segment.PID;
import ca.uhn.hl7v2.model.v25.segment.PV1;
import ca.uhn.hl7v2.parser.GenericParser;
import ca.uhn.hl7v2.parser.Parser;
import ca.uhn.hl7v2.validation.impl.DefaultValidation;

public class SaveEncounterORU_R01ValidatorAndEnricher implements Callable {

    private Log log = LogFactory.getLog(this.getClass());

    private static boolean validateClient = true;
    private static boolean validateProvider = true;
    private static boolean validateLocation = true;
    private static boolean enrichClientDemographics = true;

    protected String validateAndEnrichORU_R01(RestfulHttpRequest request, MuleClient client)
            throws MuleException, EncounterEnrichmentException, ClientValidationException,
            ProviderValidationException, LocationValidationException {
        String ORU_R01_str = request.getBody();
        ORU_R01 oru_r01 = parseORU_R01(ORU_R01_str);

        String ecid = null;
        if (validateClient) {
            ecid = validateAndEnrichClient(client, oru_r01);
            request.setPath("ws/rest/v1/patient/" + Constants.ECID_ID_TYPE + "-" + ecid + "/encounters");
        }

        if (validateProvider) {
            validateAndEnrichProvider(client, oru_r01);
        }

        if (validateLocation) {
            validateAndEnrichLocation(client, oru_r01);
        }

        if (enrichClientDemographics) {
            enrichClientDemographics(client, oru_r01, ecid);
        }

        try {
            return new GenericParser().encode(oru_r01, "XML");
        } catch (HL7Exception ex) {
            throw new EncounterEnrichmentException(ex);
        }

    }

    protected ORU_R01 parseORU_R01(String ORU_R01_str) throws EncounterEnrichmentException {
        Parser parser = new GenericParser();
        DefaultValidation defaultValidation = new DefaultValidation();
        parser.setValidationContext(defaultValidation);

        Message msg;
        try {
            msg = parser.parse(ORU_R01_str);
        } catch (HL7Exception ex) {
            throw new EncounterEnrichmentException("Failed to parse HL7 ORU_R01 message", ex);
        }

        return (ORU_R01) msg;
    }

    /**
     * Validate the patient against the Client Registry and enrich the message with the patient's ECID.
     * Each of the patient's identifiers will validate until the first valid id is found.
     */
    protected String validateAndEnrichClient(MuleClient client, ORU_R01 oru_r01)
            throws ClientValidationException, MuleException, EncounterEnrichmentException {
        // Validate that one of the patient ID's is correct
        PID pid = oru_r01.getPATIENT_RESULT().getPATIENT().getPID();
        CX[] patientIdentifierList = pid.getPatientIdentifierList();

        String ecid = null;
        if (patientIdentifierList.length < 1) {
            throw new ClientValidationException("No patient identifiers found in ORU_R01 message");
        }

        for (int i = 0; i < patientIdentifierList.length; i++) {
            String id = patientIdentifierList[i].getIDNumber().getValue();
            String idType = patientIdentifierList[i].getIdentifierTypeCode().getValue();
            ecid = getECID(client, idType, id);

            if (ecid != null)
                break;
        }

        if (ecid == null) {
            throw new ClientValidationException("Invalid client ID");
        } else {
            // Enrich the message
            CX id = pid.getPatientIdentifierList(pid.getPatientIdentifierListReps());
            try {
                id.getIdentifierTypeCode().setValue("ECID");
                id.getIDNumber().setValue(ecid);
            } catch (DataTypeException ex) {
                throw new EncounterEnrichmentException(ex);
            }
        }

        return ecid;
    }

    private String getECID(MuleClient client, String idType, String id)
            throws ClientValidationException, MuleException {
        Map<String, String> idMap = new HashMap<String, String>();
        idMap.put("id", id);
        idMap.put("idType", idType);

        MuleMessage response = client.send("vm://getecid", idMap, null, 5000);

        String success = response.getInboundProperty("success");
        if (success != null && success.equals("true")) {
            try {
                return response.getPayloadAsString();
            } catch (Exception ex) {
                //argh! getPayloadAsString throws exception.. bad bad bad
                throw new ClientValidationException(ex);
            }
        }

        return null;
    }

    /**
     * Validate the Provider ID against the Provider Registry and enrich the message with the provider's EPID.
     */
    protected void validateAndEnrichProvider(MuleClient client, ORU_R01 oru_r01)
            throws MuleException, ProviderValidationException, EncounterEnrichmentException {
        // Validate provider ID and location ID is correct
        String epid = null;
        PV1 pv1 = oru_r01.getPATIENT_RESULT().getPATIENT().getVISIT().getPV1();
        String proID = pv1.getAttendingDoctor(0).getIDNumber().getValue();
        String proIDType = pv1.getAttendingDoctor(0).getIdentifierTypeCode().getValue();

        Map<String, String> ProIdMap = new HashMap<String, String>();
        ProIdMap.put("id", proID);
        ProIdMap.put("idType", proIDType);

        MuleMessage response = client.send("vm://getepid-openldap", ProIdMap, null, 5000);

        String success = response.getInboundProperty("success");
        if (success.equals("true")) {
            try {
                epid = response.getPayloadAsString();
            } catch (Exception ex) {
                throw new ProviderValidationException(ex);
            }

            // Enrich message
            try {
                pv1.getAttendingDoctor(0).getIDNumber().setValue(epid);
                pv1.getAttendingDoctor(0).getIdentifierTypeCode().setValue(Constants.EPID_ID_TYPE);
            } catch (DataTypeException ex) {
                throw new EncounterEnrichmentException(ex);
            }
        }

        if (epid == null) {
            throw new ProviderValidationException("Invalid provider ID");
        }
    }

    /**
     * Validate the location id against the Facility Registry.
     * 
     * This method does not alter the ORU_R01 message.
     */
    protected void validateAndEnrichLocation(MuleClient client, ORU_R01 oru_r01)
            throws MuleException, LocationValidationException {
        // Validate location ID is correct - sending facility
        String elid = oru_r01.getMSH().getSendingFacility().getHd1_NamespaceID().getValue();

        MuleMessage response = client.send("vm://validateFacility-resourcemap", elid, null, 5000);

        String success = response.getInboundProperty("success");
        if (!success.equals("true")) {
            throw new LocationValidationException("Invalid location ID");
        }
    }

    /**
     * Fetch patient demographic data from the Client Registry and enrich the ORU_R01 with any details that may be missing.
     * @throws ClientValidationException 
     */
    protected void enrichClientDemographics(MuleClient client, ORU_R01 oru_r01, String ecid)
            throws MuleException, EncounterEnrichmentException, ClientValidationException {
        PID pid = oru_r01.getPATIENT_RESULT().getPATIENT().getPID();
        String givenName = pid.getPatientName(0).getGivenName().getValue();
        String familyName = pid.getPatientName(0).getFamilyName().getFn1_Surname().getValue();
        String gender = pid.getAdministrativeSex().getValue();
        String dob = pid.getDateTimeOfBirth().getTime().getValue();

        try {
            if ((givenName == null || givenName.isEmpty()) || (familyName == null || familyName.isEmpty())
                    || (gender == null || gender.isEmpty()) || (dob == null || dob.isEmpty())) {
                // fetch client record from CR to enrich message as it is missing key values
                String clientRecord = fetchClientRecord_OpenEMPI(ecid, client);
                if (clientRecord == null)
                    throw new ClientValidationException();

                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                Document document = dbf.newDocumentBuilder().parse(new InputSource(new StringReader(clientRecord)));

                XPathFactory xpf = XPathFactory.newInstance();
                XPath xpath = xpf.newXPath();

                String givenNameFromCR = getNodeContent(xpath, document, "/ADT_A05/PID/PID.5/XPN.2");
                String familyNameFromCR = getNodeContent(xpath, document, "/ADT_A05/PID/PID.5/XPN.1/FN.1");
                String genderFromCR = getNodeContent(xpath, document, "/ADT_A05/PID/PID.8");
                String dobStrFromCR = getNodeContent(xpath, document, "/ADT_A05/PID/PID.7/TS.1");

                // replace the missing fields with the CR content
                if (givenName == null || givenName.isEmpty()) {
                    pid.getPatientName(0).getGivenName().setValue(givenNameFromCR);
                }
                if (familyName == null || familyName.isEmpty()) {
                    pid.getPatientName(0).getFamilyName().getFn1_Surname().setValue(familyNameFromCR);
                }
                if (gender == null || gender.isEmpty()) {
                    pid.getAdministrativeSex().setValue(genderFromCR);
                }
                if (dob == null || dob.isEmpty()) {
                    pid.getDateTimeOfBirth().getTime().setValue(dobStrFromCR);
                }
            }
        } catch (XPathException ex) {
            throw new EncounterEnrichmentException(ex);
        } catch (SAXException ex) {
            throw new EncounterEnrichmentException(ex);
        } catch (IOException ex) {
            throw new EncounterEnrichmentException(ex);
        } catch (ParserConfigurationException ex) {
            throw new EncounterEnrichmentException(ex);
        } catch (DataTypeException ex) {
            throw new EncounterEnrichmentException(ex);
        }
    }

    private String getNodeContent(XPath xpath, Document document, String path) throws XPathExpressionException {
        XPathExpression expression = xpath.compile(path);
        Node givenNameNode = (Node) expression.evaluate(document, XPathConstants.NODE);
        return givenNameNode.getTextContent();
    }

    /**
     * Fetch client record from OpenEMPI for a patient with the specified ECID
     * 
     * @return The response from OpenEMPI in XML
     */
    private String fetchClientRecord_OpenEMPI(String ecid, MuleClient client) throws MuleException {
        RestfulHttpRequest req = new RestfulHttpRequest();
        req.setHttpMethod(RestfulHttpRequest.HTTP_GET);
        req.setPath("ws/rest/v1/patient/" + Constants.ECID_ID_TYPE + "-" + ecid);
        MuleMessage response = client.send("vm://getPatient-De-normailization-OpenEMPI", req, null, 5000);
        RestfulHttpResponse res = (RestfulHttpResponse) response.getPayload();

        return res.getBody();
    }

    @Override
    public Object onCall(MuleEventContext eventContext) throws Exception {
        MuleContext muleContext = eventContext.getMuleContext();
        MuleClient client = new MuleClient(muleContext);
        MuleMessage msg = eventContext.getMessage();
        RestfulHttpRequest payload = (RestfulHttpRequest) msg.getPayload();

        String newORU_R01 = validateAndEnrichORU_R01(payload, client);
        payload.setBody(newORU_R01);

        log.info("The validated and enriched save encounter message: " + newORU_R01);
        return msg;
    }

}