Artículo
· 7 jun, 2024 Lectura de 7 min

Flujos de tareas con InterSystems IRIS Workflow Engine - Conexión con aplicaciones externas

Finalmente y con un poco de retraso, concluimos esta serie de artículos sobre nuestro Workflow Engine mostrando un ejemplo de la conexión que podríamos hacer desde una aplicación para móvil.

En el artículo anterior mostrábamos el ejemplo que íbamos a tratar, una aplicación que permita realizar un control pormenorizado de una patología crónica como puede ser la hipertensión tanto al paciente como a su médico asociado. En este ejemplo el paciente dispondrá de acceso desde su móvil a una aplicación web (básicamente, una página web diseñada para ser responsive con el dispositivo) en la que recibirá notificaciones en base a las lecturas que el tensiómetro portatil remita a la instancia de IRIS.

Por lo tanto tendremos dos accesos diferentes a nuestra instancia de IRIS:

  • Acceso del usuario desde aplicación móvil.
  • Acceso del dispositivo para remitir las lecturas de tensión arterial.

En este artículo veremos la primera de ellas que permite al paciente gestionar las tareas que sus lecturas vayan generando.

Conexión aplicación móvil - IRIS

Para esta conexión lo más sencillo es configurar una aplicación web dentro de IRIS y para ello accederemos desde el portal de gestión, System Administration -> Security -> Applications -> Web Applications:

A continuación en la lista que se nos muestra pulsaremos Create new application que nos abrirá una pantalla como la siguiente:

En esta pantalla configuremos los siguientes campos:

  • Name: en el que definiremos la URL que publicaremos  para dar acceso a nuestra funcionalidad desplegada en IRIS.
  • Namespace: el namespace al que queremos que esté asociada la aplicación web, esto nos permitirá posteriormente aprovechar las funcionalidades de las producciones de interoperabilidad.
  • REST: seleccionaremos esta opción ya que lo que vamos a publicar es una API de REST para permitir conexiones HTTP.
  • Dispatch Class: clase de ObjectScript que recibirá la llamada HTTP y decidirá que hacer con la misma.
  • Use JWT Authentication: marcando esta opción se nos habilitarán sobre la URL que hemos definido para nuestra aplicación los endpoint /login /logout que nos permitirá obtener un JSON Web Token para autentificar nuestras llamadas sobre IRIS.
  • Security Settings -> Allowed Authentication Methods: que marcaremos a password para securizar nuestras llamadas.

Echemos un vistazo a nuestra clase Workflow.WS.Service:

Class Workflow.WS.Service Extends %CSP.REST
{

Parameter HandleCorsRequest = 0;
Parameter CHARSET = "utf-8";
XData UrlMap [ XMLNamespace = "https://www.intersystems.com/urlmap" ]
{
<Routes>
	<Route Url="/getTasks" Method="GET" Call="GetTasks" />
    <Route Url="/saveTask" Method="POST" Call="SaveTask" />
</Routes>
}

ClassMethod OnHandleCorsRequest(url As %String) As %Status
{
	set url = %request.GetCgiEnv("HTTP_REFERER")
    set origin = $p(url,"/",1,3) // origin = "http(s)://origin.com:port"
    // here you can check specific origins
    // otherway, it will allow all origins (useful while developing only)
	do %response.SetHeader("Access-Control-Allow-Credentials","true")
	do %response.SetHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE,OPTIONS")
	do %response.SetHeader("Access-Control-Allow-Origin",origin)
	do %response.SetHeader("Access-Control-Allow-Headers","Access-Control-Allow-Origin, Origin, X-Requested-With, Content-Type, Accept, Authorization, Cache-Control")
	quit $$$OK
}

ClassMethod GetTasks() As %Status
{
    Try {
        Do ##class(%REST.Impl).%SetContentType("application/json")
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
        Do ##class(%REST.Impl).%SetStatusCode("200")
        set sql = "SELECT %Actions, %Message, %Priority, %Subject, TaskStatus_TimeCreated, ID FROM EnsLib_Workflow.TaskResponse WHERE TaskStatus_AssignedTo = ? AND TaskStatus_IsComplete = 0"
        set statement = ##class(%SQL.Statement).%New(), statement.%ObjectSelectMode = 1
        set status = statement.%Prepare(sql)
        if ($$$ISOK(status)) {
            set resultSet = statement.%Execute($USERNAME)
            if (resultSet.%SQLCODE = 0) {
                set tasks = []
                while (resultSet.%Next() '= 0) {
                    set task = {"actions": "", "message": "", "priority": "", "subject": "", "creation": "", "id": ""}
                    set task.actions = resultSet.%GetData(1)
                    set task.message = resultSet.%GetData(2)
                    set task.priority = resultSet.%GetData(3)
                    set task.subject = resultSet.%GetData(4)
                    set task.creation = resultSet.%GetData(5)
                    set task.id = resultSet.%GetData(6)
                    do tasks.%Push(task)
                }                
            }
        }
        set result = {"username": ""}
        set result.username = $USERNAME
        Do ##class(%REST.Impl).%WriteResponse(tasks)
        
    } Catch (ex) {
        Do ##class(%REST.Impl).%SetStatusCode("400")
        return ex.DisplayString()
    }

    Quit $$$OK
}

ClassMethod SaveTask() As %Status
{
    Try {
        Do ##class(%REST.Impl).%SetContentType("application/json")
        If '##class(%REST.Impl).%CheckAccepts("application/json") Do ##class(%REST.Impl).%ReportRESTError(..#HTTP406NOTACCEPTABLE,$$$ERROR($$$RESTBadAccepts)) Quit
        // Reading the body of the http call with the person data
        set dynamicBody = {}.%FromJSON(%request.Content)
        
        set task = ##class(EnsLib.Workflow.TaskResponse).%OpenId(dynamicBody.%Get("id"))
        set sc = task.CompleteTask(dynamicBody.action)

        if $$$ISOK(sc) {	        
            Do ##class(%REST.Impl).%SetStatusCode("200")
            Do ##class(%REST.Impl).%WriteResponse({"result": "success"})         
		}	
        
    } Catch (ex) {
        Do ##class(%REST.Impl).%SetStatusCode("400")
        Do ##class(%REST.Impl).%WriteResponse({"result": "error"})
    }

    Quit $$$OK
}

}

Como podéis observar, desde nuestro WS resolvemos toda la lógica que necesitamos pero no os recomiendo hacer esto, ya que perdemos la trazabilidad que podríamos conseguir enviando las peticiones recibidas a la producción configurada en el Namespace.

Echando un vistazo a la sección de URLMap veremos que tenemos 2 endpoints configurados en nuestro WS:

  • getTasks: para recuperar todas las tareas pendientes del usuario (si veis la SQL utilizada estamos pasando el nombre de usuario directamente de la variable generada al loguearse).
  • saveTask: en las que recibiremos la respuesta del usuario a la tarea y procederemos a finalizarla ejecutando el método CompleteTaks de la clase EnsLib.Workflow.TaskResponse.

Por parte de IRIS ya estaría todo configurado para que la aplicación externa se conecte.

Probando la aplicación externa con el Workflow Engine

Primeramente simularemos el envío de un mensaje de HL7 a nuestra producción copiando el fichero /shared/hl7/message_1_1.hl7 a la ruta /shared/in, veamos el mensaje que estamos enviando:

MSH|^~\&|HIS|HULP|EMPI||20240402111716||ADT^A08|346831|P|2.5.1
EVN|A08|20240402111716
PID|||07751332X^^^MI^NI~900263^^^HULP^PI||LÓPEZ CABEZUELA^ÁLVARO^^^||19560121|F|||PASEO MARIO FERNÁNDEZ 258 1 DERECHA^^MADRID^MADRID^28627^SPAIN||555819817^PRN^^ALVARO.LOPEZ@VODAFONE.COM|||||||||||||||||N|
PV1||N
OBX|1|NM|162986007^Pulso^SNM||72|^bpm|||||F|||20240402111716
OBX|2|NM|105723007^Temperatura^SNM||37|^Celsius|||||F|||20240402111716
OBX|3|NM|163030003^Presión sanguínea sistólica^SNM||142|^mmHg|||||F|||20240402111716
OBX|4|NM|163031004^Presión sanguínea diastólica^SNM||83|^mmHg|||||F|||20240402111716

Para los que no recordéis los anteriores artículos, habíamos definido en nuestro BPL que se generase una alerta cuando la presión sistólica superase los 140 o la diastólica los 90, por lo tanto, este mensaje deberá generar una tarea de aviso para nuestro paciente con DNI 07751332X y otra automática que se mantendrá abierta hasta que IRIS reciba la nueva notificación.

Veamos nuestra aplicación móvil:

Ahí tenemos 2 tareas generadas, la primera de ellas que hemos definida como manual y que depende del usuario para su finalización y la segunda que hemos declarado como automática y que para que desaparezca el usuario deberá realizar una nueva lectura con su tensiómetro. Si echamos un vistazo a la llamada HTTP podremos ver como hemos incluido el JWT en la misma :

Si el usuario pulsa en el botón Accept de la tarea pendiente el flujo se mantendrá a la espera de recibir la lectura del tensiómetro y no avanzará a los siguiente pasos. Una vez aceptada la tarea manual y recibida una nueva lectura del tensiómetro en la que nuevamente se superan los límites marcados el sistema generará dos nuevas tareas de aviso, una para el paciente y otra para el médico asociado en el que se advierte de una posible crisis:

¡Perfecto! Ya tenemos montado nuestro sistema de avisos para los pacientes de una forma sencilla y rápida.

Conclusión

Como habéis podido ir viendo en esta serie de artículos la funcionalidad del Workflow Engine junto con las capacidades de interoperabilidad de InterSystems IRIS proveen de una potencialidad increible para la implementación de procesos de negocio que pocas otras soluciones empresariales pueden aportar. Es cierto que quizás sea necesario conocimientos técnicos para sacarle todo el jugo pero realmente merece la pena el esfuerzo requerido.

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