Database to HL7 migrating a legacy system

<strong>Test</strong>: Compare Outputs

One of the powerful things about the Translator is the ability to traverse messages as “node trees” programmatically.

This makes it possible to write expressive code which can list differences between HL7 messages in a convenient manner where you can see the results in real time.

That’s exactly what I did for the next phase. I tweaked the dbfill.CompareMessage routine to obtain the original message and parse it into an HL7 node tree:

local function LoadMessage(Base, Msg)
   local FileName = 'D:temp'..Base
                 ..Msg.MSH[10]..'.txt'
   trace(FileName)
   local F = io.open(FileName, "r")
   return F:read("*a")
end

function dbfill.CompareMessage(Data)
   local Result = nil
   local Out = hl7.parse{vmd='CVISOutbound.vmd', data=Data}
   local Orig = LoadMessage('orig', Out)
   if (Orig ~= Data) then
      Result = 'Original and current messages differ!'
   end
   return hl7.parse{vmd='CVISOutbound.vmd', data=Orig}, Result, Orig
end

And then I wrote a utility module called ‘diff’ would returns a nice human readable list of differences between the two trees like this:

This made it very easy to locate the last few differences between the old interface and the new interface. The dialog would update in real time as I corrected the mappings. Here’s how it was called from the mapping routine:

There was still one small difference which my diff routine did not detect – this is because the older interface was appending a constant to a subfield using python whereas the vmd had it configured as a single field. The difference showed up when comparing the HL7 messages directly.

To resolve that problem would require a tweak to the vmd file.

Here’s the source code for the diff module, you need to create the diff module and paste this code into it.

Here’s the mapping file after the last set of changes:

require 'dateparse'
require 'node'
require 'dbfill'
require 'diff'

local conn = db.connect{
   api=db.SQLITE,
   name='test', 
   live=true
}

local function trace(a,b,c,d) return end

function main(Data)
   local R = conn:query{
      live=true,
      sql="SELECT * FROM NHCCVISREPORT WHERE MESSAGE_ID = "..Data
   }

   local Msg = hl7.message{vmd='CVISOutbound.vmd', name='NHCCVIS'}

   if #R == 0 then
      print("Odd - unable to find message matching this one.")
      return
   end 

   MapMSH(Msg.MSH, R[1])
   MapPID(Msg.PID, R[1])
   MapPV1(Msg.PV1, R[1])
   MapOBR(Msg.OBR, R[1])
   MapOBX(Msg.OBX, R[1])
   local Out = Msg:S()
   if iguana.isTest() then
      Compare(Msg, 'CVISOutbound.vmd')
   end
   queue.push{data=Out}
end

function Compare(Msg)
   local Out = Msg:S()
   local Orig = dbfill.CompareMessage(Out)
   local R = diff.Compare(Orig:S(), Msg:S(), vmd)   
   trace('There were '..#R..' differences.')
end

function MapMSH(MSH, T)
   MSH[9][1]='ORU'
   MSH[9][2]='R01'
   MSH[3][1] = 'NHCCVIS'
   MSH[4][1] = 'NHC'
   MSH[5][1] = 'PCS'
   MSH[6][1] = 'EMR'
   MSH[12] = '2.3'
   MSH[10] = T.MSHMessageControlID
   MSH[7] = T.MSHDateTimeofMessage:HT()
   MSH[11][1] = 'T'
   return MSH
end

function node.HD(N)
   return os.date('%Y%m%d', dateparse.parse(N:S()))
end

function node.HT(N)
   return os.date('%Y%m%d%H%M%S', dateparse.parse(N:S()))
end

function MapPID(PID, T)
   PID[2][1] = T.PIDPatientIDExternalIDID
   PID[2][3] = '11'
   PID[2][4][1] = 'EMI Primary'

   PID[3][1] = T.PIDPatientIDInternalIDID
   PID[3][2] = T.PIDPatientIDInternalIDCheckDigit
   PID[3][3] = '11'
   PID[3][4][1] = 'MRN'

   PID[5][1] = T.PIDPatientNameFamilyName
   PID[5][2] = T.PIDPatientNameGivenName
   PID[7] = T.PIDDateTimeofBirth:HD()
   PID[8] = T.PIDSex
   PID[18][1] = T.PIDPatientAccountNumberID
   PID[18][4][1] = "Visit"

   return PID
end

function MapPV1(PV1, T)
   PV1[2] = 'OUTPATIENT'
   PV1[3][1] = 'NHCCVIS Assign'
   PV1[3][4][1] = 'NHC'
   PV1[18] = 'NA'
   PV1[19][1] = T.PVVisitNumberID
   PV1[19][4][1] = 'Visit'
   PV1[44] = T.PVAdmitDateTime:HT()
end

function MapOBR(OBR, T)
   OBR[2][2] = 'PCS'
   OBR[3][1] = T.OBRFillerOrderNumberEntityIdentifier
   OBR[3][2] = 'NHCCVIS'
   OBR[4][1] = T.OBRUniversalServiceIDIdentifier
   OBR[4][2] = T.OBRUniversalServiceIDText
   OBR[4][3] = 'NHCCVIS'
   OBR[7] = T.OBRObservationDateTime:HT()
   OBR[18] = T.OBRPlacerField
   OBR[25] = 'F'
   OBR[27][6] = 'ROUTINE'
   return OBR
end

function MapOBX(OBX, T)
   OBX[1][1] = '1'
   OBX[1][2] = 'FT'
   OBX[1][3][1] = T.OBXObservationIdentifierIdentifier
   OBX[1][3][3] = 'NHCCVIS'
   OBX[1][5][1] = T.OBXObservationValue
   OBX[1][11] = 'F'
   OBX[2][1] = '2'
   OBX[2][2] = 'RP'
   OBX[2][5][1] = T.OBXObservationValue1
   OBX[2][3][1] = T.OBXObservationIdentifierIdentifier1
   OBX[2][3][3] = 'NHCCVIS'
   OBX[2][11] = 'F'

   OBX[3][1] = '3'
   OBX[3][2] = 'FT'
   OBX[3][3][1] = T.OBXObservationIdentifierIdentifier2
   OBX[3][3][2] = T.OBXObservationIdentifierText2
   OBX[3][3][3] = 'NHCCVIS'
   OBX[3][5][1] = T.OBXObservationValue2
   OBX[3][11] = 'F'
end

Next Step?

The final step is make our last tweaks to code and the VMD file. However before that we will show the the source code for the diff module.

Leave A Comment?