Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

During the weekly design call held on the 18th July 2012, (see here) it was decided to use groovy templates to create the hl7 messages.
We decided not to use the HAPI api in order to create hl7 messages. Instead, we will use groovy templates to create the required message in xml format. The xml format looks like this: A Developer's Guide to the ORUR01 Message
Once the xml format of the message has been created, it may be translated into the pipe delimited format via conversion using the HAPI API and the pipes returned to the user.

Why

...

Groovy Templates?

We belive that using groovy will alow us to make the message specification as flexible as possible. For example, the HAPI API would allow us to set a series of specific message segments as follows-pid.getPatientName(0).getGivenName(). setValue(pat.getGivenName());However,if we were to allow users to fill in message segments in this manner, it would prevent them from using specific data which they may need. For example, some users may want to store the data represented in a concept such as CIVIL STATUS as an obs, while yet others may want to store it in the PID segment of the message.
Furthermore, some users may prefer to use PatientIdentifier objects to set the patient id's, while yet others may use Person attributes to do so.
Groovy allows us to create a resuable template which gathers these data, and displayes them in a manner which makes it easy to edit / manipulate.Specific guidelines for the hl7 message template:. The template will have access to almost all data about the patient/encounter/obs via the api and create any parts of a message that are wanted.

About The Groovy Template

The template receives a series of bindings from its parent. (the patient object, patient name object, list of encounters, etc)
The template can loop over and display any of that
The template is built of a series of sub-by calling other (sub)templates
A sub template is responsible for creating each of the main message segments (MSH, PID, PV1, ORC, OBR and OBX)
Each sub template may be in turn composed of several other sub templates.
The MSH segment is made up of all constants, and hence may not need to be composed of other segments.
The OBR segment is used to represent an OpenMRS obs group

Converting the xml version of the hl7 message into the pipe delimited format.

1. Convert the xml object into string
2. Convert the string into the HAPI object
3. Use HAPI to convert the hl7 message into the pipe delimited format

Converting an hl7 String into the HAPI object

...


The OBX is a single segment.
If there are multiple encounters in the message, the PID + PV1 segments need to be repeated for that encounter

Rendering the Template

To turn the template into xml output, the groovy needs to be able to call openmrs objects. The template is passed to the "render" method in the service. The render method also accepts a series of "bindings" (aka parameters) for the objects that the groovy template will use. Most of the time this will be the current patient and the list of encounters needing to be converted into hl7.

Converting to HL7 Pipes

The xml string returned from the rendering can be passed through HAPI

Converting the HAPI object into the pipe delimited format is done using a HAPI PipeParser

Code Block
PipeParser parser = new PipeParser();
		String msghl7pipesmsg = null;
		try {
			msghl7pipesmsg = parser.encode(r01StringxmlString);
		}
		catch (HL7Exception e) {
			log.error("Exception parsing constructed message.");
		}

pseudo-template for a R01 message (contributed by Darius)

Code Block
/* template requires: patient, encounters */
    <msh> (hard coded for this template)...</msh>  /* or just ${ callTemplate("MSH", patient) } and its done */
    ${ callTemplate("PID", patient) }
    <% encounters.each { encounter -> %>
        ${ callTemplate("PV1", encounter) }
        <% encounter.getAllObs().filter({ /* ungrouped only */ }) { obs -> %>
            ${ callTemplate("OBX", obs) }
        <% } %>
        <% encounter.getAllObs().filter({ /* obs groups only */ }) { group -> %>
            ${ callTemplate("obs-group-as-OBR-and-OBX", group) }
        <% } %>
    <% } %>

Template with name "PID":
<!-- assumes these vars are made available:
  patient == Patient object
-->
   <pid>
       <> ${ callTemplate("XPN", [name: patient.personName)] }</>
   </pid>

Template with name "XPN":
<!-- assumes these vars are made available:
  name == PersonName object
  useFieldX == boolean for whether or not to show something. (optional parameter)
-->
<XPN>
    <XPN.1>
        <FN.1>${ name.familyName }</FN.1>
        <% if (name.familyName2) { %>
            <FN.2>${ name.familyName2 }</FN.2>
        <% } %>
        <% if (param.useFieldX == true) { %>
            <FN.X> ${name.getAttribute('asdf')}</FN.X>
        <% } %>
    </XPN.1>
    <XPN.2>${ name.givenName }</XPN.2>
</XPN>

Template with name "PatientIdentifier-to-CX":
...

Template with name "PersonAttribute-to-CX":
<!-- assumes these vars are made available:
    attribute == PersonAttribute
    identifierTypeHl7Code == String
-->

...