Artículo
· 17 jul, 2023 Lectura de 10 min

Instalación y adaptación de EMPI en modo Standalone - Alimentando a la bestia con FHIR - Transformación e ingestión

Hola de nuevo a todos.

En nuestro artículo anterior vimos como configurar nuestro EMPI para recibir mensajería FHIR. Para ello instalábamos el Adaptador FHIR que InterSystems pone a nuestra disposición que configuraba un endpoint REST al que podíamos enviar nuestro mensaje FHIR. A continuación obteníamos el mensaje y lo transformábamos a un %String que enviábamos vía TCP a la producción de nuestro EMPI configurada en nuestro namespace HSPIDATA.

Muy bien, es el momento de ver como recuperamos el mensaje, lo transformamos nuevamente a un %DynamicObject y lo parseamos a la clase usada por el EMPI para almacenar la información.

Recepción de mensaje por TCP

Como hemos indicado, desde la producción que tiene configurada la recepción de recursos FHIR hemos enviado un mensaje a un puerto específico TCP en el que tenemos escuchando un Business Service, en nuestro caso este Business Service será un simple EnsLib.TCP.PassthroughService cuyo objetivo es capturar el mensaje y reenviarlo a un Business Process donde realizaremos la transformación de datos precectiva.

Aquí tenemos nuestro Business Service:

Y aquí la configuración básica de la misma:

Transformación de nuestro mensaje FHIR

Como podéis ver sólo hemos configurado el puerto en el que se va a recibir nuestro mensaje vía TCP y el componente al que vamos a enviar nuestro mensaje, en nuestro caso lo hemos llamado Local.BP.FHIRProcess, echemos un vistazo a dicha clase para ver como recuperamos la información de nuestro recurso FHIR:

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>
}

}

Veamos un poco más en detalle qué estamos haciendo:

Primeramente recibimos el mensaje enviado desde el Business Service:

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

Como podemos ver en la firma del método OnRequest el mensaje de entrada corresponde a una clase del tipo Ens.StreamContainer, esta transformación de nuestro mensaje de tipo %String se ha realizado en el Business Service. En la primera línea del método lo que haremos será recuperar el mensaje que se encuentra como un Stream dentro de la variable pRequest. A continuación lo transformamos a un %DynamicObject mediante la instrucción %FromJSON.

Con nuestro mensaje mapeado a un objeto dinámico podremos acceder a cada uno de los campos del recurso FHIR que hemos enviado:

	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)   

En este fragmento vemos como creamos un objecto de la clase HS.Message.AddUpdateHubRequest que es el que enviaremos al Business Operation HS.Hub.MPI.Manager encargado de realizar las operaciones correspondientes dentro del EMPI, ya sea la creación del nuevo paciente o la actualización del mismo, así como vincularlo con las posibles coincidencias que pueda haber con otros pacientes ya dentro del EMPI.

El siguiente paso es poblar el nuevo objeto con los datos recibidos desde el Business Service, como podéis observar lo único que hacemos es recuperar los datos de los diferentes campos del objecto dinámico que acabamos de crear. El formato del objeto dinámico corresponde exáctamente con el definido por HL7 FHIR para el recurso paciente, podéis ver ejemplos directamente en la página de HL7 FHIR

Para nuestro ejemplo hemos elegido este paciente de la lista que nos proporciona la propia página de HL7 FHIR:

{
  "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"
  }
}

Antes de nada hemos creado 2 tablas Lookup para el mapeo de los tipos de nombre y de la autoridad de asignación del número de historia clínica (MR), la primera para que sea compatible con los tipos manejados por el EMPI y el segundo para que se reconozca la autoridad de asignación que generó el identificador:

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

 

Lanzando un mensaje de pruebas

Perfecto, lancemos nuestro mensaje FHIR contra nuestro endpoint que definimos en el artículo anterior:

Como véis hemos recibido un 200 de respuesta, esto sólo significa que el EMPI ha recibido correctamente el mensaje, veamos ahora la traza que se ha generado en nuestra producción:

Aquí tenemos a nuestro paciente, como podéis ver se ha realizado la transformación con éxito y se han asignado correctamente todos los campos que venían informados en el mensaje FHIR. Como podemos observar se ha generado un mensaje de notificación IDUpdateNotificationRequest, este tipo de notificaciones se generan cuando se realizar alguna operación de creación o actualización de pacientes en el sistema.

Muy bien, comprobemos que el paciente está correctamente registrado en nuestro sistema realizando una búsqueda del mismo por su nombre y apellidos:

¡BINGO!  Veamos más en detalle los datos de nuestro querido Peter:

Perfecto, ya tenemos en nuestro EMPI toda la información necesaria del paciente. Como véis el mecanismo es bastante sencillo, repasemos los pasos que hemos realizado:

  1. Hemos instalado la herramienta de FHIR Adapter que nos proporciona InterSystems en un NAMESPACE configurado para trabajar con interoperabilidad (un namespace diferente el generado por el EMPI que en mi caso he llamado WEBINAR).
  2. Hemos creado en este namespace un Business Operation que transforma el mensaje recibido de tipo HS.FHIRServer.Interop.Request a un %String que enviará a un Business Service configurado en la producción del namespace del EMPI (HSPIDATA).
  3. A continuación hemos añadido el Business Service de la clase EnsLib.TCP.PassthroughService que recibe el mensaje enviado desde la producción del namespace WEBINAR y redirige al Business Process  Local.BP.FHIRProcess.
  4. En el BP Local.BP.FHIRProcess hemos transformado el Stream recibido a un objeto del tipo HS.Message.AddUpdateHubRequest y lo hemos enviado al Business Operation HS.Hub.MPI.Manager que se encargará de registrarlo en nuestro EMPI.

Como véis, la unión de las funcionalidades del EMPI con las proporcionadas por motor de integración de IRIS nos permite trabajar prácticamente con cualquier tipo de tecnología.

Espero que os haya resultado útil este artículo. Si tenéis alguna pregunta o sugerencia ya sabéis, dejad un comentario y estaré encantado de contestaros.

Comentarios (0)1
Inicie sesión o regístrese para continuar