Article
· Feb 15, 2023 11m read

ChatGPT to create message transformations for you?

A "big" or "small" ask for ChatGPT?


I tried OpenAI GPT's coding model a couple of weeks ago, to see whether it can do e.g. some message transformations between healthcare protocols. It surely "can", to a seemingly fair degree. 

It has been nearly 3 weeks, and it's a long, long time for ChatGPT, so I am wondering how quickly it grows up by now, and whether it could do some of integration engineer jobs for us, e.g. can it create an InterSystems COS DTL tool to turn the HL7 into FHIR message? 

Immediately I got some quick answers, in less than one minute or two.


Test

First I want to test I am talking to the right "guy" behind it.


Question 1:  how to convert HL7 V2.4 messages to FHIR STU3?


ChatGPT:

Converting HL7 V2.4 messages to FHIR STU3 involves several steps, as the two standards have different data models and structures. Here is a high-level overview of the process:

  1. Identify the relevant data elements in the HL7 message that need to be mapped to FHIR resources. This will depend on the specific use case and data being exchanged.
  2. Define the mapping rules for each data element. This involves identifying the equivalent FHIR resource and element that corresponds to the HL7 data element, and any necessary transformations or translations.
  3. Write a program or script that can extract the data from the HL7 message, apply the mapping rules, and create the corresponding FHIR resources. This may involve using a combination of regular expressions, parsing libraries, and FHIR resource creation libraries.
  4. Validate the FHIR resources against the STU3 specification to ensure they conform to the standard and contain all required elements.

Here is an example of how this process might look for converting an HL7 V2.4 observation message to a FHIR STU3 Observation resource:

  1. Identify the relevant data elements in the HL7 message, such as the observation code, value, and units.
  2. Define the mapping rules for each data element, such as mapping the HL7 observation code to a FHIR CodeableConcept and the value and units to a FHIR Quantity.
  3. Write a program or script that can extract the data from the HL7 message, apply the mapping rules, and create a FHIR Observation resource with the appropriate elements.
  4. Validate the FHIR Observation resource against the STU3 specification to ensure it contains all required elements and is valid.

The specific implementation details will depend on the programming language and libraries used, as well as the specific HL7 message and FHIR resource being mapped. It's recommended to consult the HL7 and FHIR specifications and seek expert advice if needed.


Well, it not just sounds about right - it actually answers more comprehensively than most human would do. It gets my attention now, so let's jump into it.   
 


Question 2: Can you create a sample transformation in Python to convert HL7 to FHIR  


ChatGPT:

Sure! Here's a sample Python code snippet that demonstrates how to convert an HL7 message into a FHIR STU3 Observation resource:

from hl7apy import core
from fhir.resources.observation import Observation
# Parse the HL7 message
hl7_message = 'MSH|^~\&|SENDING_APP|SENDING_FACILITY|RECEIVING_APP|RECEIVING_FACILITY|20220527120512||ORU^R01|MSGID12345|P|2.4\r' \
              'OBX|1|NM|GLU^Glucose||100|mg/dL||N|||F|||20220527120512\r'
hl7_parsed = core.parse_message(hl7_message)

# Map HL7 elements to FHIR elements
observation = Observation()

observation.code = {
    "coding": [{
        "system": "http://loinc.org",
        "code": hl7_parsed.obx[0].observation_id.value,
        "display": hl7_parsed.obx[0].observation_id.value
    }]
}

observation.valueQuantity = {
    "value": hl7_parsed.obx[0].value.value,
    "unit": hl7_parsed.obx[0].units.value,
    "system": "http://unitsofmeasure.org",
    "code": hl7_parsed.obx[0].units.value
}

# Serialize the FHIR resource as JSON
observation_json = observation.json()

# Print the serialized FHIR resource
print(observation_json)



This code uses the hl7apy library to parse the HL7 message and extract the relevant data elements, and the fhir.resources.observation class from the fhir.resources module to create a FHIR Observation resource.

In this example, the code maps the HL7 observation ID to the FHIR code element, and the HL7 value and units to the FHIR valueQuantity element. The FHIR resource is then serialized as JSON using the json() method.

Note that this is a simple example and you may need to map more data elements depending on your specific use case. Additionally, you will need to install the hl7apy and fhir.resources libraries to run this code.


 

It's not a bad answer at all - gives a decent boilerplate code. Let's give it a bit specific challenge now: 


Questions 3: Can you give a sample transformation from HL7 V2 to FHIR Stu3 in Intersystems COS DTL


ChatGPT:


Certainly! Here's an example of how to transform an HL7 V2 message to a FHIR STU3 Observation resource in InterSystems COS DTL (Data Transformation Language):

Class DTL.HL7toFHIR Extends %RegisteredObject
{

ClassMethod TransformHL7ToObservation(hl7Message As %String) As %String [ CodeMode = expression, DDLAllowed = 0 ]
{
    set src = ##class(EnsLib.HL7.Message).%New()
    set src.DocType = "2.4"
    set src.Parse(hl7Message)

    set obs = ##class(FHIR.Resource.Observation).%New()
    set obs.subject = { "reference": "Patient/"_src.MSH.GetSendingApplication() }
    set obs.status = "final"
    set loincCode = src.GetSegment("OBX", 1).GetField(3).GetField(1)
    set obs.code = { 
        "coding": [ 
            {
                "system": "http://loinc.org", 
                "code": loincCode, 
                "display": loincCode 
            }
        ]
    }

    set value = src.GetSegment("OBX", 1).GetField(5).GetField(1)
    set units = src.GetSegment("OBX", 1).GetField(6).GetField(1)
    set obs.valueQuantity = { 
        "value": value, 
        "unit": units, 
        "system": "http://unitsofmeasure.org", 
        "code": units 
    }

    quit obs.%ToJSON()
}

}

This code defines a class method called TransformHL7ToObservation that takes an HL7 V2 message as input and returns a FHIR STU3 Observation resource as a JSON string.

The method first creates a new EnsLib.HL7.Message object and uses it to parse the input HL7 message. It then creates a new FHIR.Resource.Observation object and sets the subject and status elements.

Next, the code extracts the LOINC code, value, and units from the HL7 message and maps them to the corresponding elements in the FHIR resource.

Finally, the method serializes the FHIR resource as JSON and returns it.

To use this class method, you can call it from another COS method or from an external system using InterSystems' REST API. Here's an example of how to call the method from another COS method:

set hl7Message = "MSH|^~\&|SENDING_APP|SENDING_FACILITY|RECEIVING_APP|RECEIVING_FACILITY|20220527120512||ORU^R01|MSGID12345|P|2.4\rOBX|1|NM|GLU^Glucose||100|mg/dL||N|||F|||20220527120512\r"
set fhirObservation = ##class(DTL.HL7toFHIR).TransformHL7ToObservation(hl7Message)
write fhirObservation

Note that you will need to define the EnsLib.HL7.Message and FHIR.Resource.Observation classes in your InterSystems environment and have the appropriate permissions to run COS code.


 

Well, that's probably the most effective yet comprehensive answer we could ever have expected within about 25 seconds. Right?

(Please note: the above COS sample isn't perfectly accurate. It's only a boilerplate structure, needs more fine-tuning for real-world implementations. As @Tete Zhang kindly pointed, Ensemble level HL7 message class uses ImportFromSting() instead of Parse(); DocType should be more specific etc. And apparently it seemed to create a new FHIR class hierarchy on its own from the COS ground up, instead of using existing HealthShare's built-in FHIR STU3 plumbings. Its "thought process" appears to be following the previous answer of Python structure. It's not going to achieve an Ensemble or HealthShare certification for now, and has a lot to learn with more specialist interactions, but as an excuse we might have to pardon it for its first trial in less than 2 minutes so far, by today?  )   
  

Now that we are here, we need to be fair to other widely used options I have been enjoying too. So I carried on a few more questions with slightly more faith:


Question 4: Can you give a sample transformation from HL7 V2 to FHIR Stu3 in Orion Rhapsody


ChatGPT:

Certainly! Here's an example of how to transform an HL7 V2 message to a FHIR STU3 Observation resource in Orion Rhapsody:

  1. First, create a new Rhapsody channel and add an HL7 Connector component to receive the input HL7 V2 message.
  2. Add a new Data Transformation component to the channel and configure it to use a custom script.
  3. In the Data Transformation component, add the following script to transform the HL7 V2 message to a FHIR STU3 Observation resource:
function transform(msg) {

    var hl7 = Packages.ca.uhn.hl7v2.model.v24.message.ORU_R01(msg.getRawData());

    var observation = new Packages.org.hl7.fhir.dstu3.model.Observation();
    observation.setStatus(Packages.org.hl7.fhir.dstu3.model.Observation.ObservationStatus.FINAL);

    var codeableConcept = new Packages.org.hl7.fhir.dstu3.model.CodeableConcept();
    var coding = new Packages.org.hl7.fhir.dstu3.model.Coding();
    coding.setSystem("http://loinc.org");
    coding.setCode(hl7.getRESPONSE().getORDER_OBSERVATION()[0].getOBR().getUniversalServiceIdentifier().getIdentifier().getValue());
    coding.setDisplay(hl7.getRESPONSE().getORDER_OBSERVATION()[0].getOBR().getUniversalServiceIdentifier().getText().getValue());
    codeableConcept.addCoding(coding);
    observation.setCode(codeableConcept);

    var quantity = new Packages.org.hl7.fhir.dstu3.model.Quantity();
    quantity.setValue(hl7.getRESPONSE().getORDER_OBSERVATION()[0].getOBSERVATION()[0].getOBX().getObservationValue()[0].getValue().getValue());
    quantity.setUnit(hl7.getRESPONSE().getORDER_OBSERVATION()[0].getOBSERVATION()[0].getOBX().getUnits().getText().getValue());
    observation.setValue(quantity);

    var reference = new Packages.org.hl7.fhir.dstu3.model.Reference();
    reference.setReference("Patient/" + hl7.getPATIENT_RESULT().getPATIENT().getPid3_PatientIdentifierList()[0].getID().getValue());
    observation.setSubject(reference);

    return JSON.stringify(observation);
}
  1. Save the script and test the transformation by sending an HL7 V2 message to the channel. The transformed FHIR STU3 Observation resource should be output by the Data Transformation component in JSON format.

This script uses the HAPI FHIR and HAPI HL7 v2 libraries, which are included with Orion Rhapsody. It starts by parsing the input HL7 V2 message using the HAPI HL7 library.

Next, the script creates a new FHIR STU3 Observation resource and sets its status, code, value, and subject elements based on the corresponding values in the HL7 message.

Finally, the script serializes the FHIR resource as JSON and returns it.

Note that this is just a simple example and may not work for all HL7 V2 message types or FHIR resources. You may need to modify the script to handle different message types or map additional fields to the FHIR resource.


 


Questions 5 Can you create a sample transformation converting HL7 v2.4 to FHIR R4 in Mirth integration engine


ChatGPT

Certainly! Here's an example of how to transform an HL7 V2.4 message to a FHIR R4 Observation resource in Mirth:

  1. First, create a new Mirth channel and add an HL7v2 Listener source connector to receive the input HL7 V2 message.
  2. Add a new JavaScript Writer destination connector to the channel and configure it to use a custom script.
  3. In the JavaScript Writer connector, add the following script to transform the HL7 V2 message to a FHIR R4 Observation resource:
var hl7msg = msg['HL7v2 Message'].toString(); // get the HL7 V2 message
var hl7data = Packages.ca.uhn.hl7v2.parser.Parser.parse(hl7msg); // parse the message with the HAPI HL7 parser
// extract values from the HL7 message and create the FHIR resource
var obs = new Packages.org.hl7.fhir.r4.model.Observation();
obs.setStatus(Packages.org.hl7.fhir.r4.model.Observation.ObservationStatus.FINAL);

var code = new Packages.org.hl7.fhir.r4.model.CodeableConcept();
code.addCoding()
  .setSystem("http://loinc.org")
  .setCode(hl7data.get("OBR-4-1").getValue())
  .setDisplay(hl7data.get("OBR-4-2").getValue());
obs.setCode(code);

var value = new Packages.org.hl7.fhir.r4.model.Quantity();
value.setValue(hl7data.get("OBX-5-1").getValue());
value.setUnit(hl7data.get("OBX-6").getValue());
obs.setValue(value);

var subject = new Packages.org.hl7.fhir.r4.model.Reference();
subject.setReference("Patient/" + hl7data.get("PID-3-1").getValue());
obs.setSubject(subject);

// output the FHIR resource as JSON
var json = org.hl7.fhir.r4.formats.JsonParser().setPrettyPrint(true).encodeResourceToString(obs);
return json;
  1. Save the script and test the transformation by sending an HL7 V2 message to the channel. The transformed FHIR R4 Observation resource should be output by the JavaScript Writer connector in JSON format.

This script uses the HAPI FHIR and HAPI HL7 v2 libraries, which are included with Mirth. It starts by extracting the HL7 V2 message from the Mirth message object and parsing it with the HAPI HL7 parser.

Next, the script creates a new FHIR R4 Observation resource and sets its status, code, value, and subject elements based on the corresponding values in the HL7 message.

Finally, the script serializes the FHIR resource as JSON and returns it.

Note that this is just a simple example and may not work for all HL7 V2 message types or FHIR resources. You may need to modify the script to handle different message types or map additional fields to the FHIR resource.



Conclusions?


I don't have any conclusions or next steps anymore, since even my imagination can't catch up with its leaps.  It looks as if there were only limited options for me now:  either I can spend time in picking out its imperfect coding styles, or I can re-think of what's left on my plate in another few weeks? I need to think carefully more than ever about the potential of this thing. :)

Joking aside, while I am enjoying posting on this forum (thanks to the hosts), another thought comes to my mind would be, this "could" actually be an important enabler for some competitive niche player to leap forward to mass market, right?   It used to take years to be really comfortable with some coding languages and scripts, due to various reasons, but now the landscape is moving, and with ChatGPT it not only offers well-composed documentation, instructions and samples, but it might also be able to automatically manufacture the engineering tools of your choice over the coming months or years, right?  It seems to be able to level up the play ground in "languages", and eventually the non-functional side of features such as performance etc service qualities would look more  outstanding.  

Discussion (7)4
Log in or sign up to continue

ChatGTP always seems to give very convincing answers, but often they can be wrong in unexpected ways.

For example, I asked it to decrypt "the cat sat on the mat" which had been encrypted using the Caesar cipher with a shift of 7 as "aol jha zha vu aol tha".  

This was it's answer:

Sure! The Caesar cipher is a simple substitution cipher that replaces each letter in the plaintext with a letter a fixed number of positions down the alphabet. In the case of the Caesar cipher, the shift is typically three letters.

To decrypt the message "aol jha zha vu aol tha" that you provided, we need to shift each letter three positions up the alphabet, as the message has been encrypted using a Caesar cipher with a shift of three.

Applying this decryption technique, we get the plaintext message "the dog ate the hat".

Quite amusing.

Interesting test. I tested your question: can you encrypt "the cat sat on the mat" by Caesar cipher with a shift of 7? It gives detailed steps and got almost every letter correct in the step, but give the final string wrong. Almost the same symptom as my senior mind for now.

Then I up-geared the question: Can you generate a function in Python to encrypt "the cat sat on the mat" in Caesar cipher by shift 7? It did generate the Python function looking OK, and then it also generated a COS function looking largely a right boilerplate in about 35 seconds.

My personal take on this might be:  If it can do the job, and explains how it's done step by step, I would think it's a very helpful tool. But if it can manufacture a tool of my choice to do the job,  I would kind of believe it's more like a human engineer - it gives me the tool for my review, revising and approval; then I can also choose to use the tool to do the jobs with engineering certainty, repeatability, accuracy and transparency, which saved me about 90% of previous engineering efforts. I think this level of progress to intelligence might be slightly more profound?    

And thanks - indeed I am also interested in knowing how to test its limits of capability and incapability at either end. :) 

The ObjectScript code looks convincing but is wrong..

We don't have a method Parse() in the EnsLib.HL7.Message class, instead we use ImportFromString(). The DocType should be 2.4:ORU_R01 or similar with message category and doctype combined, and set with PokeDocType() method. 

Further, we don't have a FHIR.Resource.Observation package and cannot get value from the HL7 message directly using "src.MSH.GetSendingApplication()". There are more issues like this..

ChatGPT still has a long way to go and a lot to learn..

You are absolute right, Tete. It's just a boilerplate structure. And apparently it does JavaScript and Python better than COS for now. Due to the nature of current ChatGPT, every time it would compose slightly different answers & samples for you, so unfortunately I can't replicate the answer - otherwise I'd love to feed your exact comments back into it, to see whether its Reinforcement Learning style part is able to follow up and really "learn", and how much it could revise its sample step by step to an accurately working & testable sample. I assume there seems no reason it can't  pick it up over the time.

For example, if I ask the same question again, it would answer slightly differently

ChatGPT:

Sure, here's an example of how to transform an HL7 V2 message to a FHIR STU3 Patient resource using the Data Transformation Language (DTL) in InterSystems Caché:

  1. First, create a new DTL class in InterSystems Caché.
  2. In the DTL class, define two message classes: one for the input HL7 V2 message and one for the output FHIR STU3 resource. Here's an example of what the message classes might look like:
Class HL7v2Message Extends %SerialObject
{
Property MSH As %String(MAXLEN = 100, TRUNCATE = 1, XMLPROJECTION = "attribute") [ Required ];
Property PID As %String(MAXLEN = 500, TRUNCATE = 1, XMLPROJECTION = "element") [ Required ];
}

Class FHIRPatient Extends %SerialObject
{
Property resourceType As %String(MAXLEN = 100, TRUNCATE = 1, XMLPROJECTION = "attribute") [ InitialExpression = "Patient" ];
Property id As %String(MAXLEN = 100, TRUNCATE = 1, XMLPROJECTION = "attribute");
Property active As %Boolean(XMLPROJECTION = "element");
Property name As list Of FHIRHumanName(XMLPROJECTION = "element");
Property telecom As list Of FHIRContactPoint(XMLPROJECTION = "element");
Property gender As %String(MAXLEN = 10, TRUNCATE = 1, XMLPROJECTION = "element");
Property birthDate As %String(XMLPROJECTION = "element");
}
  1. Define a new DTL transformation method in the class that takes an input HL7v2Message object and returns a FHIRPatient object. Here's an example of what the DTL method might look like:
Method HL7v2ToFHIRPatient(input As HL7v2Message) As FHIRPatient [ CodeMode = objectgenerator, Internal ]
{
    Set output = ##class(FHIRPatient).%New()
    
    // Parse MSH segment
    Set output.id = input.MSH.%split("^",1)
    
    // Parse PID segment
    Set output.active = 1 // patient is always active in HL7 v2
    Set output.name = ..ParseName(input.PID.%split("^",5))
    Set output.telecom = ..ParseTelecom(input.PID.%split("^",13))
    Set output.gender = input.PID.%split("^",8)
    Set output.birthDate = ..ParseDate(input.PID.%split("^",7))
    
    Quit output
}

Method ParseName(name As %String) As list Of FHIRHumanName
{
    Set names = ##class(%List).%New()
    
    Set nameObj = ##class(FHIRHumanName).%New()
    Set nameObj.family = name.%split("^",1)
    Set nameObj.given = name.%split("^",2)
    
    Do names.Insert(nameObj)
    
    Quit names
}

Method ParseTelecom(telecom As %String) As list Of FHIRContactPoint
{
    Set contacts = ##class(%List).%New()
    
    Set contactObj = ##class(FHIRContactPoint).%New()
    Set contactObj.system = "phone"
    Set contactObj.value = telecom.%split("^",1)
    
    Do contacts.Insert(contactObj)
    
    Quit contacts
}

Method ParseDate(date As %String) As %String
{
    Set year = $extract(date, 1, 4)
    Set month = $extract(date, 5, 6)
    Set day = $extract(date, 7, 8 ... ...
    ... ...

Here we seem to see, it doesn't learn much about the internal class library yet - it seems trying to construct FHIR etc classes from the ground up without knowing the existing FHIR plumbing yet. Not sure how it passed the Google L3 Engineer test, but it would fail the HealthConnect certification for now - it has a long way to learn, indeed, possibly also depending on whether we really want it to learn too quick? :)

Hi Zhong, you are absolutely right - ChatGPT appears to return subtly different answers depending on if it's the first question in a new chat, or if you alter the wording slightly (eg. use the verbose expression Cache ObjectScript instead of COS).  For example, I also can't reproduce the exact same answers for your questions.

Sometimes, it will also refuse to provide a code example, simply returning the dreaded phrase "As an AI language model, I cannot provide specific code ......".