HL7 Modules

HL7 Conformance

Using the validate.lua module.

This is an area which is becoming more and more important as more data is exchanged between different health organizations. i.e. HIE (Health Information Exchange).  If your organization can offer interfaces to your trading partners that are very rich and informative in telling what is wrong with the format of HL7 they are feeding you, then it can save an awful lot of time in creating interfaces.  The programmer on the other side can see immediately from the responses coming back from your interfaces what they need to fix, rather than requiring time consuming manual processes with unreadable specifications and endless conference calls.

The Translator is a great tool for doing detailed validation of HL7 messages once you realize the best way to leverage it.

It helps if you think about message validation from the perspective of the overall goals:

  1. The application should catch all the errors.
  2. It is nice if you can list the problems and describe them in nice language that describe to a counter party how they can fix their messages.

A good model for doing conformance testing is:

  1. Make a reusable shared module that have the conformance logic within it.
  2. In that module define a function which takes the message you wish to check and
  3. Have it return a list of clear reasons why the message doesn’t conform.

Structuring things in this manner has the following advantages:

  1. The code can be made very clean and easy to understand.
  2. Very complex conformance rules can be tested for – i.e. looking at multiple fields in a message.
  3. The function can be used to generate informative ACKs which NTE note segments with the data, or to generate good error messages or do a true/false pass/fail type logic for accepting or rejecting messages.

This following screen shot shows a nice example of a conformance module that follows these principles (the full source is given at the end of this page):

From the above it’s clear to see how maintainable the code is – the validation messages are all in clean understandable English which would clearly communicate to the user what the issue is.  Because of the use of small helper functions to define the rules, it’s simple to read the code and edit the rules.  In the above example we write out the error message explicitly.  In some cases though you might be able to use this helper function which uses the grammar information of the HL7 node tree.

If we click on the ErrList table annotation we can see a nice readable list of errors:

With the validation errors in this structure it becomes easy to use this information.  For instance we could invoke this code inside of LLP listener Translator instance to make a nice NACK (see custom ACKS):

You will also need to add the conformance_ack.vmd to your project.

local validate = require 'validate'

-- this module follows the best practice of using local functions
-- this requires main() function to be placed at the end after those functions

local function GenerateNACK(Msg, Errs)
   local N = hl7.message{vmd='conformance_ack.vmd', name='Acknowledgement'}  
   N.MSH[7] = os.date('%Y%m%d%H%M%S')
   N.MSH[3][1] = Msg.MSH[5][1]
   N.MSH[4][1] = Msg.MSH[6][1]
   N.MSH[5][1] = Msg.MSH[3][1]
   N.MSH[6][1] = Msg.MSH[4][1]
   N.MSH[11][1] = 'P'
   N.MSH[9][1] = 'ACK'
 
   N.MSA[2] = Msg.MSH[10]
   trace(N)
   
 
   for i = 1, #Errs do
      N.NTE[i][3][1] = Errs[i]
   end
   return N:S()
end

local function ProcessADT(Msg)
   local Errs = validate.CheckAdt(Msg) 
   if (#Errs > 0) then
      return GenerateNACK(Msg, Errs)   
   end
end
 
function main(Data)
   local Msg = hl7.parse{vmd='example/demo.vmd', data=Data}
   
   local R = ProcessADT(Msg)
 
   ack.send(R)
end

Here’s the ACK that is generated:

MSH|^~&|Main HIS|St. Micheals|AcmeMed|Lab|20110908122935||ACK||P|
MSA||9B38584D9903051F0D2B52CC0148965775D2D23FE4C51BE060B33B6ED27DA820|
NTE|||PID 17.1 religion code not present.|
NTE|||PID.8, patient sex had value 'F' instead of acceptable values ['Female','Male']|

Get the latest version of the validate.lua module from our repository.

Helper function which uses the grammar information of the HL7 node tree

This helper function makes use of the grammar information of the HL7 node tree. This may in some cases make it easier to maintain conformance code:

As you can see from the usage this helper function doesn’t require a description of the field being checked to be passed in. Instead we pass in the address co-ordinates in a Lua table in an array format. It works quite well for PID.2 and PID.17.1 but as you can see for PID.5.1.1.3 the error message isn’t that nice. That’s partly why in my first cut of this type of logic I preferred to just explicitly put in the error message.

There are compromises possible – like not having the code make the textual description and instead just have it generate the PID.5.1.1.3 notation.

One could take it further and look at nodeType() to check if a node is in fact a repeating field and alter the validation function depending on whether we are going down into a repeating field and so on. It’s not clear to me that the added benefit will be worth the effort. Here is the code for the RequiredFieldMeta function:

local function RequiredFieldMeta(Errs, N, Address)
   local T = N
   for i=1, #Address do
      T = T[Address[i]]
      trace(T)
   end
   if T:isNull() then
      local Msg = N:nodeName()
      local Long = ''
      T = N
      for i=1, #Address do
         T = T[Address[i]]
         Msg = Msg..'.'..Address[i]
         Long = Long..' '..T:nodeName()
      end
      Msg = Msg.." - "..Long..' not present.'
      Errs:add(Msg)
      return Errs[#Errs]
   end
end