Thursday 16 September 2021

Looping over repeating OBX values and updating the segment

I regularly need to update HL7 messages as the data flows between systems. Often, it's significantly cheaper to take the feed designed for another integration and massage the HL7 message before passing it onto your integration.  

With Integration Host you generally just remap using the Transformers and it's all very simple. But the other day I got a wee bit tangled when trying to update OBX values.

I wanted to loop over each OBX segment, look at the value in the OBX-3.4, then use some conditions and lookup tables (note: loving lookup tables right now) to alter the value before inserting it back into the OBX-3.2.

So I used a FOREACH transformer to loop over the OBX values, then put the OBX-3.4 into a variable that I then updated to the value I needed.  This was all easy, but the trick came when I tried to write the value back into the same OBX line the FOREACH was on.  My mapping just wrote each value into the first segment, overwriting it each time, leaving the last in place. This stumped me for a bit and I contacted HL7 Soup support.

It turns out that the loop is only looping over the incoming OBX values, not the outgoing ones, so you can’t use it to set the value in the destination message.  Rather you need to create a different HL7 path for each iteration and set that value.  Apparently, they looked at creating a feature in the UI to allow this several times, but it turns out that code is by far the easiest way:

//Update the OBX-3.2 on the same index as the for each loop.
IHL7Message destinationMessage = (IHL7Message)activityInstance.Message;
string path = $"OBX[{workflowInstance.GetVariable("ForEachIterator")}]-3.2";
destinationMessage.SetValueAtPath(path,  workflowInstance.GetVariable("UpdatedValueVariable"));

Above, you can see that you just construct the path we wish to write to using the forEachIterator variable (which is automatically populated by the FOREACH Transformer), then set the path in the destination message with the needed value - the variable "UpdatedValueVariable" in my case.

Extracting HL7 Fields out of File Names with HL7 Soup

Sometimes when generating an HL7 message, the trigger or source of the message is a file that doesn't contain parsable data such as a PDF.  It's a fairly common practice to use the 255 filename characters to include some additional metadata.  This post shows how to access the filename and then how to extract the values.

Integration Host makes accessing the filename very easy.  If you have a Directory scanner the filename is automatically put into a variable for you to use "DirectoryScannerFileName". The value includes the filename and extension, so it's pretty convenient to use if you just want to update the same file, but as we want to use the values, we need to remove the extension and split that metadata out.

To do this I used a Code Transformer with the following code:

//Get the destination message
IHL7Message destinationMessage = (IHL7Message)activityInstance.Message; 

//Get the filename
string fileName = workflowInstance.GetVariable("DirectoryScannerFileName");

//Remove the extension
fileName = System.IO.Path.GetFileNameWithoutExtension(fileName);

//split it by the _ character
string[] values = fileName.Split('_');

//Write the values to their HL7 location.  I've just put them in dummy locations for now, as you can change them
//Note, that to use this the filename must have the correct number of values or you will get an error.
destinationMessage.SetValueAtPath("PV1-19", values[0]);
destinationMessage.SetValueAtPath("PV1-20", values[1]);
destinationMessage.SetValueAtPath("PV1-21", values[2]);

It's pretty straightforward.  You can see that I get the variable with the filename, then remove the extension, followed by splitting it by the "_" character.

Then it's just a matter of placing each value where I want it.  I've put it into the PV1-19 to 21, but you can use whichever location you need.

The one thing to note is that I've written this to error if the number of fields required isn't provided.  I prefer this outcome rather than proceeding even if the filename doesn't have all the values, but others may wish to put an IF condition around it to only add the values if the right number of fields exist.