Article
· Jul 17, 2023 10m read

Installation and adaptation of EMPI in Standalone mode - Feeding the beast with FHIR - Transformation and ingestion

Hello again everyone.

In our previous article we saw how to configure our EMPI to receive FHIR messages. To do this we installed the FHIR Adapter that InterSystems made available to us that configured a REST endpoint to which we could send our FHIR message. We would then get the message and transform it to a %String that we would send via TCP to the output of our EMPI configured in our HSPIDATA namespace.

Alright, it's time to see how we retrieve the message, transform it back to a %DynamicObject and parse it to the class used by the EMPI to store the information.

Message reception by TCP

As we have indicated, from the production that has configured the reception of FHIR resources, we have sent a message to a specific TCP port in which we have a Business Service listening, in our case this Business Service will be a simple EnsLib.TCP.PassthroughService whose objective is Capture the message and forward it to a Business Process where we will perform the required data transformation.

Here we have our Business Service:

And here the basic configuration of it:

Transformation of our FHIR message

As you can see we have only configured the port on which our message will be received via TCP and the component to which we are going to send our message, in our case we have called it Local.BP.FHIRProcess, let's take a look at said class to see How we retrieve the information from our FHIR resource:

Class Local.BP.FHIRProcess Extends Ens.BusinessProcess [ ClassType = persistent ]
{

Method OnRequest(pRequest As Ens.StreamContainer, Output pResponse As Ens.Response) As %Status
{
	
	set tDynObj = {}.%FromJSON(pRequest.Stream)
		
	If (tDynObj '= "") {		
		set hubRequest = ##class(HS.Message.AddUpdateHubRequest).%New()
		// Create AddUpdateHub Message    
    	// Name, sex, DOB
    	set givenIter = tDynObj.name.%Get(0).given.%GetIterator()
    	while givenIter.%GetNext(, .givenName){
	    	if (hubRequest.FirstName '= "")
	    	{
		    	Set hubRequest.FirstName=givenName
	    	}
	    	else {
	    		Set hubRequest.FirstName=hubRequest.FirstName_" "_givenName
	    	}    		
    	}
    	Set hubRequest.FirstName=tDynObj.name.%Get(0).given.%Get(0)
    	Set hubRequest.LastName=tDynObj.name.%Get(0).family
    	Set hubRequest.Sex=tDynObj.gender
    	Set hubRequest.DOB=hubRequest.DOBDisplayToLogical(tDynObj.birthDate)    	
    
    	// Inserts full birth name information for the patient
    	set nameIter = tDynObj.name.%GetIterator()
    	while nameIter.%GetNext(, .name){
    		Set tName = ##class(HS.Types.PersonName).%New()
    		if (name.prefix '= "")
    		{
	    		Set tName.Prefix = name.prefix.%Get(0)	
    		}    		
    		Set tName.Given = name.given.%Get(0)
    		Set tName.Middle = ""
    		Set tName.Family = name.family
    		Set tName.Suffix = ""
    		Set tName.Type=^Ens.LookupTable("TypeOfName",name.use)
    		Do hubRequest.Names.Insert(tName)
    	}
    	
    
    	set identIter = tDynObj.identifier.%GetIterator()
    	while identIter.%GetNext(, .identifier){
    		if (identifier.type'=""){
    			if (identifier.type.coding.%Get(0).code = "MR") {
    				Set hubRequest.MRN = identifier.value
    				Set hubRequest.AssigningAuthority = ^Ens.LookupTable("hospital",identifier.system)
    				Set hubRequest.Facility = ^Ens.LookupTable("hospital",identifier.system)
    			}
    			elseif (identifier.type.coding.%Get(0).code = "SS") {
    				Set hubRequest.SSN = identifier.value
    			}
    			else {
    				Set tIdent=##class(HS.Types.Identifier).%New()
    				Set tIdent.Root = identifier.system   // refers to an Assigning Authority entry in the OID Registry
    				Set tIdent.Extension = identifier.value
    				Set tIdent.AssigningAuthorityName = identifier.system
    				Set tIdent.Use = identifier.type.coding.%Get(0).code
    				Do hubRequest.Identifiers.Insert(tIdent)
    			}
    		}    		
    	}
    
    	// Address
    	set addressIter = tDynObj.address.%GetIterator()
    	while addressIter.%GetNext(, .address){
    		Set addr=##class(HS.Types.Address).%New()
    		Set addr.City=address.city
    		Set addr.State=address.state
    		Set addr.Country=address.country
    		Set addr.StreetLine=address.line.%Get(0)
    	   	Do hubRequest.Addresses.Insert(addr)
    	}
    	
    	//Telephone
    	set identTel = tDynObj.telecom.%GetIterator()
    	while identTel.%GetNext(, .telecom){    		
    		if (telecom.system = "phone") {
	    		Set tel=##class(HS.Types.Telecom).%New()
    			Set tel.PhoneNumber=telecom.value
    			Do hubRequest.Telecoms.Insert(tel)
    		}    		    		
    	}
	}
	
	Set tSC = ..SendRequestSync("HS.Hub.MPI.Manager", hubRequest, .pResponse) 
    Quit tSC
}

Storage Default
{
<Type>%Storage.Persistent</Type>
}

}

Let's see a little more in detail what we are doing:

First we received the message sent from the Business Service:

Method OnRequest(pRequest As Ens.StreamContainer, Output pResponse As Ens.Response) As %Status
{
	
	set tDynObj = {}.%FromJSON(pRequest.Stream)

As we can see in the signature of the OnRequest method, the input message corresponds to a class of type Ens.StreamContainer. This transformation of our message of type %String has been carried out in the Business Service. In the first line of the method, what we will do is retrieve the message that is found as a Stream inside the pRequest variable. We then transform it to a %DynamicObject using the %FromJSON statement.

With our message mapped to a dynamic object we will be able to access each of the fields of the FHIR resource that we have sent:

	set tDynObj = {}.%FromJSON(pRequest.Stream)
		
	If (tDynObj '= "") {		
		set hubRequest = ##class(HS.Message.AddUpdateHubRequest).%New()
		// Create AddUpdateHub Message    
    	// Name, sex, DOB
    	set givenIter = tDynObj.name.%Get(0).given.%GetIterator()
    	while givenIter.%GetNext(, .givenName){
	    	if (hubRequest.FirstName '= "")
	    	{
		    	Set hubRequest.FirstName=givenName
	    	}
	    	else {
	    		Set hubRequest.FirstName=hubRequest.FirstName_" "_givenName
	    	}    		
    	}
    	Set hubRequest.FirstName=tDynObj.name.%Get(0).given.%Get(0)
    	Set hubRequest.LastName=tDynObj.name.%Get(0).family
    	Set hubRequest.Sex=tDynObj.gender
    	Set hubRequest.DOB=hubRequest.DOBDisplayToLogical(tDynObj.birthDate)   

In this fragment we see how we create an object of the HS.Message.AddUpdateHubRequest class, which is what we will send to the Business Operation HS.Hub.MPI.Manager in charge of carrying out the corresponding operations within the EMPI, whether it is the creation of the new patient or the updating it, as well as linking it with possible matches that may exist with other patients already within the EMPI.

The next step is to populate the new object with the data received from the Business Service. As you can see, all we do is retrieve the data from the different fields of the dynamic object that we have just created. The format of the dynamic object corresponds exactly to the one defined by HL7 FHIR for the patient resource, you can see examples directly on the HL7 FHIR web page

For our example we have chosen this patient from the list provided by the HL7 FHIR page itself:

{
  "resourceType": "Patient",
  "id": "example",
  "text": {
    "status": "generated",
    "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n\t\t\t<table>\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Name</td>\n\t\t\t\t\t\t<td>Peter James \n              <b>Chalmers</b> (&quot;Jim&quot;)\n            </td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Address</td>\n\t\t\t\t\t\t<td>534 Erewhon, Pleasantville, Vic, 3999</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Contacts</td>\n\t\t\t\t\t\t<td>Home: unknown. Work: (03) 5555 6473</td>\n\t\t\t\t\t</tr>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<td>Id</td>\n\t\t\t\t\t\t<td>MRN: 12345 (Acme Healthcare)</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>"
  },
  "identifier": [
    {
      "use": "usual",
      "type": {
        "coding": [
          {
            "system": "http://terminology.hl7.org/CodeSystem/v2-0203",
            "code": "MR"
          }
        ]
      },
      "system": "urn:oid:1.2.36.146.595.217.0.1",
      "value": "12345",
      "period": {
        "start": "2001-05-06"
      },
      "assigner": {
        "display": "Acme Healthcare"
      }
    }
  ],
  "active": true,
  "name": [
    {
      "use": "official",
      "family": "Chalmers",
      "given": [
        "Peter",
        "James"
      ]
    },
    {
      "use": "usual",
      "given": [
        "Jim"
      ]
    },
    {
      "use": "maiden",
      "family": "Windsor",
      "given": [
        "Peter",
        "James"
      ],
      "period": {
        "end": "2002"
      }
    }
  ],
  "telecom": [
    {
      "use": "home"
    },
    {
      "system": "phone",
      "value": "(03) 5555 6473",
      "use": "work",
      "rank": 1
    },
    {
      "system": "phone",
      "value": "(03) 3410 5613",
      "use": "mobile",
      "rank": 2
    },
    {
      "system": "phone",
      "value": "(03) 5555 8834",
      "use": "old",
      "period": {
        "end": "2014"
      }
    }
  ],
  "gender": "male",
  "birthDate": "1974-12-25",
  "_birthDate": {
    "extension": [
      {
        "url": "http://hl7.org/fhir/StructureDefinition/patient-birthTime",
        "valueDateTime": "1974-12-25T14:35:45-05:00"
      }
    ]
  },
  "deceasedBoolean": false,
  "address": [
    {
      "use": "home",
      "type": "both",
      "text": "534 Erewhon St PeasantVille, Rainbow, Vic  3999",
      "line": [
        "534 Erewhon St"
      ],
      "city": "PleasantVille",
      "district": "Rainbow",
      "state": "Vic",
      "postalCode": "3999",
      "period": {
        "start": "1974-12-25"
      }
    }
  ],
  "contact": [
    {
      "relationship": [
        {
          "coding": [
            {
              "system": "http://terminology.hl7.org/CodeSystem/v2-0131",
              "code": "N"
            }
          ]
        }
      ],
      "name": {
        "family": "du Marché",
        "_family": {
          "extension": [
            {
              "url": "http://hl7.org/fhir/StructureDefinition/humanname-own-prefix",
              "valueString": "VV"
            }
          ]
        },
        "given": [
          "Bénédicte"
        ]
      },
      "telecom": [
        {
          "system": "phone",
          "value": "+33 (237) 998327"
        }
      ],
      "address": {
        "use": "home",
        "type": "both",
        "line": [
          "534 Erewhon St"
        ],
        "city": "PleasantVille",
        "district": "Rainbow",
        "state": "Vic",
        "postalCode": "3999",
        "period": {
          "start": "1974-12-25"
        }
      },
      "gender": "female",
      "period": {
        "start": "2012"
      }
    }
  ],
  "managingOrganization": {
    "reference": "Organization/1"
  }
}

First of all, we have created 2 Lookup tables for mapping the types of names and the authority for assigning the medical record number (MR), the first to be compatible with the types managed by the EMPI and the second to be recognize the assignment authority that generated the identifier:

Set tName.Type=^Ens.LookupTable("TypeOfName",name.use)
Set hubRequest.AssigningAuthority = ^Ens.LookupTable("hospital",identifier.system)
Set hubRequest.Facility = ^Ens.LookupTable("hospital",identifier.system)

Launching a test message

Perfect, let's launch our FHIR message against our endpoint that we defined in the previous article:

As you can see we have received a 200 response, this only means that the EMPI has correctly received the message, now let's see the trace that has been generated in our production:

Here we have our patient, as you can see the transformation has been carried out successfully and all the fields that were reported in the FHIR message have been correctly assigned. As we can see, an IDUpdateNotificationRequest notification message has been generated. This type of notification is generated when an operation to create or update patients is carried out in the system.

Very well, let's check that the patient is correctly registered in our system by performing a search for it by her name and surname:

BINGO! Let's see more in detail the data of our dear Peter:

Perfect, we already have in our EMPI all the necessary information about the patient. As you can see the mechanism is quite simple, let's review the steps we have carried out:

  1. We have installed the FHIR Adapter tool provided by InterSystems in a NAMESPACE configured to work with interoperability (a different namespace than the one generated by EMPI standalone installation, which in my case I have called WEBINAR).
  2. We have created a Business Operation in this namespace that transforms the received message of type HS.FHIRServer.Interop.Request to a %String that it sends to a Business Service configured in the production of the EMPI namespace (HSPIDATA).
  3. Next we have added the Business Service of the EnsLib.TCP.PassthroughService class that receives the message sent from the production of the WEBINAR namespace and redirects to the Business Process Local.BP.FHIRProcess.
  4. In the BP Local.BP.FHIRProcess we have transformed the Stream received into an object of type HS.Message.AddUpdateHubRequest and we have sent it to the Business Operation HS.Hub.MPI.Manager that will be in charge of registering it in our EMPI.

As you can see, the union of the EMPI functionalities with those provided by the IRIS integration engine allows us to work with practically any type of technology.

I hope this article has been useful to you. If you have any questions or suggestions, you already know, leave a comment and I will be happy to answer you.

Discussion (0)1
Log in or sign up to continue