Article
· 4 hr il y a 7m de lecture

Flux de tâches avec le moteur InterSystems IRIS Workflow Engine - Connexion à des applications externes

Enfin et avec un peu de retard, nous concluons cette série d'articles sur notre moteur de Workflow en montrant un exemple de connexion que nous pourrions établir à partir d'une application mobile.

Dans l'article précédent, nous avons présenté un exemple d'application permettant un contrôle détaillé d'une pathologie chronique telle que l'hypertension, tant pour le patient que pour son médecin associé. Dans cet exemple, le patient pourra accéder à une application web à partir de son téléphone portable (en fait, à une page web conçue pour s'adapter à cet appareil) dans laquelle il recevra des notifications basées sur les mesures que le tensiomètre portable envoie à l'instance IRIS.

Par conséquent, nous aurons deux accès différents à notre instance IRIS:

  • Accès utilisateur à partir d'une application mobile.
  • Accès à l'appareil pour soumettre les lectures de tension artérielle.

Dans cet article, nous verrons le premier d'entre eux qui permet aux patients de gérer les tâches que leurs lectures génèrent.

Application mobile de connexion - IRIS

Pour réaliser cette connexion, le plus simple est de configurer une application web dans IRIS et pour ce faire, nous y accéderons à partir du portail de gestion, System Administration -> Security -> Applications -> Web Applications (Administration du système > Sécurité > Applications > Applications web):

Ensuite, dans la liste affichée, nous cliquerons sur Create new application (Créer une nouvelle application), ce qui ouvrira un écran comme le suivant:

Sur cet écran, configurons les champs suivants:

  • Nom: dans ce champ, nous définirons l'URL à publier pour donner accès à notre fonctionnalité déployée dans IRIS.
  • Espace de Noms: l'espace de noms auquel nous voulons que l'application web soit associée, ce qui nous permettra plus tard de profiter des fonctionnalités des productions d'interopérabilité.
  • REST: Nous sélectionnerons cette option car ce que nous allons publier est une API REST pour autoriser les connexions HTTP.
  • Classe de répartition: Classe ObjectScript qui recevra l'appel HTTP et décidera quoi en faire.
  • Utilisation de l'authentification JWT: en cochant cette option, les points de terminaison /login et /logout seront activés sur l'URL que nous avons définie pour notre application, ce qui nous permettra d'obtenir un jeton Web JSON afin d'authentifier nos appels via IRIS.
  • Paramètres de sécurité -> Méthodes d'authentification autorisées: nous allons définir un mot de passe pour sécuriser nos appels.

Jetons un coup d'œil à notre classe 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"
    // ici vous pouvez vérifier les origines spécifiques
    // sinon, toutes les origines seront autorisées (utile uniquement lors du développement)
    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
        // Lecture du corps de l'appel http avec les données relatives à la personne
        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
}

}

Comme vous pouvez le voir, nous résoudrons toute la logique dont nous avons besoin à partir de notre WS, mais je ne recommande pas de procéder de cette manière, car nous perdons la traçabilité possible en envoyant les requêtes reçues à la production configurée dans l'espace de noms Namespace.

En jetant un coup d'œil à la section URLMap, nous verrons que nous avons 2 points de terminaison configurés dans notre WS:

  • getTasks: pour récupérer toutes les tâches en attente de l'utilisateur (si vous voyez le SQL utilisé, nous passons le nom d'utilisateur directement à partir de la variable générée lors de la connexion).
  • saveTask: pour recevoir la réponse de l'utilisateur à la tâche et la terminer en exécutant la méthode CompleteTaks de la classe EnsLib.Workflow.TaskResponse.

De la part d'IRIS, tout serait déjà configuré pour que l'application externe se connecte.

Test de l'application externe avec le moteur de Workflow

Tout d'abord nous allons simuler l'envoi d'un message de HL7 vers notre production en copiant le fichier /shared/hl7/message_1_1.hl7 vers le chemin /shared/in, examinons donc le message que nous envoyons:

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

Pour ceux d'entre vous qui ne se rappellent pas les articles précédents, nous avions défini dans notre BPL qu'une alerte serait générée lorsque la pression systolique dépassait 140 ou la pression diastolique dépassait 90, par conséquent, ce message devrait générer une tâche d'alerte pour notre patient avec DNI (Document National d'Identification) 07751332X et une autre tâche automatique qui restera ouverte jusqu'à ce qu'IRIS reçoive le nouveau message.

Vérifions notre application mobile:

Deux tâches sont générées : la première est définie comme manuelle et dépend de l'utilisateur ; la seconde est définie comme automatique et, pour qu'elle disparaisse, l'utilisateur doit effectuer une nouvelle lecture avec son tensiomètre . Si nous examinons l'appel HTTP, nous pouvons voir comment nous y avons inclus le JWT:

Si l'utilisateur clique sur le bouton Accepter de la tâche en attente, le flux restera en attente de la lecture du tensiomètre et ne passera pas aux étapes suivantes. Une fois que la tâche manuelle est acceptée et qu'une nouvelle lecture du tensiomètre est reçue, dans laquelle les limites marquées sont à nouveau dépassées, le système génère deux nouvelles tâches d'avertissement, l'une pour le patient et l'autre pour le médecin associé, afin de l'avertir d'une crise possible:

Parfait! Nous avons déjà mis en place notre système de notification des patients de manière simple et rapide.

Conclusion

Comme vous l'avez vu dans cette série d'articles, la fonctionnalité du moteur de Workflow ainsi que les capacités d'interopérabilité d'InterSystems IRIS offrent un potentiel incroyable pour la mise en œuvre de processus métier que peu d'autres solutions métier peuvent fournir. Il est vrai que certaines connaissances techniques peuvent être nécessaires pour en tirer le maximum, mais le jeu en vaut vraiment la chandelle.

Discussion (0)2
Connectez-vous ou inscrivez-vous pour continuer