#Ensemble

0 Abonnés · 86 Publications

InterSystems Ensemble est une plateforme d'intégration complète et facile à utiliser qui permet aux utilisateurs de connecter les personnes, les processus et les applications en un temps record.

En savoir plus

Documentation

InterSystems officiel Sylvain Guilbaud · Mai 9, 2023

InterSystems a corrigé un défaut pouvant entraîner la corruption des bases de données et des fichiers journaux sur les systèmes AIX avec IBM POWER8 ou des processeurs POWER ultérieurs. Ce défaut peut être déclenché uniquement lorsque le chiffrement de la base de données ou du journal est utilisé.

Pour déclencher ce défaut, les conditions suivantes sont requises :

0
0 55
Article Sylvain Guilbaud · Avr 26, 2023 3m read

Mise à jour : ajout du support pour le modèle de régression Bonjour à toutes et à tous !

Dans ce bref article, je vous montrerai comment écrire un adaptateur pour IRIS Interoperability afin d'utiliser des modèles ML gérés par IRIS IntegratedML.

L'adaptateur

L'adaptateur utilise simplement les fonctions SQL d'IntegratedML comme prédiction PREDICT et probabilité PROBABILITY, pour obtenir la classe prédite par le modèle et sa probabilité. Il s'agit d'un simple SQL :
![code1][code1] [code1]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-it-works-1.png
Notez que le nom du modèle est référencé par la propriété Model. Cette propriété doit être définie dans la classe hôte qui utilise l'adaptateur, sinon une exception sera produite. Par exemple:


La liste des modèles dans les paramètres de l'adaptateur est établie en deux étapes :
  1. Création d'une méthode dans une classe qui étend %ZEN.Portal.ContextSearch pour charger tous les modèles de classification et les renvoyer (dc.Ens.Adapter.ClassficationMLContextSearch)
    ![code2][code2] [code2]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-it-works-2.png
  2. Configuration d'une telle classe et d'une telle méthode comme chargeur pour la propriété Model dans le paramètre SETTINGS de la classe adaptateur (dc.Ens.Adapter.ClassificationMLAdapter)
    ![code3][code3] [code3]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-it-works-3.png
    Pour les modèles de régression, il existe la classe dc.Ens.Adapter.RegressionMLContextSearch, qui charge tous les modèles de régression.

Dans ce cas, le filtre MODE_TYPE est réglé sur "régression" au lieu de "classification" :

Utilisation de l'adaptateur

Pour la démonstration, j'ai simulé un système simple de paiement par carte de crédit avec des fonctions de détection des fraudes, à l'aide d'un modèle de classification ML. Lorsqu'une transaction suspecte est détectée, une alerte est émise.

Pour utiliser l'adaptateur, créez une classe hôte (une classe de processus métier "Business Process" ou d'opération métier "Business Operation") qui utilise comme adaptateur la classe dc.ENS.Adapter.ClassificationMLAdapter.
![code4][code4] [code4]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-to-use-it-1.png
Vous pouvez maintenant utiliser la méthode Classify() de l'adaptateur et fournir un échantillon des caractéristiques attendues par le modèle :
Pour l'utiliser, créez une classe hôte (une classe de processus métier ou d'opération métier) qui utilise comme adaptateur la classe dc.ENS.Adapter.ClassificationMLAdapter
![code5][code5] [code5]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-to-use-it-2.png
Vous pouvez les utiliser selon vos besoins. Dans l'exemple, seul le résultat de la prédiction de la fraude était nécessaire, la classe d'opération métier "Business Operation" utilise donc simplement la valeur renvoyée dans la propriété "Predicted" :
![code6][code6] [code6]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/how-to-use-it-4.png
Pour les modèles de régression, les résultats sont modélisés par la classe dc.Ens.Adapter.RegressionResult. Cette classe possède une propriété appelée Estimated.

Pour les modèles de régression, les résultats sont modélisés par la classe dc.Ens.Adapter.RegressionResult. Cette classe possède une propriété appelée Estimated. Pour obtenir une valeur estimée à partir d'un échantillon, la classe d'adaptateur pour le modèle de régression dispose de la méthode "Estimate".

Le résultat final est affiché ci-dessous :
![code7][code7] [code7]: https://raw.githubusercontent.com/jrpereirajr/interoperability-integratedml-adapter/master/img/KMWbgqw1C9.gif
Le code complet est disponible dans OpenExchange. J'espère que cela pourra vous être utile. José

0
0 51
InterSystems officiel Adeline Icard · Fév 21, 2023

InterSystems met périodiquement à jour ses politiques et pratiques en matière de publication de logiciels afin de s'adapter aux besoins des clients.

Nous sommes en train de modifier notre cadence de publication des mises à jour afin d'être plus prévisibles pour les clients et les partenaires, et de modifier quelques autres aspects.

Cet article résume la cadence de publication de nos produits "Data Platforms" (Plateformes de données) et les changements récents qui y ont été apportés, et vous annonce quelques nouvelles mises à jour.

Pourquoi changer ?

  • Nos clients acceptent plus rapidement nos nouvelles versions.
  • Les problèmes de sécurité sont plus fréquents, notamment dans les bibliothèques de tiers.
  • Nos clients demandent des dates de livraison plus prévisibles.

Qu'est-ce qui n'a pas changé ? Rappel de notre cadence de publication des fonctionnalités

InterSystems utilise une cadence de publication de fonctionnalités à deux flux avec InterSystems IRIS depuis 2018 (voir l' annonce originale original announcement). Nous fournissons :

  • Livraison continue (CD) des versions—Ces versions permettent d'accéder rapidement aux nouvelles fonctionnalités et sont idéales pour le développement et le déploiement d'applications qui sont continuellement mises à jour et peuvent bénéficier immédiatement des nouvelles fonctionnalités. On l'appelle parfois le "train rapide".
  • Les versions de maintenance prolongée (EM)—ces versions sont moins fréquentes que les versions de livraison continue mais elles apportent la stabilité accrue des versions de maintenance. Elles sont idéales pour les grandes applications d'entreprise où la facilité d'obtenir des correctifs dans les versions de maintenance est plus importante que d'obtenir un accès précoce aux nouvelles fonctionnalités. On l'appelle parfois le train lent.

Les versions EM sont faciles à identifier car leur numéro de version est YYYY.1 (par exemple 2022.1 ou 2023.1). Les versions CD auront un numéro de version de la forme YYYY.2, YYYY.3 etc.

Il y a un an, nous avons fait évoluer notre cadence, en ajoutant des kits pour les versions CD et en ajoutant HealthShare Health Connect dans ces trains de versions aux côtés d'InterSystems IRIS et d'InterSystems IRIS for Health. ( Voir la mise à jour février 2022). Quelques restrictions sur les versions CD du train rapide restent en vigueur : il n'y a pas de mises à jour de maintenance ou de sécurité ; il n'y a pas de conversion sur place à partir de Caché ou Ensemble ; et le chemin de mise à jour pour une version CD est limité à la version CD suivante ou à la version EM suivante.

Les versions de fonctionnalités (EM et CD) passent par une phase de prévisualisation au cours de laquelle les clients peuvent télécharger et utiliser les nouvelles versions, afin de se préparer à la sortie de la nouvelle version. Les prévisualisations sont un moment idéal pour fournir des commentaires et des tests afin de s'assurer que votre application fonctionne bien avec la nouvelle version. À partir de la version 2022.2, nous avons commencé à mettre à jour les prévisualisations toutes les deux semaines, toujours le mercredi.

Les réactions à la cadence de publication et à ces mises à jour ont été très positives, et nous avons pu gérer la cadence de publication à deux flux tout en maintenant une très haute qualité.

Mises à jour des plateformes

Les clients adoptent de nouveaux systèmes d'exploitation beaucoup plus rapidement, notamment dans le cloud. Nous avons modifié notre cadence en conséquence. En 2022, nous avons commencé à ajouter la prise en charge de nouveaux systèmes d'exploitation dans les versions de maintenance. La version 2022.1.1 a ajouté la prise en charge d'Ubuntu 22.04 et la version 2022.1.2 celle de RHEL 9. Cette approche signifie que les clients peuvent adopter de nouveaux systèmes d'exploitation beaucoup plus tôt.

Les changements de sécurité sont plus fréquents, en particulier pour les bibliothèques communes fournies avec ces systèmes d'exploitation, comme OpenSSL. Avec notre version 2022.1, nous avons commencé à utiliser les bibliothèques OpenSSL du système d'exploitation, afin que les clients puissent se tenir au courant des mises à jour de sécurité via leur système d'exploitation. Cela signifie également la compilation et l'emballage de kits séparés pour chaque version majeure d'un système d'exploitation Linux. Nous limitons ces kits à deux versions majeures dans chaque version EM. Si nous introduisons une nouvelle prise en charge du système d'exploitation dans une version de maintenance, nous ne supprimerons pas les versions antérieures, de sorte qu'il peut y avoir trois ensembles de kits ; cela est réduit à deux avec la prochaine version EM. Par exemple, la version 2022.1.2 comporte trois ensembles de kits Red Hat (RHEL 7, RHEL 8 et RHEL 9) ; la version 2022.1.3 comportera les mêmes ensembles de kits, mais la version 2023.1.0 ne comportera que les kits RHEL 8 et RHEL 9.

Comme les changements de plateforme s'accélèrent, nous voulons donner aux clients une visibilité sur ce qui se prépare. Nous avons introduit une "mise à jour des plateformes" trimestrielle sous forme de bulletin d'information ; vous pouvez lire le premier numéro sur la communauté des développeurs. Veuillez nous faire part de vos commentaires sur le format, l'horizon temporel, etc.

Maintenance et mises à jour de sécurité

Nous continuons à fournir des mises à jour de maintenance sur InterSystems IRIS pendant deux ans, ainsi que des mises à jour de maintenance sur Caché et Ensemble (voir Version minimale prise en charge). Outre les mises à jour de maintenance, nous fournissons des corrections de sécurité.

Nous faisons référence à la suite de versions qui mettent à jour une version EM, sur tous les produits et plateformes associés, comme un flux. Par exemple, 2021.1.0, 2021.1.1, 2021.1.2 est un flux, tandis que 2022.1.0, 2022.1.1, 2022.1.2 est un autre flux. Cela signifie que nous fournissons des versions de maintenance pour trois flux (l'EM le plus récent et les EM précédents d'InterSystems IRIS, d'InterSystems IRIS for Health et de Health Connect, ainsi que Caché et Ensemble, qui constitue son propre flux).

À partir d'avril 2023, InterSystems fournira des corrections de sécurité pour les versions actuelles et les versions des trois dernières années d'InterSystems IRIS, ainsi que pour la dernière version de maintenance de Caché. Cela signifie que les corrections de sécurité sont fournies pour deux flux supplémentaires au-delà des mises à jour de maintenance (cinq flux au total). Par exemple, en 2024, InterSystems fournira des corrections de sécurité pour les versions InterSystems IRIS 2021.1.x, 2022.1.x, 2023.1.x, ainsi que pour la version 2024.1.x alors en vigueur ; InterSystems fournira également des corrections de sécurité pour Caché 2018.1.x. 

Nous avons récemment amélioré notre politique de traitement des vulnérabilités de sécurité pour tenir compte du volume plus important de problèmes de sécurité que nous constatons, dont la plupart sont de gravité faible ou moyenne (voir Politique de traitement des vulnérabilités de sécurité mise à jour). Nous incluons désormais des mises à jour de sécurité dans chaque version. Les informations sur les problèmes de gravité élevée et critique font l'objet d'un embargo (pour éviter de fournir des informations qui pourraient être utilisées pour exploiter des brèches de sécurité) jusqu'à ce que ces problèmes soient traités dans tous les flux pris en charge - à ce moment-là, nous émettons une alerte de sécurité avec les détails des vulnérabilités qui ont été traitées.

Des versions de maintenance prévisibles

Les clients nous disent qu'ils apprécient de recevoir régulièrement des mises à jour logicielles et qu'ils veulent être en mesure de faire des plans en fonction de la date à laquelle ils peuvent les attendre. Nous sommes en train d'officialiser notre calendrier de publication des mises à jour, comme suit :

  • Flux le plus récent d'InterSystems IRIS : version de maintenance tous les trois mois.
  • Flux précédent d'InterSystems IRIS : version de maintenance tous les six mois.
  • Caché et Ensemble : version de maintenance tous les douze mois.
  • Nous avons publié une version de maintenance pour le flux InterSystems IRIS 2022.1 le 18 janvier (voir notre annonce de la version 2022.1.2). Nous prévoyons de publier des versions de maintenance pour le flux InterSystems IRIS 2021.1 et le flux Caché et Ensemble 2018.1 le 28 février.

    En 2023, nous prévoyons une version EM (2023.1) et deux versions CD (2023.2 et 2023.3). Une fois que 2023.1.0 est généralement disponible (GA), il devient le flux IRIS d'InterSystems le plus récent, et 2022.1 devient le flux précédent.

    Suppression des versions précédentes de la CMR (mais pas de l'ICR)

    Parce que nos sorties sont devenues plus fréquentes et que nous publions plus de kits (un par version majeure de Linux OS), le nombre de versions disponibles sur le site de distribution de logiciels du CMR a considérablement augmenté et est devenu déroutant pour certains clients. Nous adoptons une nouvelle pratique consistant à retirer régulièrement les anciennes versions de chaque flux du site de distribution.

    • Seule la version la plus récente du CD sera visible - parce que 2022.3 est maintenant généralement disponible, nous retirerons les images 2022.2 à la fin du mois de février.
    • Seule la version de maintenance la plus récente par flux sera visible - 2022.1.1 a été supprimée lors de la publication de 2022.1.2. Cela permet d'éviter le problème des clients qui installent par erreur des logiciels présentant des problèmes de sécurité connus.

    Les versions précédentes sont disponibles sur demande. Nous suggérons également aux clients qui standardisent une version unique pour de nombreux sites de conserver leur propre copie du kit pour cette version. Pour garantir l'intégrité, tous les kits et conteneurs sont signés ; les fichiers checksum et les fichiers de signature PGP sont téléchargeables sur le site de distribution du CMR.

    Nous travaillons de manière différente avec les conteneurs publiés sur l'InterSystems Container Repository (ICR), car les clients utilisent généralement des versions spécifiques dans les pipelines CI/CD. Nous ne supprimerons pas les anciennes images de l'ICR avant qu'elles aient deux ans. Nous recommandons aux clients de maintenir leurs pipelines CI/CD à jour, et la rétroaction que nous recevons est celui qu'ils font.

    Engagement envers la réussite du client

    Tous les changements décrits dans cet article ont été effectués dans le but d'aider les clients à réussir. Nous sommes à l'écoute des préoccupations de nos clients concernant les problèmes de sécurité, l'adoption de la plate-forme, les mises à jour de maintenance et la cadence des versions, et nous modifions les choses en fonction de ces rétroactions. N'hésitez pas à nous contacter pour nous faire part de vos commentaires et suggestions !

    0
    0 76
    InterSystems officiel Adeline Icard · Fév 18, 2023

    InterSystems a corrigé un problème qui pouvait empêcher InterSystems IRIS® et Caché de tirer parti des pages volumineuses pour la mémoire partagée sous Windows, même si ces produits signalent que des pages volumineuses sont allouées. Cela peut avoir des effets néfastes sur les performances du système.

    Le problème résulte d'un changement dans Windows 10 qui nécessite la modification d'InterSystems IRIS® et de Caché. Notez que ce problème affecte également tous les produits InterSystems basés sur InterSystems IRIS® ou Caché. Le problème se produit sur les versions suivantes de Windows :

    0
    0 72
    Question Ewan Whyte · Déc 15, 2022

    J'essaie d'obtenir un compte de type de message spécifique avec une entrée spécifique et j'ai pensé que je pourrais construire la requête dans Message Viewer mais cela ne fournit pas de comptes (pour autant que je sache). Ainsi, lorsque je prends le SQL à partir de "Show Query", il omet les critères de segment comme le montre le code ci-dessous.

    J'ai attaché les critères qui ont été exclus. Est-ce possible ?

    Merci

    1
    0 109
    Article Lorenzo Scalese · Déc 21, 2022 4m read

    Cet article décrit et contient un exemple de la manière d'intégrer un fichier PDF externe dans un segment HL7, plus précisément dans ADT_A01:2.3.1 OBX().  Cela peut être utile lorsqu'on tente de mettre des images ou d'autres données externes dans un message HL7.  Dans cet exemple, le nom du fichier PDF à intégrer est fourni dans le message HL7 entrant dans le champ OBX(1):ObservationValue.


    Les stipulations de base de cet exemple sont les suivantes :

    1. Accepter un fichier PDF
    2. Accepter un message HL7
    3. En utilisant le nom du fichier PDF, intégrer les données PDF dans le message HL7
    4. Transmettre le message au fichier

      

    En utilisant EnsLib.File.InboundAdapter, l'exemple a deux Services Métiers configurés.  L'un est destiné à recevoir des fichiers PDF à l'aide de l'EnsLib.File.PassthroughService intégré.  L'autre accepte les messages HL7 en utilisant le service intégré EnsLib.HL7.Service.FileService.


    PDF Processing

    L'ordre de ces composants est essentiel car le fichier PDF doit être retiré en premier.  Il est également important que l'Archive Path et le Work Path du PDF Business Service soient définis sur le même chemin de répertoire.  Cela permet d'enregistrer localement une copie du PDF qui sera utilisée ultérieurement pour intégrer les données dans le message HL7. 

    Pour que le Business Service enregistre le fichier PDF dans ce répertoire Archive/Work Path, il faut appeler SendRequestAsyn().  Cette méthode est appelée par défaut lors d'un envoi entre les composants d'Ensemble/HealthShare.

    Pour ce faire, le PDF Business Service est configuré pour envoyer le message PDF à une Business Operation personnalisée qui accepte le message sans effectuer d'autres actions.

    Remarque : Le composant cible du PDF Business Service n'est pas pertinent pour cet exemple, mais il pourrait être construit pour effectuer toute action supplémentaire avec le message PDF.


    HL7 Processing

    Le message HL7 du service commercial est envoyé à un processus commercial personnalisé.  Ce processus appelle un BPL qui identifie et ouvre le fichier PDF approprié, puis appelle un DTL qui se charge de l'intégration des données dans le message HL7.

    === BPL ===

    Le BPL accomplit les tâches suivantes :

    1. Extraction du nom du fichier PDF à partir du message HL7.
    2. Demande au tableau Ens.StreamContainer, en recherchant les entrées qui correspondent au nom de fichier extrait.
    3. Appel d'un DTL pour effectuer l'integration
      1. Voir la section "DTL" de cet article
    </ol>
    
    1. Le CPL envoie ensuite le message en aval à une transaction métier intégrée dans EnsLib.HL7.Operaiton.FileOperation.

     

    === DTL ===

    1. Utilisation de target.{OBX(*)} pour compter le nombre de segments OBX
    2. Incrémentation du nombre d'OBX par unité
    3. Ajout des données PDF à l'aide de StoreFieldStreamBase64() dans un nouveau segment OBX()
      1. Pour ce code, voir "DTL Code Action"
    </ol>
    

     

    === DTL Code Action ===

    //Récupérer l'objet du flux PDF précédemment enregistré

       Set pdfStreamContainer = ##Class(Ens.StreamContainer).%OpenId(context.StreamContainerID)

       Try {

         Set pdfStreamObj = pdfStreamContainer.StreamGet()

       }

       Catch {

          $$$TRACE("Error opening stream object ID = "_context.StreamContainerID)

          Quit

       }

     //Set PDF stream object into new OBX:5 segment

       Set status = target.StoreFieldStreamBase64(pdfStreamObj,"OBX("_count_"):ObservationValue(1)")

       Set ^testglobal("status",$H) = status


    Sample Production

    Voici la liste des fichiers inclus dans le modèle ci-joint.

    1. Le message HL7 initial contenant uniquement le nom du fichier PDF intégré dans OBX(1)
    2. Le fichier PDF modèle
    3. Une exportation de classe qui comprend les éléments suivants :
      1. Classe de production
      2. Transaction modèle PDF personnalisée
      3. Règle de routage
      4. CPL
      5. DTL
    </ol>
    

     

    Cet exemple a été construit sur Ensemble 2016.2.0 mais il a été testé sur Ensemble 2015.1.2 sans aucun problème de compatibilité.  Le fichier .zip se trouve ici :

    https://community.intersystems.com/sites/default/files/post-associated-docs/sample_hl7_embedpdf_production_export.zip

    0
    0 402
    Article Irène Mykhailova · Déc 19, 2022 1m read

    EnsLib.HL7.Message.cls fournit de nombreuses méthodes API pour manipuler un message HL7.  RemoveSegmentAt(), par exemple, peut être utilisé pour supprimer un segment par chemin ou par index, mais pas plus d'un segment à la fois. Il peut arriver que vous ayez besoin de supprimer tous les segments d'un groupe ou même de nombreux groupes de segments du message HL7.  Bien sûr, vous pouvez itérer à travers chaque segment dans chaque groupe et les supprimer un par un, mais il y a un moyen beaucoup plus facile. 

    Avec une seule commande, comme ci-dessous, vous pouvez supprimer tous les segments OBX dans un message ORU_R01 (msg) :

    Set tSC = msg.SetValueAt(,"PIDgrpgrp(1).ORCgrp(1).OBXgrp()","remove")

    Ou encore, avec la seule commande ci-dessous, vous pouvez facilement supprimer tous les segments PR1 et ROL d'un groupe spécifique dans un message ADT_A01 (msg) :

    Set tSC = msg.SetValueAt(,"PR1grp(1)","remove")

    J'espère que cet article vous sera utile. Faites-moi savoir si vous avez des questions ou des soucis. Merci et à bientôt !

    0
    0 102
    Article Lorenzo Scalese · Déc 16, 2022 4m read

    Au fil des ans, je me suis souvent retrouvé dans la nécessité de créer plusieurs messages HL7 à partir d'un seul message entrant. Il s'agit généralement d'une commande ou d'un résultat provenant d'un laboratoire. Chaque fois que j'ai abordé ce problème, j'ai essayé de repartir de zéro en pensant que la tentative précédente aurait pu être mieux faite.

    Récemment, le besoin est réapparu et j'ai pu créer une solution dont je n'avais pas honte. Mon principal souci était que je me retrouvais toujours à m'enterrer dans une BPL, ou à utiliser ObjectScript et à tenter de modifier des messages en utilisant la méthode SetValueAt pour la classe de messages HL7.

    Problème

    Lorsque le Système A traite plusieurs commandes pour un seul patient, le résultat est transmis dans un seul message avec un ORCgrp répété, contenant les segments OBR et OBX. Le Système B ne peut recevoir qu'un seul OBR par message.

    Approche

    Développez un processus ObjectScript qui divisera un message unique en plusieurs en fonction du nombre de répétitions ORCgrp, et ce, en manipulant uniquement le message HL7 avec un DTL.

    Exécution

    Le code (partie 1)

    Pour commencer, nous avons besoin d'une classe qui étend Ens.BusinessProcess, et qui attend un message HL7 sur demande :

    Class Demo.Processes.MessageSplitter Extends Ens.BusinessProcess{
    Method OnRequest(pRequest As EnsLib.HL7.Message) As %Status{
      Quit $$$OK
       }
    }
    

     

    L'étape suivante consiste à boucler le message en fonction du nombre de répétitions ORCgrp. Pour cela, 2 choses sont nécessaires :

    1. Le nombre de répétitions
    2. Une boucle de type For

    Pour obtenir le nombre de répétitions, nous pouvons récupérer le compte du message en utilisant le code suivant :

    Set ORCCount = pRequest.GetValueAt("PIDgrpgrp(1).ORCgrp(*)")
    

     

    Cela va définir la variable "ORCCount" en fonction du nombre de répétitions.

    En combinant cela avec une boucle type For et une trace pour voir la sortie, cela ressemble à ceci :

    Method OnRequest(pRequest As EnsLib.HL7.Message) As %Status{
       Set ORCCount = pRequest.GetValueAt("PIDgrpgrp(1).ORCgrp(*)")
       For i=1:1:ORCCount {
           $$$TRACE("This is loop number: "_i)
       }
       Quit $$$OK
    }
    

     

    En faisant passer un message avec deux répétitions ORCgrp par ce processus à partir d'une production, nous obtenons :

    Comme vous pouvez le voir, nous obtenons deux traces.

    A partir de là, nous devons être en mesure d'appeler une transformée, et aussi d'indiquer à cette transformée quelle itération de l'ORCgrp elle doit retourner. Pour cela, nous allons utiliser le paramètre "aux", parfois négligé, pour les classes de transformée.

     

    La transformée

    La transformée elle-même peut être très simple. Tout ce que nous voulons pour cela est le suivant :

    1. Copier l'en-tête MSH tout en rendant le MessageControlID unique à votre message splitté
    2. Définir le premier ORCgrp du message cible à l'itération sur laquelle nous sommes depuis la source.
    3. Définir la valeur Target SetIDOBR à " 1 ", car elle sera toujours la première dans le message cible.

    Pour cela, notre transformée devrait ressembler à ceci :

    Mais attendez - où aux. StringValue obtient-il des données ?

    Pour le savoir, il faut revenir au code...

    Le code (partie 2)

    Nous pouvons transmettre une classe à la transformée via le paramètre aux, et pour ce scénario, je vais juste utiliser un conteneur string. Nous allons également envoyer le résultat de la transformation à une cible afin de voir les résultats :

    Class Demo.Processes.MessageSplitter Extends Ens.BusinessProcess{
    
    Property TargetConfigName As Ens.DataType.ConfigName;
    Parameter SETTINGS = "TargetConfigName";
    
    Method OnRequest(pRequest As EnsLib.HL7.Message) As %Status{
    
       Set ORCCount = pRequest.GetValueAt("PIDgrpgrp(1).ORCgrp(*)")
        For i=1:1:ORCCount {
            Set contextString = ##class(Ens.StringContainer).%New()
           Set contextString.StringValue = i
           $$$QuitOnError(##Class(Demo.Transformations.R01Split).Transform(pRequest,.SplitR01,.contextString))
            $$$QuitOnError(..SendRequestAsync(..TargetConfigName,SplitR01,0))
        }
       Quit $$$OK
     }
    }
    

     

    Ensuite, dans la production, nous définissons une destination en utilisant la nouvelle option de paramètres que nous avons créée avec la propriété et le paramètre en tête de la classe, et nous verrons quelque chose comme ceci :

     

    Conclusion

    Comme je l'ai dit au début de cet article, j'ai toujours l'impression de développer une solution à ce type de problème, et ensuite je regarde en arrière et je pense que cela pourrait être mieux fait. Cette solution ne fait pas exception, mais elle est certainement meilleure que les itérations précédentes.

    Pour améliorer cela à court terme, je vais ajouter des commentaires descriptifs à l'ObjectScript. A plus long terme, j'aimerais pouvoir ajouter un paramètre pour la classe de transformation afin qu'elle puisse être contrôlée depuis l'extérieur de l'ObjectScript.

    De façon générale, je considère qu'il s'agit d'une approche que j'adopterai pour les prochains développements et que je ne partirai pas complètement de zéro.

    (PS - Je suis heureux de partager le code pour cela, mais je ne peux que joindre un pdf à cet article. Cela semble un peu léger pour être quelque chose pour Open Exchange, mais s'il y a un intérêt pour moi de le télécharger là ou ailleurs, s'il vous plaît faites le moi savoir)

    0
    0 65
    Question William Glover · Déc 9, 2022

    J'essaie d'installer les packages requis pour utiliser .Net avec HealthShare comme ci-dessous.

    Mais je ne peux pas utiliser Nuget pour le faire, et la documentation semble assez vague, doit-elle être configurée sur la même machine que l'instance InterSystems ?

    2
    0 91
    Article Irène Mykhailova · Déc 12, 2022 5m read

    Summary

    Il existe encore des systèmes dans le secteur de la santé qui utilisent PB9, Delphi7 et d'autres langages. Pour accélérer le processus de développement et permettre aux applications tierces d'invoquer built-in le service web HL7 V2 intégré fourni par Ensemble ou IRIS, nous présentons  ici plusieurs exemples d'invocations de l'interface SOAP HL7 V2 d'Ensemble en utilisant Java, PB9 et Delphi7.

    0
    0 96
    Question Julian Matthews · Déc 8, 2022

    Je suis confronté à un problème où je dois récupérer un numéro d'identification dans un tableau SQL externe lorsqu'il n'existe pas dans un message HL7, puis l'ajouter au message HL7.

    J'ai construit la logique dans un BPL qui vérifie l'absence d'ID, puis extrait l'ID du tableau SQL (et sauvegarde sa valeur en utilisant la fonctionnalité de contexte dans le BPL), mais je ne sais pas comment prendre l'ID et l'ajouter au message HL7 avant de le transmettre à un routeur.

    1
    0 77
    Article Kevin Koloska · Déc 8, 2022 1m read

    Lors de la création d’un PRA (Privileged Routine Application; qui d’ailleurs n’est pas pertinent uniquement pour les routines mais aussi pour les classes/méthodes), il est important de s’assurer d’inclure un new $ROLES, avant d’appeler AddRoles(). Par exemple:

     New $ROLES

    set status=$System. Security.AddRoles(« MyPrivilegedRoutineApplication »)

    De cette façon, vous vous assurez que les rôles ajoutés (élevés) « s’évaporent » pour l’utilisateur exécutant ce code, une fois que l’utilisateur est hors du champ d’application de cette routine / méthode.

    [Merci @Andreas Dieckow d’avoir validé cela]

    0
    0 57
    Question Scott Roth · Nov 24, 2022

    Nous avons un cas où un fournisseur peut nous envoyer plus d'informations dans le message DICOM plutôt que dans le message de résultat HL7. Théoriquement, il devrait être possible de prendre un DICOM et de le convertir en HL7, mais la question stupide est la suivante : quelqu'un l'a-t-il déjà fait ? Quelqu'un a-t-il un bon exemple de DTL qu'il a fait pour me montrer comment configurer les structures de message et la conversion ?
    Merci
    Scott Roth
    Centre médical Wexner de l'Université d'État de l'Ohio

    1
    0 115
    Article Kevin Koloska · Nov 8, 2022 3m read

    Ensemble : Introduction à la taille du pool pour les processus métier

    #Ensemble

    Chaque composant métier d’Ensemble récupère ses tâches allouées à partir d’un pool, et le paramètre Taille du pool de chaque composant détermine le nombre de tâches sur lesquelles chaque composant peut travailler à chaque fois. Dans cet article, nous aborderons spécifiquement les différents paramètres de taille de pool pour les processus métier.

    0
    0 73
    Article Lorenzo Scalese · Oct 10, 2022 3m read

    L'interopérabilité des soins de santé permet d'améliorer les soins aux patients, de réduire les coûts des prestataires de soins et de fournir une image plus précise aux prestataires. Cependant, avec un si grand nombre de systèmes différents, les données sont formatées de nombreuses manières différentes. De nombreuses normes ont été créées pour tenter de résoudre ce problème, notamment HL7v2, HL7v3 et CDA, mais toutes présentent des inconvénients.

    FHIR (Fast Healthcare Interoperability Resources), ou Ressources rapides d'interopérabilité des soins de santé, est un nouveau format pour les échanges des informations médicales qui vise à résoudre ces problèmes. Il est développé par Health Level Seven International (HL7), une organisation qui a également développé HL7v2, HL7v3 et CDA.

    Aujourd'hui nous allons explorer comment créer et valider une ressource FHIR en utilisant le schéma FHIR à l'aide d'IntelliSense et de la fonctionnalité de complétion automatique dans VS Code.

    Etape 1 : Téléchargement du fichier de schéma JSON pour la validation des ressources sur le site officiel de FHIR https://www.hl7.org/fhir/.

    Étape 2: Création d'un dossier (dans cet exemple, j'utilise le dossier Patient et la ressource Patient) et copiage du fichier fhir.schema.json extrait dans le même dossier, puis ouverture du dossier à partir du code VS. 

     

    Étape 3: Configurez le code VS pour reconnaître le schéma FHIR en modifiant le fichier setting.json.
    Appuyez sur CTRL+SHIFT+P et tapez les paramètres de l'espace de travail JSON
     

    Étape 4: Création d'un nouveau fichier patient.fhir.json dans le même dossier.
    Appuyez sur Ctrl+Espace et vous obtiendrez tous les attributs des ressources FHIR à travers IntelliSense

    #

    Ajoutez le type de ressource Patient et tous les attributs liés à la ressource Patient vont apparaître dans l'IntelliSense.

    VS Code validera automatiquement la structure et la syntaxe de la ressource.


     

    Avec l'aide d'IntelliSense et de la fonction de complétion automatique, nous avons créé et validé notre ressource patient.

    Step 5: Affichez la ressource créée dans le serveur FHIR d'InterSystems en utilisant l'API Rest à partir de postman

    Récupérer la ressource patient créée en utilisant la méthode "Get"

    Félicitations, nous avons créé, validé notre ressource patient et réussi à l'envoyer et la récupérer sur le serveur FHIR d'InterSystems en utilisant postman.
    De cette façon, nous pouvons facilement créer et valider n'importe quelle ressource FHIR.

    0
    0 456
    Annonce Irène Mykhailova · Juin 15, 2022

    Nous avons hâte de voir tout le monde au InterSystems Global Summit la semaine prochaine !
    George James Software sera présent pour proposer des démonstrations de notre contrôle de source Deltanji. Il a été prouvé qu'il améliore l'efficacité des développeurs individuels, des grandes organisations et de tous les autres en apportant de la clarté au développement de votre système. Il encourage la gestion de la configuration, la gestion des versions et le contrôle des processus pour améliorer la qualité de votre code.
    Si vous souhaitez en savoir plus, rendez-vous dans le pavillon des partenaires ou réservez pour notre session de groupe d'utilisateurs pendant le déjeuner du mercredi 22 juin. Envoyez un e-mail laurelj@georgejames.com pour nous informer de votre présence.

     

    0
    0 78
    Article Irène Mykhailova · Juin 9, 2022 1m read

    Les champs peuvent être obtenu à l'aide du schéma INFORMATION_SCHEMA.

    INFORMATION_SCHEMA est un schéma système et n'apparaît pas dans le menu SQL du Management Portal par défaut.

    La méthode d'affichage est la suivante.

    1. Ouvrez le Management Portal → System Explorer → SQL
    2. Cochez "System" sur le côté gauche du menu déroulant du schéma.
    3. Sélectionnez INFORMATION_SCHEMA dans le menu déroulant du schéma.

    Le SQL pour obtenir l'ID, le nom du champ (COLUMN_NAME), le type de données (DATA_TYPE) et la description (DESCRIPTION) pour la table spécifiée (Test.Person) est le suivant.

    0
    0 1517
    Annonce Irène Mykhailova · Juin 8, 2022

    Venez nous dire bonjour dans le pavillon des partenaires du Global Summit !Nous présenterons notre débogueur sur place Serenji qui a subi de grands changements au cours de la dernière année. Les utilisateurs peuvent désormais profiter d'une expérience de débogage transparente sans aucune configuration, tout en profitant des dernières fonctionnalités de VS Code lui-même, permettant aux utilisateurs d'identifier en douceur et de corriger rapidement les erreurs dans votre code, contribuant ainsi à la production d'un code de qualité et maintenable.Si vous voulez voir Serenji en action, rendez-vous

    0
    0 84
    Article Irène Mykhailova · Juin 2, 2022 1m read

    Étant donné que SELECT ... FOR UPDATE est implémenté dans de nombreux RDBMS en tant que méthode d'acquisition de lock de ligne explicite, vous utilisez probablement cette fonctionnalité dans de nombreux cas.

    Cette syntaxe n'entraîne pas d'erreur dans les produits InterSystems, mais elle n'acquiert pas les locks de ligne attendus.

    Cet article vous montrera comment obtenir la même fonctionnalité.

    DECLARE CURSOR C1 IS
    SELECT Name FROM Person WHERE Name LIKE 'A%' FOR UPDATE
    OPEN C1
    LOOP FETCH C1 INTO name 
    ...afficher le nom...
    ...sortir de loop lorsque vous avez terminé...
    END LOOP
    CLOSE C1

     

    L'instruction SQL ci-dessus peut être remplacée par l'instruction SQL suivante.

     &SQL(START TRANSACTION ISOLATION LEVEL READ COMMITTED)
     &SQL(UPDATE Person SET ID=ID Where Name like 'A%')
     &SQL(DECLARE C1 CURSOR FOR SELECT ID,Name into :id,:name FROM Person Where Name like 'A%')
     &SQL(OPEN C1)
     &SQL(FETCH C1)
     While (SQLCODE = 0) {
       Write id, ":  ", name,!  &SQL(FETCH C1)
     }
     &SQL(CLOSE C1)&SQL(COMMIT) 

     

    Remarque : &SQL() est appelé Embedded SQL et est une méthode de description qui peut être utilisée lorsque vous souhaitez incorporer des instructions SQL dans la logique côté serveur. Veuillez vous référer au document pour plus de détails.

    0
    0 211
    Article Irène Mykhailova · Mai 31, 2022 1m read

    La cause de cette erreur est que la ressource locked est déjà locked par un autre processus dans l'application et que le lock n'est pas libéré pour une raison quelconque.

    S'il n'y a aucun signe que d'autres processus avec le lock, il est possible que la table de locks manque d'espace libre. Dans ce cas, le message LOCK TABLE FULL est envoyé au Message Log

    Si vous effectuez un traitement transactionnel, il est possible que le report du lock ait un effet.
    Veuillez vous référer aux documents suivants pour la transaction et le report de lock.

    Using LOCK in Transactions【IRIS】

    Using LOCK in Transactions

    De plus, s'il existe un grand nombre d'enregistrements mis à jour par des instructions SQL dans la même table au cours d'une transaction, le seuil de lock (la valeur par défaut est 1000) est atteint et une escalade de lock se produit, entraînant un état de lock de table.

    Comme vous pouvez le voir, il existe plusieurs causes possibles pour l'erreur de délai d'attente de lock. Tout d'abord, vérifiez l'état actuel du lock dans le menu de locks de Management Portal.

    【Version 2011.1 ou ultérieure】
    Management Portal : [System Operations]> [Lock]

    【Version 2010.2 ou antérieure】
    Management Portal :[Operations]> [Lock]

    0
    0 154
    Article Irène Mykhailova · Mai 27, 2022 3m read

    Il est possible de construire (reconstruire) l'index pendant que des données sont enregistrées/supprimées, mais si vous construisez l'index pendant ce processus, il sera référencé pendant sa mise à jour, utilisez donc l'utilitaire dédié et procédez à la construction de l'index.

    La procédure est la suivante.

    1. Masquez le nom d'index que vous prévoyez d'ajouter l'optimiseur de requête.
    2. Ajoutez la définition de l'index et effectuez la construction de l'index.
    3. Une fois la construction de l'index est terminée, publiez l'index ajouté dans l'optimiseur.

    L'exemple d'exécution est le suivant.

    * Dans l'exemple, l'index standard HomeStateIdx est défini pour la colonne Home_State (informations d'état de l'adresse de contact) de Sample.Person.

    1. Masquez le nom d'index que vous prévoyez d'ajouter l'optimiseur de requête.
    SAMPLES>write $system.SQL.SetMapSelectability("Sample.Person","HomeStateIdx",0)
    1


    2.Après avoir ajouté la définition d'index, reconstruisez-la.
      Exemple de définition: Index HomeStateIdx On Home.State;

    SAMPLES>do ##class(Sample.Person).%BuildIndices($LB("HomeStateIdx"))


    3. Une fois la construction de l'index est terminée, publiez l'index ajouté dans l'optimiseur.

    SAMPLES>write $system.SQL.SetMapSelectability("Sample.Person","HomeStateIdx",1)
    1

    Reportez-vous au plan de requête pour voir si l'index a été utilisé/non utilisé.
    Dans l'exemple suivant, le résultat de la confirmation du plan avec le terminal basculé sur l'environnement d'exécution SQL avec $system.SQL.Shell() s'affiche (lors du référencement dans le Management Portal, après avoir exécuté SQL sur l'écran d'exécution de la requête, cliquez sur le bouton "Affichage du plan").

    SAMPLES>do $system.SQL.Shell()
    SQL Command Line Shell
    ----------------------------------------------------
    The command prefix is currently set to: <>.
    Enter q to quit, ? for help.
    SAMPLES>>select ID,Name from Sample.Person where Home_State='NY'
    1.      select ID,Name from Sample.Person where Home_State='NY'
    ID      Name
    61      Alton,Debby O.
    138     Isaksen,Charlotte L.
    175     Walker,Emily O.
    3 Rows(s) Affected
    statement prepare time(s)/globals/lines/disk: 0.0026s/35/974/0ms
              execute time(s)/globals/lines/disk: 0.0017s/216/2447/0ms
                              cached query class: %sqlcq.SAMPLES.cls1
    ---------------------------------------------------------------------------
    SAMPLES>>show plan    // ★ Affichage du plan lorsque l'index n'est pas utilisé DECLARE QRS CURSOR FOR SELECT ID , Name FROM Sample . Person WHERE Home_State = ?
    Read master map Sample.Person.IDKEY, looping on ID.
    For each row:
        Output the row.
    SAMPLES>>show plan    // ★ Affichage du plan lors de l'utilisation de l'index DECLARE QRS CURSOR FOR SELECT ID , Name FROM Sample . Person WHERE Home_State = ?
    Read index map Sample.Person.HomeStateIdx, using the given %SQLUPPER(Home_State), and looping on ID.
    For each row:
        Read master map Sample.Person.IDKEY, using the given idkey value.
        Output the row.
    SAMPLES>>

    Pour plus de détails, veuillez consulter les documents suivants.
    Building Indices on a READ and WRITE Active System【IRIS】

    Building Indices on a READ and WRITE Active System

    0
    0 81
    Article Irène Mykhailova · Avr 22, 2022 2m read

    Comment rechercher une globale contenant un string spécifique ?

    Vous pouvez afficher les globales dans le Management Portal et effectuer une recherche avec Ctrl + F, mais les grandes globales peuvent prendre du temps et être difficiles à afficher.

    Bien sûr, vous pouvez effectuer une boucle en utilisant les fonctions $ORDER et $QUERY pour trouver la chaîne.

    Mais il existe un moyen plus simple et plus pratique.

    Il s'agit d'une méthode qui utilise la fonction de recherche globale de chaîne de caractères qui peut être effectuée dans le Management Portal.

    Cela peut être facilement fait dans [System Explorer]> [Globals] : Rechercher dans le Management Portal.

    Par exemple, pour rechercher dans le global ^%ISCLOG une variable contenant l'erreur "Erreur CSP de nettoyage après la page" :

     ↓


    Si vous voulez vraiment le faire par programmation en utilisant les fonctions $ORDER et $QUERY :

    * S'il y a plusieurs indices, utilisez la fonction $QUERY pour boucler dans plusieurs couches.

    set glb="^%ISCLOG"
    set glb=$query(@glb@(""))
    for {
      if glb="" quit
      
      if @glb [ "CSP error cleaning up after page" {
         write glb,"=",@glb,! ;; <= you can take values with @glb
      }
      set glb=$query(@glb)
    }


    Le résultat de l'exécution est le suivant (la routine ci-dessus est enregistrée et exécutée par test.mac).

    %SYS>Do ^test
    ^%ISCLOG("Data",5960)=
                            CSPServerUError cleaning up after page, HALTing ...
    ^%ISCLOG("Data",5960,0,"$ZE")=CSP error cleaning up after page, HALTing
    ^%ISCLOG("Data",10046)=
                             CSPServerUError cleaning up after page, HALTing...
    ^%ISCLOG("Data",10046,0,"$ZE")=CSP error cleaning up after page, HALTing
    ^%ISCLOG("Data",13398)=
                             CSPServerUError cleaning up after page, HALTing ...
    ^%ISCLOG("Data",13398,0,"$ZE")=CSP error cleaning up after page, HALTing
     
    %SYS>
    0
    0 102
    Article Sylvain Guilbaud · Avr 20, 2022 4m read

    Lors d'une montée de version majeure il est conseillé de recompiler les classes et les routines de tous vos espaces de noms (cf. Major Version Post-Installation Tasks).

    do $system.OBJ.CompileAllNamespaces("u")
    do ##Class(%Routine).CompileAllNamespaces()

    Pour automatiser cette tâche d'administration et conserver un journal des erreurs éventuelles, vous trouverez ci-dessous un exemple d'une classe à importer et compiler dans l'espace de noms USER que vous pourrez utiliser après chaque montée de version : admin.utils.cls

    0
    1 175
    Article Lorenzo Scalese · Mars 30, 2022 3m read

    Comme vous le savez, dans Caché / IRIS, vous avez la possibilité de définir une propriété comme Multidimensionnelle, comme documenté ici et l'explication de la façon de l'utiliser est ici.

    Bien que l'accès soit assez confortable (au sens traditionnel du COS), il y a 2 restrictions principales qui font mal :

    1. Il n'est pas sauvegardé sur le disque, sauf si votre application inclut du code pour le sauvegarder spécifiquement.
    2. Il ne peut pas être stocké dans des tableaux SQL ou exposé à travers ceux-ci.

    il y en a d'autres Je vais vous montrer comment surmonter ces limites.

    1. Prenons cette classe simple comme exemple :
    Class DC.Multi Extends (%Persistent, %Populate) [ Final ]
    {
        Property Name As %String;
        Property DOB As %Date;
        Property Multi As %String [ MultiDimensional 
    ];

    La carte de stockage montre déjà le problème n°1 : pas de place pour "Multi"

    Storage Default
    {
      <Data name="MultiDefaultData">
        <Value name="1">
          <Value>Name</Value>
        </Value>
        <Value name="2">
          <Value>DOB</Value>
        </Value>
      </Data>
      <DataLocation>^DC.MultiD</DataLocation>
      <DefaultData>MultiDefaultData</DefaultData>
      <IdLocation>^DC.MultiD</IdLocation>
      <IndexLocation>^DC.MultiI</IndexLocation>
      <StreamLocation>^DC.MultiS</StreamLocation>
      <Type>%Storage.Persistent</Type>
    }

    donc nous ajoutons 2 méthodes :

    /// save Multidimensional property
    Method %OnAfterSave(insert As %Boolean) As %Status [ Private, ServerOnly = 1 ]
    {    Merge ^(..%Id(),"Multi")=i%Multi quit $$$OK   }
    /// load Multidimensional property
    Method %OnOpen() As %Status [ Private, ServerOnly = 1 ]
    {   Merge i%Multi=^(..%Id(),"Multi") quit $$$OK  }

    nous attachons juste la structure orpheline à notre objet réel. Pour être franc, ce n'est pas mon invention, mais l'approche (simplifiée) qui était utilisée dans la classe %CSP.Session lorsqu'elle a été écrite vers le début du millénaire. Avec le simple ajout suivant, votre structure multidimensionnelle devient persistante.

    L'objet en mémoire ressemble à ceci :

    CACHE>zw o2
    o2=3@DC.Multi  ; <OREF>
    +----------------- general information ---------------
    |      oref value: 3
    |      class name: DC.Multi
    |           %%OID: $lb("2","DC.Multi")
    | reference count: 2
    +----------------- attribute values ------------------
    |       %Concurrency = 1  <Set>
    |                DOB = 62459
    |         Multi("a") = 1
    |     Multi("rob",1) = "rcc"
    |     Multi("rob",2) = 2222
    |               Name = "Klingman,Uma C."
    +-----------------------------------------------------

    et c'est le stockage associé, une jolie globale multidimensionnelle :

    CACHE>zw ^DC.MultiD(2)
    ^DC.MultiD(2)=$lb("Klingman,Uma C.",62459)
    ^DC.MultiD(2,"Multi","a")=1
    ^DC.MultiD(2,"Multi","rob",1)="rcc"
    ^DC.MultiD(2,"Multi","rob",2)=2222

    Jusqu'à présent, tout va bien.

    1. SELECT * from DC.Multi n'a aucune idée de ce que peut être une colonne "Multi".

    donc nous ajoutons une propriété calculée SQL et un style approprié.

    Property SqlMulti As %String(MAXLEN = "") [ Calculated, SqlComputed,
    SqlComputeCode 
    = { set {*}= ##class(DC.Multi).ShowMulti({ID}) }];
    ClassMethod ShowMulti(id) As %String(MAXLEN="")
    { set res="{"
          ,obj=..%OpenId(id)
       if $isobject(obj) {
          set qry=$query(obj.Multi(""),1,value)
          while qry'="" {
             set res=res_$piece(qry,".",2,99)_"="_value_","
                ,qry=$query(@qry,1,value)
             }
         set $extract(res,*)=""
       }
       quit res_"}"   
    }

    Et cela ressemble à ceci

    Inutile de dire que la conception est totalement entre vos mains.

    Comme il y a un progrès évident par rapport au passé, le prochain article vous montrera une solution plus appropriée au nouveau siècle.

    0
    0 94
    Article Guillaume Rongier · Mars 16, 2022 21m read

    Cette formation s'adresse aux débutants qui souhaitent découvrir le framework IRIS Interoperability. Nous utiliserons Docker et VSCode.

    GitHub : https://github.com/grongierisc/formation-template

    1. Formation Ensemble / Interoperability

    Le but de cette formation est d'apprendre le cadre d'interopérabilité d'InterSystems, et en particulier l'utilisation de :

    • Productions
    • Messages
    • Opérations commerciales
    • Adaptateurs
    • Processus métier
    • Services commerciaux
    • Services et opérations REST

    TABLE DES MATIÈRES :

    2. Cadre de travail

    Voici le Framework IRIS.

    FrameworkFull

    Les composants à l'intérieur d'IRIS représentent une production. Les adaptateurs entrants et les adaptateurs sortants nous permettent d'utiliser différents types de formats comme entrée et sortie pour notre base de données. Les applications composites nous donneront accès à la production via des applications externes comme les services REST.

    Les flèches entre tous ces composants sont des messages. Ils peuvent être des demandes ou des réponses.

    3. Adapter le framework

    Dans notre cas, nous allons lire des lignes dans un fichier csv et les enregistrer dans la base de données IRIS.

    Nous ajouterons ensuite une opération qui nous permettra de sauvegarder également des objets dans une base de données externe, en utilisant JDBC. Cette base de données sera située dans un conteneur docker, en utilisant postgre.

    Enfin, nous verrons comment utiliser des applications composites pour insérer de nouveaux objets dans notre base de données ou pour consulter cette base (dans notre cas, via un service REST).

    Le framework adapté à notre objectif nous donne :

    FrameworkAdapted

    4. Prérequis

    Pour cette formation, vous aurez besoin de :

    5. Mise en place

    5.1. Conteneurs Docker

    Afin d'avoir accès aux images InterSystems, nous devons nous rendre à l'url suivante : http://container.intersystems.com. Après nous être connectés avec nos identifiants InterSystems, nous obtiendrons notre mot de passe pour nous connecter au registre. Dans l'addon docker VScode, dans l'onglet image, en appuyant sur connect registry et en entrant la même url que précédemment (http://container.intersystems.com) comme registre générique, il nous sera demandé de donner nos identifiants. L'identifiant est l'identifiant habituel mais le mot de passe est celui que nous avons obtenu sur le site Web.

    De là, nous devrions pouvoir construire et composer nos conteneurs (avec les fichiers docker-compose.yml et Dockerfile donnés).

    5.2. Portail de gestion

    Nous allons ouvrir un portail de gestion. Il nous donnera accès à une page web où nous pourrons créer notre production. Le portail devrait être situé à l'url : http://localhost:52775/csp/sys/UtilHome.csp?$NAMESPACE=IRISAPP. Vous aurez besoin des informations d'identification suivantes :

    IDENTIFIANT : SuperUser

    MOT DE PASSE : SYS

    5.3. Sauvegarde de la progression

    Une partie des choses que nous allons faire sera sauvegardée localement, mais tous les processus et productions sont sauvegardés dans le conteneur docker. Afin de persister tout notre progrès, nous devons exporter chaque classe qui est créée par le portail de gestion avec l'addon InterSystems ObjectScript :

    ExportProgress

    Nous devrons sauvegarder notre production, le plan d'enregistrement, les processus métier et le transfert de données de cette manière. Après cela, lorsque nous fermerons notre conteneur docker et le recomposerons, nous aurons toujours toute notre progression sauvegardée localement (c'est, bien sûr, à faire après chaque changement via le portail). Pour le rendre à nouveau accessible à IRIS, nous devons compiler les fichiers exportés (en les sauvegardant, les addons InterSystems s'occupent du reste).

    6. Productions

    Nous pouvons maintenant créer notre première production. Pour cela, nous allons passer par les menus [Interoperability] et [Configurer] :

    MenuProduction

    Nous devons ensuite appuyer sur [Nouveau], sélectionner le paquet [Formation] et choisir un nom pour notre production :

    ProductionCreation

    Immédiatement après avoir créé notre production, nous devons cliquer sur [Paramètres de production] juste au-dessus de la section [Opérations]. Dans le menu latéral de droite, nous devrons activer [Test activé] dans la partie [Développement et débogage] de l'onglet [Paramètres] (n'oubliez pas de cliquer sur [Appliquer]).

    ProductionTesting

    Dans cette première production, nous allons maintenant ajouter des opérations commerciales.

    7. Opérations

    Une Business Operation (BO) est une opération spécifique qui nous permettra d'envoyer des requêtes depuis IRIS vers une application / un système externe. Elle peut également être utilisée pour enregistrer directement dans IRIS ce que nous voulons.

    Nous allons créer ces opérations en local, c'est-à-dire dans le fichier Formation/BO/. En sauvegardant les fichiers, nous les compilerons dans IRIS.

    Pour notre première opération, nous allons sauvegarder le contenu d'un message dans la base de données locale.

    Nous devons d'abord avoir un moyen de stocker ce message.

    7.1. Création de notre classe de stockage

    Les classes de stockage dans IRIS étendent le type %Persistent. Elles seront enregistrées dans la base de données interne.

    Dans notre fichier Formation/Table/Formation.cls nous avons :

    Class Formation.Table.Formation Extends %Persistent
    {
    
    Property Name As %String;
    
    Property Salle As %String;
    
    }
    

    Notez que lors de l'enregistrement, des lignes supplémentaires sont automatiquement ajoutées au fichier. Elles sont obligatoires et sont ajoutées par les addons InterSystems.

    7.2. Création de notre classe de messages

    Ce message contient un objet Formation, situé dans le fichier Formation/Obj/Formation.cls :

    Class Formation.Obj.Formation Extends (%SerialObject, %XML.Adaptor)
    {
    
    Property Nom As %String;
    
    Property Salle As %String;
    
    }
    

    La classe Message utilisera cet objet Formation, src/Formation/Msg/FormationInsertRequest.cls :

    Class Formation.Msg.FormationInsertRequest Extends Ens.Request
    {
    
    Property Formation As Formation.Obj.Formation;
    
    }
    

    7.3. Création de notre opération

    Maintenant que nous avons tous les éléments dont nous avons besoin, nous pouvons créer notre opération, dans le fichier Formation/BO/LocalBDD.cls :

    Class Formation.BO.LocalBDD Extends Ens.BusinessOperation
    {
    
    Parameter INVOCATION = "Queue";
    
    Method InsertLocalBDD(pRequest As Formation.Msg.FormationInsertRequest, Output pResponse As Ens.StringResponse) As %Status
    {
        set tStatus = $$$OK
    
        try{
            set pResponse = ##class(Ens.Response).%New()
            set tFormation = ##class(Formation.Table.Formation).%New()
            set tFormation.Name = pRequest.Formation.Nom
            set tFormation.Salle = pRequest.Formation.Salle
            $$$ThrowOnError(tFormation.%Save())
        }
        catch exp
        {
            Set tStatus = exp.AsStatus()
        }
    
        Quit tStatus
    }
    
    XData MessageMap
    {
    <MapItems>
        <MapItem MessageType="Formation.Msg.FormationInsertRequest"> 
            <Method>InsertLocalBDD</Method>
        </MapItem>
    </MapItems>
    }
    
    }
    
    

    La MessageMap nous donne la méthode à lancer en fonction du type de demande (le message envoyé à l'opération).

    Comme nous pouvons le voir, si l'opération a reçu un message du type Formation.Msg.FormationInsertRequest, la méthode InsertLocalBDD sera appelée. Cette méthode enregistrera le message dans la base de données locale d'IRIS.

    7.4. Ajout de l'opération à la production

    Nous devons maintenant ajouter cette opération à la production. Pour cela, nous utilisons le portail de gestion. En appuyant sur le signe [+] à côté de [Opérations], nous avons accès à l'[Assistant d'opérations commerciales]. Là, nous choisissons la classe d'opération que nous venons de créer dans le menu déroulant.

    OperationCreation

    9.3. Test

    Un double clic sur l'opération nous permettra de l'activer. Après cela, en sélectionnant l'opération et en allant dans les onglets [Actions] dans le menu latéral droit, nous devrions être en mesure de tester l'opération (si ce n'est pas le cas, consultez la partie création de la production pour activer les tests / vous devrez peut-être démarrer la production si elle est arrêtée).

    En faisant cela, nous allons envoyer à l'opération un message du type que nous avons déclaré plus tôt. Si tout se passe bien, les résultats devraient être comme indiqué ci-dessous :

    OperationTest

    L'affichage de la trace visuelle nous permettra de voir ce qui s'est passé entre les processus, les services et les opérations. Ici, nous pouvons voir le message envoyé à l'opération par le processus, et l'opération renvoyant une réponse (qui est juste une chaîne vide).

    8. Processus métier

    Les processus métier (BP) sont la logique métier de notre production. Ils sont utilisés pour traiter les demandes ou relayer ces demandes à d'autres composants de la production.

    Les processus métier sont créés dans le portail de gestion :

    BPMenu

    8.1. BP simple

    8.1.1. Création du processus

    Nous sommes maintenant dans le concepteur de processus métier. Nous allons créer un BP simple qui appellera notre opération :

    BPAddingCall

    8.1.2. Modifier le contexte d'un BP

    Un BP possède un Contexte. Il est composé d'une classe de requête, la classe de l'entrée, et d'une classe de réponse, la classe de la sortie. Les processus métier n'ont qu'une entrée et une sortie. Il est également possible d'ajouter des propriétés.

    Puisque notre BP ne sera utilisé que pour appeler notre BO, nous pouvons mettre comme classe de requête la classe de message que nous avons créée (nous n'avons pas besoin d'une sortie puisque nous voulons juste insérer dans la base de données).

    BPContext

    Nous avons ensuite choisi la cible de la fonction d'appel : notre BO. Cette opération, étant appelée a une propriété callrequest. Nous devons lier cette callrequest à la requête de la BP (elles sont toutes deux de la classe Formation.Msg.FormationInsertRequest), nous faisons cela en cliquant sur la fonction d'appel et en utilisant le constructeur de requête :

    BPBindRequests

    Nous pouvons maintenant sauvegarder cette BP (dans le package « Formation.BP » et sous le nom « InsertLocalBDD » ou « Main », par exemple). Tout comme les opérations, les processus peuvent être instanciés et testés via la configuration de production, pour cela ils doivent être compilés au préalable (sur l'écran Concepteur de processus métier).

    Pour l'instant, notre processus ne fait que transmettre le message à notre opération. Nous allons le complexifier pour que le BP prenne en entrée une ligne d'un fichier CSV.

    8.2. Lecture de lignes CSV par un BP

    8.2.1. Création d'une carte d'enregistrement

    Afin de lire un fichier et de mettre son contenu dans un fichier, nous avons besoin d'une Carte d'enregistrement (RM). Il existe un Record Mapper spécialisé pour les fichiers CSV dans le menu [Interoperability > Build] du portail de gestion :

    RMMenu

    Nous créerons le mapper comme suit :

    RMCreation

    Nous allons créer le mappeur comme ceci :

    RMDetails

    Maintenant que la carte est créée, nous devons la générer (avec le bouton Générer). Nous devons maintenant avoir une Transformation de données à partir du format de la carte des enregistrements et un message d'insertion.

    8.2.2. Création d'une transformation de données

    Nous trouverons le créateur de transformations de données (DT) dans le menu [Interoperability > Builder]. Nous allons créer notre DT comme ceci (si vous ne trouvez pas Formation.RM.Csv.Record, peut-être que vous n'avez pas généré la carte d'enregistrement) :

    DTCreation

    Nous pouvons pouvons maintenant mapper les différents champs ensemble :

    DTMap

    8.2.3. Ajout de la transformation des données au processus métier

    La première chose que nous devons changer est la classe de requête de la BP, puisque nous devons avoir en entrée la carte d'enregistrement que nous avons créée.

    BP2ChangeContext

    Nous pouvons alors ajouter notre transformation (le nom du processus ne change rien, d'ici nous avons choisi de le nommer Main) :

    BP2AddingTransform

    L'activité de transformation va prendre la requête de la BP (un enregistrement du fichier CSV, grâce à notre Record Mapper), et la transformer en un message FormationInsertRequest. Afin de stocker ce message pour l'envoyer à la BO, nous devons ajouter une propriété au contexte de la BP.

    BP2MsgContext

    Nous pouvons maintenant configurer notre fonction de transformation pour qu'elle prenne l'entrée du BP et enregistre sa sortie dans la propriété nouvellement créée. La source et la cible de la transformation RmToMsg sont respectivement request et context.Msg :

    BP2RmToMsg

    Nous devons faire de même pour Call BO. Son entrée, ou callrequest, est la valeur stockée dans context.msg :

    BP2CallBO

    Au final, le flux dans la BP peut être représenté comme ceci :

    BP2Diagram

    8.2.4. Configuration de la production

    Avec le signe [+], nous pouvons ajouter notre nouveau processus à la production (si ce n'est pas déjà fait). Nous avons également besoin d'un service générique pour utiliser le record map, nous utilisons EnsLib.RecordMap.Service.FileService (nous l'ajoutons avec le bouton [+] à côté des services). Nous paramétrons ensuite ce service :

    ServiceParam

    Nous devrions maintenant être en mesure de tester notre BP.

    8.2.5. Test

    Nous testons l'ensemble de la production comme suit :

    TestProductionCSV

    Dans le menu Explorateur système > SQL, vous pouvez exécuter la commande

    SELECT 
    ID, Name, Salle
    FROM Formation_Table.Formation
    

    pour voir les objets que nous venons d'enregistrer.

    9. Accéder à une base de données externe à l'aide de JDBC

    Dans cette section, nous allons créer une opération pour sauvegarder nos objets dans une base de données externe. Nous utiliserons l'API JDBC, ainsi que l'autre conteneur docker que nous avons mis en place, avec postgre dessus.

    9.1. Création de notre nouvelle opération

    Notre nouvelle opération, dans le fichier Formation/BO/RemoteBDD.cls est la suivante :

    Include EnsSQLTypes
    
    Class Formation.BO.RemoteBDD Extends Ens.BusinessOperation
    {
    
    Parameter ADAPTER = "EnsLib.SQL.OutboundAdapter";
    
    Property Adapter As EnsLib.SQL.OutboundAdapter;
    
    Parameter INVOCATION = "Queue";
    
    Method InsertRemoteBDD(pRequest As Formation.Msg.FormationInsertRequest, Output pResponse As Ens.StringResponse) As %Status
    {
        set tStatus = $$$OK
    
        try{
            set pResponse = ##class(Ens.Response).%New()
            set ^inc = $I(^inc)
            set tInsertSql = "INSERT INTO public.formation (id, nom, salle) VALUES(?, ?, ?)"
            $$$ThrowOnError(..Adapter.ExecuteUpdate(.nrows,tInsertSql,^inc,pRequest.Formation.Nom, pRequest.Formation.Salle ))
        }
        catch exp
        {
            Set tStatus = exp.AsStatus()
        }
    
        Quit tStatus
    }
    
    XData MessageMap
    {
    <MapItems>
        <MapItem MessageType="Formation.Msg.FormationInsertRequest"> 
            <Method>InsertRemoteBDD</Method>
        </MapItem>
    </MapItems>
    }
    
    }
    

    Cette opération est similaire à la première que nous avons créée. Lorsqu'elle recevra un message du type Formation.Msg.FormationInsertRequest, elle utilisera un adaptateur pour exécuter des requêtes SQL. Ces requêtes seront envoyées à notre base de données postgre.

    9.2. Configurer la production

    Maintenant, via le portail de gestion, nous allons instancier cette opération (en l'ajoutant avec le signe [+] dans la production).

    Nous devrons également ajouter le JavaGateway pour le pilote JDBC dans les services. Le nom complet de ce service est EnsLib.JavaGateway.Service.

    JDBCProduction

    Nous devons maintenant configurer notre opération. Puisque nous avons mis en place un conteneur postgre, et connecté son port 5432, les valeurs dont nous avons besoin dans les paramètres suivants sont :

    DSN : jdbc:postgresql://db:5432/DemoData

    Pilote JDBC : org.postgresql.Driver

    JDBC Classpath : /tmp/iris/postgresql-42.2.14.jar

    JDBCParam

    Enfin, nous devons configurer les informations d'identification pour avoir accès à la base de données distante. Pour cela, nous devons ouvrir la visionneuse d'identifiants :

    JDBCCredentialMenu

    L'identifiant et le mot de passe sont tous deux DemoData, comme nous l'avons configuré dans le fichier docker-compose.yml.

    JDBCCredentialCreation

    De retour à la production, nous pouvons ajouter « Postgre » dans le champ [Identifiant] dans les paramètres de notre opération (il devrait être dans le menu déroulant). Avant de pouvoir le tester, nous devons ajouter le JGService à l'opération. Dans l'onglet [Paramètres], dans les [Paramètres supplémentaires] :

    JDBCService

    7.5. Test

    Lors du test, la trace visuelle doit indiquer un succès :

    JDBCTest

    Lors du test, la trace visuelle doit montrer un succès :

    9.4. Exercice

    À titre d'exercice, il pourrait être intéressant de modifier BO.LocalBDD afin qu'il renvoie un booléen qui indiquera au BP d'appeler BO.RemoteBDD en fonction de la valeur de ce booléen.

    Indice : cela peut être fait en changeant le type de réponse que LocalBDD renvoie et en ajoutant une nouvelle propriété au contexte et en utilisant l'activité if dans notre BP.

    9.5. Solution

    Tout d'abord, nous devons avoir une réponse de notre opération LocalBDD. Nous allons créer un nouveau message, dans le fichier Formation/Msg/FormationInsertResponse.cls :

    Class Formation.Msg.FormationInsertResponse Extends Ens.Response
    {
    
    Property Double As %Boolean;
    
    }
    

    Ensuite, nous modifions la réponse de LocalBDD par cette réponse, et définissons la valeur de son booléen de manière aléatoire (ou non) :

    Method InsertLocalBDD(pRequest As Formation.Msg.FormationInsertRequest, Output pResponse As Formation.Msg.FormationInsertResponse) As %Status
    {
        set tStatus = $$$OK
    
        try{
            set pResponse = ##class(Formation.Msg.FormationInsertResponse).%New()
            if $RANDOM(10) < 5 {
                set pResponse.Double = 1
            } 
            else {
                set pResponse.Double = 0
            }
    ...
    

    Nous allons maintenant créer un nouveau processus (copié sur celui que nous avons fait), où nous ajouterons une nouvelle propriété de contexte, de type %Boolean :

    ExerciseContext

    Cette propriété sera remplie avec la valeur du callresponse.Double de notre appel d'opération (nous devons définir la [Classe de message de réponse] à notre nouvelle classe de message) :

    ExerciseBinding

    Nous ajoutons ensuite une activité if, avec la propriété context.Double comme condition :

    ExerciseIf

    TRÈS IMPORTANT : nous devons décocher Asynchrone dans les paramètres de notre appel LocallBDD, ou l'activité if se déclenchera avant de recevoir la réponse booléenne.

    Enfin, nous configurons notre activité d'appel avec comme cible le BO RemoteBDD :

    ExerciseRemoteCall

    Pour compléter l'activité if, nous devons faire glisser un autre connecteur de la sortie du if vers le triangle join ci-dessous. Comme nous ne ferons rien si le booléen est faux, nous laisserons ce connecteur vide. Après avoir compilé et instancié, nous devrions être en mesure de tester notre nouveau processus. Pour cela, nous devons changer le Target Config Name de notre File Service.

    Dans la trace, nous devrions avoir environ la moitié des objets lus dans le csv enregistrés également dans la base de données distante.

    10. Service REST

    Dans cette partie, nous allons créer et utiliser un service REST.

    10.1. Créer le service

    Pour créer un service REST, nous avons besoin d'une classe qui étend %CSP.REST, dans Formation/REST/Dispatch.cls nous avons :

    Class Formation.REST.Dispatch Extends %CSP.REST
    {
    
    /// Ignore any writes done directly by the REST method.
    Parameter IgnoreWrites = 0;
    
    /// Convertir par défaut le flux d'entrée en Unicode
    Parameter CONVERTINPUTSTREAM = 1;
    
    /// Le jeu de caractères par défaut est utf-8
    Parameter CHARSET = "utf-8";
    
    Parameter HandleCorsRequest = 1;
    
    XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
    {
    <Routes>
      <!-- Get this spec -->
      <Route Url="/import" Method="post" Call="import" />
    </Routes>
    }
    
    /// Obtenir cette spécification
    ClassMethod import() As %Status
    {
      set tSc = $$$OK
    
      Try {
    
          set tBsName = "Formation.BS.RestInput"
          set tMsg = ##class(Formation.Msg.FormationInsertRequest).%New()
    
          set body = $zcvt(%request.Content.Read(),"I","UTF8")
          set dyna = {}.%FromJSON(body)
    
          set tFormation = ##class(Formation.Obj.Formation).%New()
          set tFormation.Nom = dyna.nom
          set tFormation.Salle = dyna.salle
    
          set tMsg.Formation = tFormation
    
          $$$ThrowOnError(##class(Ens.Director).CreateBusinessService(tBsName,.tService))
    
          $$$ThrowOnError(tService.ProcessInput(tMsg,.output))
    
      } Catch ex {
          set tSc = ex.AsStatus()
      }
    
      Quit tSc
    }
    
    }
    

    Cette classe contient une route pour importer un objet, liée au verbe POST :

    <Routes>
      <!-- Obtenir cette spécification -->
      <Route Url="/import" Method="post" Call="import" />
    </Routes>
    

    La méthode d'importation va créer un message qui sera envoyé à un service métier.

    10.2. Ajouter notre BS

    Nous allons créer une classe générique qui va router toutes ses sollicitations vers TargetConfigNames. Cette cible sera configurée lorsque nous instancierons ce service. Dans le fichier Formation/BS/RestInput.cls nous avons :

    Class Formation.BS.RestInput Extends Ens.BusinessService
    {
    
    Property TargetConfigNames As %String(MAXLEN = 1000) [ InitialExpression = "BuisnessProcess" ];
    
    Parameter SETTINGS = "TargetConfigNames:Basic:selector?multiSelect=1&context={Ens.ContextSearch/ProductionItems?targets=1&productionName=@productionId}";
    
    Method OnProcessInput(pDocIn As %RegisteredObject, Output pDocOut As %RegisteredObject) As %Status
    {
        set status = $$$OK
    
        try {
    
            for iTarget=1:1:$L(..TargetConfigNames, ",") {
                set tOneTarget=$ZStrip($P(..TargetConfigNames,",",iTarget),"<>W")  Continue:""=tOneTarget
                $$$ThrowOnError(..SendRequestSync(tOneTarget,pDocIn,.pDocOut))
            }
        } catch ex {
            set status = ex.AsStatus()
        }
    
        Quit status
    }
    
    }
    

    De retour à la configuration de production, nous ajoutons le service de la manière habituelle. Dans le champ [Target Config Names], nous mettons notre BO LocalBDD :

    RESTServiceSetup

    Pour utiliser ce service, nous devons le publier. Pour cela, nous utilisons le menu [Modifier l'application Web] :

    RESTServicePublish

    10.3. Test

    Enfin, nous pouvons tester notre service avec n'importe quel type de client REST :

    RESTTest

    Conclusion

    Grâce à cette formation, nous avons créé une production capable de lire les lignes d'un fichier csv et de sauvegarder les données lues à la fois dans la base de données IRIS et dans une base de données externe en utilisant JDBC. Nous avons également ajouté un service REST afin d'utiliser le verbe POST pour sauvegarder de nouveaux objets.

    Nous avons découvert les principaux éléments du cadre d'interopérabilité d'InterSystems.

    Nous l'avons fait en utilisant docker, vscode et le portail de gestion IRIS d'InterSystems.

    0
    0 322