Recherche

Effacer le filtre
Article
Guillaume Rongier · Juil 6, 2022

Template Flask pour Embedded Python

1. intersystems-iris-docker-rest-template Il s'agit d'un modèle d'une application REST API intégrée en python dans InterSystems IRIS. Elle possède également une spécification OPEN API et peut être développée avec Docker et VSCode. 1. intersystems-iris-docker-rest-template 2. Conditions préalables 3. Installation 3.1. Installation pour le développement 3.2. Portail de gestion et et VSCode 3.3. Avoir le répertoire ouvert à l'intérieur du conteneur 4. Comment cela fonctionne 5. Comment l'utiliser 5.1. Requête POST 5.1.1. Test de la requête POST 5.1.2. Comment fonctionne la requête POST 5.2. Requête GET 5.2.1. Test de la requête GET 5.2.2. Comment fonctionne la requête GET 5.3. Requête PUT 5.3.1. Test de la requête PUT 5.3.2. Comment fonctionne la requête PUT 5.4. Requête DELETE 5.4.1. Test de la requête DELETE 5.4.2. Comment fonctionne la requête DELETE 6. Comment commencer le codage 7. Contenu du dépôt 7.1. Dockerfile 7.2. .vscode/settings.json 7.3. .vscode/launch.json 2. Conditions préalables Assurez-vous que vous avez installé git et Docker desktop. Il est à noter que le tableau Sample.Person a déjà été créé à l'avance pour la démo en utilisant le portail de gestion dans l'onglet sql : CREATE TABLE Sample.Person ( Société VARCHAR(50), DATE DE NAISSANCE DATE, Nom VARCHAR(4096), Téléphone VARCHAR(4096), Position VARCHAR(50) ) 3. Installation 3.1. Installation pour le développement Clone/git tire le repo dans n'importe quel répertoire local, par exemple comme indiqué ci-dessous : $ git clone https://github.com/grongierisc/iris-python-flask-api-template.git Ouvrez le terminal dans ce répertoire et exécutez : $ DOCKER_BUILDKIT=1 docker-compose up -d --build 3.2. Management Portal et VSCode Ce dépôt est prêt pour VS Code. Ouvrez le répertoire formation-template-python cloné localement dans VS Code. Si vous êtes invité à le faire (dans le coin inférieur droit), installez les extensions recommandées. 3.3. Avoir le répertoire ouvert à l'intérieur du conteneur Il est vraiment important de se rendre à l'intérieur du conteneur avant de coder. Pour cela, il faut activer docker avant d'ouvrir VSCode. Ensuite, dans VSCode, lorsque vous y êtes invité (dans le coin inférieur droit), rouvrez le répertoire à l'intérieur du conteneur afin de pouvoir utiliser les composants python qu'il contient. La première fois que vous faites cela, il se peut que cela prenne plusieurs minutes, le temps que le conteneur soit préparé. Plus d'informations ici En ouvrant le répertoire à distance, vous permettez à VS Code et à tous les terminaux que vous ouvrez dans ce répertoire d'utiliser les composants python dans le conteneur. Configurer ceux-ci pour utiliser /usr/irissys/bin/irispython 4. Comment cela fonctionne L' app.py, une fois lancé (à l'intérieur du conteneur) va recueillir les requêtes CRUD. En fonction du type de demande, le message approprié sera créé pour être envoyé au service FlaskService, ce service appellera l'opération CrudPerson qui, en fonction du type de message envoyé par le service, distribuera les informations nécessaires pour effectuer l'action demandée.. Pour plus de détails, vous pouvez consulter la partie How it works (Comment ça marche) de cette démo entièrement documentée this fully documented demo. 5. Comment l’utiliser Ce modèle crée une application web REST /crud sur IRIS qui met en œuvre 4 types de communication : GET, POST, PUT et DELETE, c'est-à-dire les opérations CRUD. Cette interface fonctionne avec un exemple de classe persistante Person trouvée dans src/python/person/obj.py. Tout d'abord, il est nécessaire de démarrer l'application 'app.py' située dans src/python/person/app.py en utilisant flask. Pour ce faire, allez dans le fichier app.py , ensuite dans la fenêtre run and debug de VSCode et sélectionnez Python: Flask et et ensuite exécutez. Ceci lancera l'application. 5.1. Requête POST 5.1.1. Test de la requête POST Créez une requête POST, par exemple dans Postman ou dans RESTer pour mozilla, avec des données brutes en JSON du type suivant : {"nom":"Elon Musk","position":"CEO","société":"Tesla","téléphone":"123-123-1233","DATE DE NAISSANCE":"1982-01-19"} En utilisant Content-Type en tant qu’ application/json Ajustez l'autorisation si nécessaire - elle est basique pour le conteneur avec le login et le mot de passe par défaut pour le conteneur IRIS Community edition. Envoyez la demande POST à localhost:4040/persons/ Cette opération créera un enregistrement dans le tableau Sample.Person d'IRIS et renverra l'identifiant id de la Person nouvellement ajoutée de la requête POST pour ajouter Elon Musk dans le tableau. 5.1.2. Comment fonctionne la requête POST def create_person(self,request:CreatePersonRequest): """ > Créer une nouvelle personne dans la base de données et renvoyer son identifiant :param request: L'objet de la requête qui a été transmise par le client :type request: CreatePersonRequest :return: L'identifiant de la personne nouvellement créée. """ # sqlInsert = 'insert into Sample.Person values (?,?,?,?,?)' # iris.sql.exec(sqlInsert,request.person.company,dob,request.person.name,request.person.phone,request.person.title) # IRIS ORM person = iris.cls('Sample.Person')._New() if (v:=request.person.company) is not None: person.Company = v if (v:=request.person.name) is not None: person.Name = v if (v:=request.person.phone) is not None: person.Phone = v if (v:=request.person.title) is not None: person.Title = v if (v:=request.person.dob) is not None: person.DOB = v Utils.raise_on_error(person._Save()) return CreatePersonResponse(person._Id()) En utilisant IRIS ORM , nous pouvons créer une nouvelle personne Person et le sauvegarder dans notre base de données. 5.2. Requête GET 5.2.1. Test de la requête GET Pour tester GET, vous devez avoir des données. Vous pouvez les créer avec une requête requête POST. Cette API REST expose deux requêtes GET : toutes les données et un enregistrement. Pour obtenir toutes les données en JSON, lancez : localhost:4040/persons/all Pour demander les données d'un enregistrement particulier, fournissez l'identifiant dans la requête GET comme 'localhost:5000/persons/id', voici un exemple : localhost:4040/persons/1 Cela renverra des données JSON pour la personne avec ID=1, par exemple : {"nom":"Elon Musk","position":"CEO","société":"Tesla","téléphone":"123-123-1233","DATE DE NAISSANCE":"1982-01-19"} 5.2.2. Comment fonctionne la requête GET def get_person(self,request:GetPersonRequest): """ > La fonction prend un objet `GetPersonRequest`, exécute une requête SQL, et renvoie Objet `GetPersonResponse` :param request: L'objet de la demande qui est passé dans :type request: GetPersonRequest :return: Objet GetPersonResponse """ sql_select = """ SELECT Société, date de naissance, nom, téléphone, position FROM Sample.Person where ID = ? """ rs = iris.sql.exec(sql_select,request.id) response = GetPersonResponse() for person in rs: response.person= Person(company=person[0],dob=person[1],name=person[2],phone=person[3],title=person[4]) return response def get_all_person(self,request:GetAllPersonRequest): """ > Cette fonction renvoie une liste de toutes les personnes figurant dans le tableau des personnes :param request: L'objet de la requête qui est passé au service :type request: GetAllPersonRequest :return: Une liste d'objets Person """ sql_select = """ SELECT Société, date de naissance, nom, téléphone, position FROM Sample.Person """ rs = iris.sql.exec(sql_select) response = GetAllPersonResponse() response.persons = list() pour les personnes dans rs: response.persons.append(Person(company=person[0],dob=person[1],name=person[2],phone=person[3],title=person[4])) return response Cette fois, à l'aide de la fonction iris python sql.exec , nous pouvons exécuter directement du code SQL dans la base de données IRIS, recueillir les informations nécessaires et les renvoyer à l'API et à l'utilisateur. 5.3. Requête PUT 5.3.1. Test de la requête PUT La requête PUT peut être utilisée pour mettre à jour les enregistrements. Il faut envoyer le même JSON que dans la requête POST ci-dessus en fournissant l'identifiant de l'enregistrement mis à jour dans l'URL. Par exemple, nous voulons changer l'enregistrement avec id=5. Préparez le JSON en brut comme suit : {"nom":"Jeff Besos","position":"CEO","société":"Amazon","téléphone":"123-123-1233","DATE DE NAISSANCE":"1982-01-19"} et envoyer la requête PUT à : localhost:4040/persons/1 5.3.2. Comment fonctionne la requête PUT def update_person(self,request:UpdatePersonRequest): """ > Mettre à jour une personne dans la base de données :param request: L'objet de la requête qui est passé au service :type request: UpdatePersonRequest :return: UpdatePersonResponse() """ # IRIS ORM if iris.cls('Sample.Person')._ExistsId(request.id): person = iris.cls('Sample.Person')._OpenId(request.id) if (v:=request.person.company) is not None: person.Company = v if (v:=request.person.name) is not None: person.Name = v if (v:=request.person.phone) is not None: person.Phone = v if (v:=request.person.title) is not None: person.Title = v if (v:=request.person.dob) is not None: person.DOB = v Utils.raise_on_error(person._Save()) return UpdatePersonResponse() En utilisant IRIS ORM, nous pouvons vérifier si l'identifiant mène à une personne, si c'est le cas, nous pouvons le mettre à jour en utilisant nos nouvelles informations et le sauvegarder dans notre base de données. 5.4. Requête DELETE 5.4.1. Test de la requête DELETE Pour les demandes de suppression, cette API REST attend uniquement l'identifiant de l'enregistrement à supprimer. Par exemple, si l'id=5, l'appel DELETE suivant supprimera l'enregistrement : localhost:5000/persons/1 5.4.2. Comment fonctionne la requête DELETE def delete_person(self,request:DeletePersonRequest): """ > Supprimer une personne de la base de données :param request: L'objet de la requête qui est passé au service :type request: DeletePersonRequest :return: La réponse est renvoyée. """ sql_select = """ DELETE FROM Sample.Person as Pers WHERE Pers.id = ? """ rs = iris.sql.exec(sql_select,request.id) response = DeletePersonResponse() return response Cette fois, en utilisant la fonction iris python sql.exec , nous pouvons directement exécuter un code SQL dans la base de données IRIS et supprimer la personne. 6. Comment commencer le codage Ce référentiel est prêt à être codé en VSCode avec les plugins InterSystems. Ouvrez /src/python/person/app.py pour changer quoi que ce soit sur l'api. Ouvrez /src/python/person/bo.py pour pouvoir modifier les choses liées aux demandes internes, c'est ici que vous pouvez utiliser SQL - il sera compilé dans le conteneur docker IRIS en cours d'exécution. 7. Contenu du dépôt 7.1. Dockerfile Le dockerfile le plus simple pour lancer IRIS. Utilisez le fichier docker-compose.yml associé pour configurer facilement des paramètres supplémentaires comme le numéro de port et l'endroit où vous mappez les clés et les dossiers d'hôte. 7.2. .vscode/settings.json Fichier de configuration pour vous permettre de coder immédiatement en VSCode avec le plugin VSCode ObjectScript) 7.3. .vscode/launch.json Fichier de configuration si vous voulez déboguer avec VSCode ObjectScript
Article
Lucas Enard · Sept 18, 2022

Un exemple simple de client Fhir en c#

# 1. Fhir-client-net Ceci est un client fhir simple en c# pour s'exercer avec les ressources fhir et les requêtes CRUD vers un serveur fhir. Notez que pour la majeure partie, l'autocomplétion est activée. [GitHub](https://github.com/LucasEnard/fhir-client-net) - [1. Fhir-client-net](#1-fhir-client-net) - [2. Préalables](#2-préalables) - [3. Installation](#3-installation) - [3.1. Installation pour le développement](#31-installation-pour-le-développement) - [3.2. Portail de gestion et VSCode](#32-portail-de-gestion-et-VSCode) - [3.3. Avoir le dossier ouvert à l'intérieur du conteneur](#33-avoir-le-dossier-ouvert-à-l'intérieur-du-conteneur) - [4. Serveur FHIR](#4-serveur-FHIR) - [5. Présentation pas à pas](#5-présentation) - [5.1. Partie 1](#51-part-1) - [5.2. Partie 2](#52-part-2) - [5.3. Partie 3](#53-part-3) - [5.4. Partie 4](#54-part-4) - [5.5. Conclusion de la présentation](#55-conclusion-de-la-présentation) - [7. Comment commencer le codage](#7-comment-commencer-le-codage) - [8. Ce qu'il y a dans le référentiel](#8-ce-qu'il-y-a-dans-le-référentiel) - [8.1. Dockerfile](#81-dockerfile) - [8.2. .vscode/settings.json](#82-vscodesettingsjson) - [8.3. .vscode/launch.json](#83-vscodelaunchjson) # 2. Préalables Assurez-vous que [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) et [Docker desktop](https://www.docker.com/products/docker-desktop) sont installé. Si vous travaillez à l'intérieur du conteneur, ces modules sont déjà installés. Si ce n'est pas le cas, utilisez nugget pour installer `Hl7.Fhir.R4`, nous allons utiliser Model et Rest à partir de celui-ci : [Hl7.Fhir.Model](https://docs.fire.ly/projects/Firely-NET-SDK/model.html) [Hl7.Fhir.Rest](https://docs.fire.ly/projects/Firely-NET-SDK/client.html) # 3. Installation ## 3.1. Installation pour le développement Clone/git tire le repo dans n'importe quel répertoire local, par exemple comme indiqué ci-dessous : ``` git clone https://github.com/LucasEnard/fhir-client-net.git ``` Ouvrez le terminal dans ce répertoire et lancez : ``` docker build . ``` ## 3.2. Portail de gestion et VSCode Ce référentiel est prêt pour [VS Code](https://code.visualstudio.com/). Ouvrez le dossier `fhir-client-net` cloné localement dans VS Code. Si vous y êtes invité (coin inférieur droit), installez les extensions recommandées. ## 3.3. Avoir le dossier ouvert à l'intérieur du conteneur Vous pouvez être *à l'intérieur* du conteneur avant de coder si vous le souhaitez. Pour cela, il faut que docker soit activé avant d'ouvrir VSCode. Ensuite, dans VSCode, lorsque vous y êtes invité ( coin inférieur droit ), rouvrez le dossier à l'intérieur du conteneur afin de pouvoir utiliser les composants python qu'il contient. La première fois que vous effectuez cette opération, cela peut prendre plusieurs minutes, le temps que le conteneur soit préparé. Si vous n'avez pas cette option, vous pouvez cliquer dans le coin inférieur gauche et cliquer sur `press reopen in container` puis sélectionner `From Dockerfile` [Plus d'informations ici](https://code.visualstudio.com/docs/remote/containers) ![Architecture](https://code.visualstudio.com/assets/docs/remote/containers/architecture-containers.png) En ouvrant le dossier à distance, vous permettez à VS Code et à tous les terminaux que vous ouvrez dans ce dossier d'utiliser les composants c# dans le conteneur. Si vous y êtes invité (coin inférieur droit), installez les extensions recommandées. # 4. Serveur FHIR Pour réaliser cette présentation, vous aurez besoin d'un serveur FHIR. Vous pouvez soit utiliser le vôtre, soit vous rendre sur le site [InterSystems free FHIR trial](https://portal.live.isccloud.io) et suivre les étapes suivantes pour le configurer. En utilisant notre essai gratuit, il suffit de créer un compte et de commencer un déploiement, puis dans l'onglet `Overview` vous aurez accès à un endpoint comme `https://fhir.000000000.static-test-account.isccloud.io` que nous utiliserons plus tard. Ensuite, en allant dans l'onglet d'informations d'identification `Credentials`, créez une clé api et enregistrez-la quelque part. C'est maintenant terminé, vous avez votre propre serveur fhir pouvant contenir jusqu'à 20 Go de données avec une mémoire de 8 Go. # 5. Présentation pas à pas La présentation pas à pas du client se trouve à `/Client.cs`. Le code est divisé en plusieurs parties, et nous allons couvrir chacune d'entre elles ci-dessous. ## 5.1. Partie 1 Dans cette partie, nous connectons notre client à notre serveur en utilisant Fhir.Rest. ``` // Partie 1 // Creation of an htpclient holding the api key of the server as an header var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("x-api-key", "api-key"); var settings = new FhirClientSettings { Timeout = 0, PreferredFormat = ResourceFormat.Json, VerifyFhirVersion = true, // PreferredReturn can take Prefer.ReturnRepresentation or Prefer.ReturnMinimal to return the full resource or an empty payload PreferredReturn = Prefer.ReturnRepresentation }; // Création de notre client en utilisant une correcte url var client = new FhirClient("url",httpClient,settings); ``` Afin de vous connecter à votre serveur, vous devez modifier la ligne : ``` httpClient.DefaultRequestHeaders.Add("x-api-key", "api-key"); ``` Et cette ligne aussi : ``` var client = new FhirClient("url",httpClient,settings); ``` L`'url'` est un point de terminaison tandis que l'`"api-key"` est la clé d'api pour accéder à votre serveur. Notez que si **vous n'utilisez pas** un serveur InterSystems, vous pouvez vérifier comment autoriser vos accès si nécessaire. Comme ça, nous avons un client FHIR capable d'échanger directement avec notre serveur. ## 5.2. Partie 2 Dans cette partie, nous créons un Patient en utilisant Fhir.Model et nous le complétons avec un HumanName, en suivant la convention FHIR, `use` et `family` sont des chaînes et `given` est une liste de chaînes. De la même manière, un patient peut avoir plusieurs HumanNames, donc nous devons mettre notre HumanName dans une liste avant de le mettre dans notre patient nouvellement créé. ``` // Partie 2 // Building a new patient and setting the names var patient0 = new Patient(); patient0.Name.Add(new HumanName().WithGiven("GivenName").AndFamily("FamilyName")); // Creation of our client in the server // It is to be noted that using SearchParams you can check if an equivalent resource already exists in the server // For more information https://docs.fire.ly/projects/Firely-NET-SDK/client/crud.html var created_pat = client.Create(patient0); Console.Write("Partie 2 : Identifiant du patient nouvellement créé : "); Console.WriteLine(created_pat.Id); ``` Après cela, nous devons sauvegarder notre nouveau Patient sur notre serveur en utilisant notre client. Notez que si vous lancez `Client.cs` plusieurs fois, plusieurs Patients ayant le nom que nous avons choisi seront créés. C'est parce que, suivant la convention FHIR, vous pouvez avoir plusieurs Patients avec le même nom, seul l' `id` est unique sur le serveur. Vérifier [la documentation](https://docs.fire.ly/projects/Firely-NET-SDK/client/search.html#searching) pour avoir plus d'information. Il est à noter qu'en utilisant SearchParams vous pouvez vérifier si une ressource équivalente existe déjà sur le serveur avant de la créer. Pour plus d'information https://docs.fire.ly/projects/Firely-NET-SDK/client/crud.html Nous conseillons donc de commenter la ligne après le premier lancement. ## 5.3. Partie 3 Dans cette partie, nous avons un client qui recherche un patient nommé d'après celui que nous avons créé précédemment. ``` // Part 3 // This gets all the Patient having the exact name "FamilyName" and we take the first one // Note that if you have multiple patients with the same name, you will get only the first one // We advise to use the id, the names, and any other informations for the SearchParams to be sure to get the right patient var q = new SearchParams().Where("name:exact=FamilyName"); Bundle bund = client.Search(q); patient0 = bund.Entry[0].Resource as Patient; Console.Write("Part 3 : Name of the patient we found by searching : "); Console.WriteLine(patient0.Name[0]); // Creation of our patient telecom, here a phone number patient0.Telecom.Add(new ContactPoint(new ContactPoint.ContactPointSystem(),new ContactPoint.ContactPointUse(),"1234567890")); // Change the given name of our patient patient0.Name[0].Given = new List() { "AnotherGivenName" }; Console.Write("Part 3 : Name of the changed patient : "); Console.WriteLine(patient0.Name[0]); Console.Write("Part 3 : Phone of the changed patient : "); Console.WriteLine(patient0.Telecom[0].Value); // Update the patient var update_pat = client.Update(patient0); ``` Une fois que nous l'avons trouvé, nous ajoutons un numéro de téléphone à son profil et nous changeons son prénom en un autre. Maintenant nous pouvons utiliser la fonction de mise à jour de notre client pour mettre à jour notre patient sur le serveur. ## 5.4. Partie 4 Dans cette section, nous voulons créer une observation pour notre patient. Pour ce faire, nous avons besoin de son identifiant, qui est son identifiant unique. A partir de là, nous remplissons notre observation et ajoutons comme sujet, l'identifiant de notre Patient. ``` // Part 4 // Building of our new observation Observation obsv = new Observation { Value = new Quantity(70, "kg"), Code = new CodeableConcept { Coding = new List { new Coding { System = "http://loinc.org", Code = "29463-7", Display = "Body weight" } }}, Category = new List { new CodeableConcept { Coding = new List { new Coding { System = "http://snomed.info/sct", Code = "276327007", Display = "Body weight" } } }}, Status = new ObservationStatus {}, Subject = new ResourceReference { Reference = "Patient/" + update_pat.Id} }; // Creation of our observation in the server var new_obsv = client.Create(obsv); Console.Write("Part 4 : Id of the observation : "); Console.WriteLine(new_obsv.Id); ``` Ensuite, nous enregistrons notre observation à l'aide de la fonction create. ## 5.5. Conclusion de la présentation Si vous avez suivi ce parcours, vous savez maintenant exactement ce que fait Client.cs, vous pouvez le lancer et vérifier votre Patient et votre Observation nouvellement créés sur votre serveur. Pour le lancer, ouvrez un terminal VSCode et entrez : ``` dotnet run ``` Vous devriez voir des informations sur le Patient créé et son observation. Si vous utilisez un serveur Intersystems, allez à `API Deployement`, autorisez-vous avec la clé api et d'ici vous pouvez OBTENIR par id le patient et l'observation que nous venons de créer. # 7. Comment commencer le codage Ce référentiel est prêt à être codé dans VSCode avec les plugins InterSystems. Ouvrez `Client.cs` pour commencer à coder ou utiliser l'autocomplétion. # 8. Ce qu'il y a dans le référentiel ## 8.1. Dockerfile Un dockerfile pour créer un dot net env pour que vous puissiez travailler. Utilisez `docker build .` pour construire et rouvrir votre fichier dans le conteneur pour travailler à l'intérieur de celui-ci. ## 8.2. .vscode/settings.json Fichier de paramètres ## 8.3. .vscode/launch.json Fichier de configuration si vous voulez déboguer
Annonce
Irène Mykhailova · Sept 1, 2022

La rentrée en FHIR!

Salut la Сommunauté ! Nous sommes ravis de vous annoncer le lancement de notre nouveau sujet de publications sur la Communauté de Developpeurs francophones ! Cette rentrée scolaire on commence à parler de FHIR ! Vous avez peut-être remarqué qu'avant, nous nous sommes efforcé de suivre l'actualité avec une priorité pour nos thématiques. Cette fois, nous allons préparer l'ensemble de l'affaire pour que vous en appreniez plus en profondeur sur le FHIR. Nous prévoyons d'inclure des articles, des vidéos, des annonces de rencontres et des reportages de ces rencontres liés à la thématique actuelle. Nous espérons que cela vous aidera à tirer le meilleur parti de cette expérience. Et bien sûr n'hésitez pas à vous joindre à nos efforts et à partager vos connaissances sur le sujet. Comme je l'ai déjà mentionné, quelques prochains mois seront consacrés au FHIR. Nous espérons que ça vous plaira et que vous l'utiliserez dans vos projets. Partagez vos idées concernant le sujet que nous avons choisi et l'idée générale de choisir le sujet à aborder dans les commentaires ! Restez à l'écoute! -- PS. J'ajouterai les posts publiés dans les commentaires de cette annonce afin que chacun puisse suivre le sujet ! Hello Developers! If you're here to learn more about this activity - here it is! Our DC FR decided to dedicate the next couple of months to FHIR. What does it mean? We're going to post articles, videos, announcements and reports from different meetups - all related to FHIR! In the comments here, I will add links to the corresponding posts so everyone could join. Besides, most of the articles have translations in English so don't get discouraged that we're doing it in French! Les posts liés à la rentrée en FHIR: 1. Découvrez FHIR - Getting to know FHIR 2. Présentation de FHIRaaS - FHIRaaS overview 3. Bibliothèque de SMART sur FHIR JS et les exemples dans iris-on-fhir - SMART on FHIR JS Library and examples in iris-on-fhir 4. Meetup FHIR : API Mon Espace Santé 5. Présentation d'IRIS sur FHIR - IRIS on FHIR Overview 6. Un exemple simple d'un client Fhir en java - A simple example of a Fhir client in java 7. Un exemple simple de client Fhir en python - A simple example of a Fhir client in python 8. Un exemple simple de client Fhir en c# - A simple example of a Fhir client in c# 9. Formulaires et questionnaire dans FHIR : De la création à l'utilisation - Questionnaire & Forms in FHIR : From creation to usage 10. Client FHIR pour se connecter à n'importe quel serveur FHIR public en utilisant Embedded Python - FHIR Client to connect any open FHIR Server by using embedded python 11. Création de Patient et d'Observations de Patient en utilisant l'application iris-fhir-client - Creating Patient and Patient Observations by using iris-fhir-client application 12. Comment FHIR et les API améliorent l’interopérabilité dans la santé ? 13. HL7v2 vers FHIR, c'est facile ! - HL7v2 to FHIR, it's easy ! 14. Meetup FHIR #11 : API Mon Espace Santé - Paris & Online 15. Réalisation complète d'IRIS en Python : DataTransformation d'un CSV vers un serveur FHIR - Full Python IRIS production : DataTransformation of a CSV to a FHIR server 16. Créons un profil FHIR à l'aide de SUSHI Partie 1 - Let's create a FHIR profile using SUSHI Part 1 17. Utilisation de SUSHI pour la création de profils FHIR, partie 2 18. Invitation du service FHIR Accelerator à votre fête de microservices Kubernetes - Invite the FHIR Accelerator Service to your Kubernetes Microservice Party 19. Création et validation de n'importe quelle ressource HL7 FHIR en utilisant le schéma FHIR à l'aide d'IntelliSense et de la fonctionnalité de complétion automatique dans VS Code - Creating and Validating Any HL7 FHIR Resource by using FHIR schema with the help of IntelliSense and auto complete functionality in VS Code 20. Construction d'un référentiel FHIR + le serveur d'autorisation OAuth2/serveur de ressources sur IRIS for Health - Partie 1 - Building an FHIR Repository + OAuth2 Authorization Server/Resource Server Configuration on IRIS for Health Part 1 21. Construction d'un référentiel FHIR + le serveur d'autorisation OAuth2/serveur de ressources sur IRIS for Health - Partie 2 - Building an FHIR Repository + OAuth2 Authorization Server/Resource Server Configuration on IRIS for Health Part 2 22. [Vidéo] Qu'est-ce que le serveur InterSystems FHIR ? 23. [Vidéo] Le FHIR, une brique essentielle de votre Stratégie DATA ? 24. Utilisation de l'interface utilisateur fhir-react pour une visualisation rapide - Using fhir-react UI for quick visualization 25. iOS, FHIR et IRIS for Health - iOS, FHIR and IRIS for Health 26. [Vidéo] Qu'est-ce que le service InterSystems IRIS FHIR Accelerator 27. L'ère de la norme FHIR : pourquoi FHIR devrait vous intéresser ? 28. [Vidéo] Projection FHIR vers SQL pour faciliter l’exploitation des données et le reporting 29. Le serveur FHIR fait fonctionner le projet Vulcain au cours du HL7 FHIR Connectathon - FHIR Server powers Project Vulcan @ HL7 FHIR Connectathon 30. Quelques exemples d'adaptateurs d'interopérabilité FHIR - Some FHIR interoperability adapter examples
Article
Corentin Blondeau · Avr 28

Présentation de l'adaptateur UDP

Bonjour à tous,Cet article fait suite à la question que j'avais posée à la communauté L'adaptateur UDP ne fonctionne pasDans cet article, je vais vous présenter les points suivants1) Qu'est-ce que "UDP"?2) L'état actuel d'Iris avec UDP3) Ma solution avec l'adaptateur UDP 1) Qu'est-ce que "UDP"? L'acronyme UDP signifie User Datagram Protocol (protocole de datagramme utilisateur). Il s'agit de l'un des principaux protocoles composant la suite IP (Internet Protocol), utilisé pour la transmission de données sur un réseau. Voici quelques fonctionnalités clés de l'UDP: Sans connexion : L'UDP n'établit pas de connexion avant d'envoyer des données, ce qui signifie qu'il peut envoyer des messages (datagrammes) sans échange préalable. Peu fiable : Il n'y a aucune garantie que les messages envoyés via l'UDP arriveront à destination. Il n'y a pas de récupération des erreurs ni de retransmission des paquets perdus. Vitesse : Étant donné que l'UDP est sans connexion et qu'il ne nécessite pas de vérification ou de récuperation des erreurs, il est généralement plus rapide que le TCP (Transmission Control Protocol), ce qui le rend approprié pour les applications où la vitesse est cruciale. Constrruit autour des datagrammes : L'UDP envoie des messages sous forme de paquets discrets, qui peuvent être de longueur variable. Chaque paquet est traité indépendamment. Cas d'utilisation : L'UDP est couramment utilisé dans les applications où la vitesse est plus importante que la fiabilité, telles que la diffusion vidéo en continu, les jeux en ligne, la voix sur IP (VoIP) et les communications en temps réel. En général, l'UDP est un protocole léger qui est utile pour des applications spécifiques où une faible latence est essentielle. 2) L'état actuel d'Iris avec UDP Bien entendu, InterSystems IRIS permet d'utiliser ce protocole pour envoyer et recevoir des données.En tant que protocole REST, il y a deux façons de le faire :- avec ##class(%Net.UDP).%New().- avec EnsLib.UDP.OutboundAdapter et EnsLib.UDP.InboundAdapter ##class(%Net.UDP).%New() Même si la documentation de la classe et très détaillée, voici le lien de la documentation d'InterSystems sur la façon de l'utiliser.Pour envoyer/recevoir des données, il faut utiliser une instance de class(%Net.UDP).%New() et une méthode associée.En résumé, pour envoyer des données (sur localhost avec le port 3001) : SET client = ##class(%Net.UDP).%New() SET addrbin = ##class(%Net.UDP).GetHostAddr("127.0.0.1") Set status = client.Send("message text", addrbin, 3001) Pour recevoir des données (sur localhost avec le port 3001) : Set sobj = ##class(%Net.UDP).%New(3001,"127.0.0.1") Set data = sobj.Recv() EnsLib.UDP.OutboundAdapter and EnsLib.UDP.InboundAdapter Ce dernier est plus simple : c'est un adaptateur.La documentation : EnsLib.UDP.OutboundAdapter et EnsLib.UDP.InboundAdapterPour envoyer des données : Set status = ..Adapter.SendStream(stream) Pour obtenir des données : Set status = ..Adapter.Receive(stream) Cependant, cela ne fonctionne pas. J'ai posé ma question à la communauté "L'adaptateur UDP ne fonctionne pas" et j'ai créé un ticket pour le WRC.La réponse a été formulée comme suit : Cet exécutable sous-jacent n'est plus installé dans le produit depuis Ensemble 2018.1. J'ai vérifié au niveau interne et le JIRA DP-437486 a été soumis pour mettre à jour ces adaptateurs afin d'utiliser la classe %Net.UDP, mais cela sera soumis à l'approbation de la gestion des produits et aux ressources de développement disponibles. Malheureusement, la seule option possible actuellement est de créer votre propre adaptateur personnalisé en utilisant la classe %Net.UDP. Voici les deux principales différences entre la classe (%Net.UDP) et l'adaptateur EnsLib.UDP.OutboundAdapter La classe %Net.UDP utilise la classe système $System.UDP, elle utilise donc le code du noyau Cache/IRIS pour envoyer/recevoir les messages UDP, alors que l'adaptateur UDP utilise un tuyau de commande pour appeler des exécutables externes afin d'envoyer/recevoir le message UDP. La classe %Net.UDP envoie/lit une chaîne de caractères alors que l'adaptateur UDP utilise un flux pour envoyer/lire les messages. 3) Ma solution avec l'adaptateur UDP J'ai donc écrit ma propre méthode (approuvée par le service d'assistance) pour envoyer des données : /// Adaptateur pour l'envoi de données avec une connexion UDP Class USER.UDP.OutboundAdapter Extends Ens.Adapter { /// Hôte du serveur UDP Property Host As %String [ Required ]; /// Port du serveur UDP Property Port As %Integer [ Required ]; /// si 1, le texte qui sera envoyé est affiché Property UDPTrace As %Integer(VALUELIST = ",0,1") [ InitialExpression = 0, Required ]; Parameter SETTINGS = "Host:Basic,Port:Basic,UDPTrace:Basic"; /// Envoi de "texte" par le biais de la connexion UDP Method SendDataText(text As %String) As %Status { Try { Set status = $$$OK If ..UDPTrace=1 { Do ..ShowText(text) } Set udpClient = ##class(%Net.UDP).%New() Set addrbin = ##class(%Net.UDP).GetHostAddr(..Host) Set sentBytes = udpClient.Send(text, addrbin, ..Port) } Catch exception { Set status = exception.AsStatus() } Return status } /// Conversion du "stream" en texte et son envoi via la connexion UDP Method SendDataStream(stream As %Stream.Object) As %Status { Try { Do stream.Rewind() Set text = "" While 'stream.AtEnd{ Set text = text _ stream.Read() } Set status = ..SendDataText(text) } Catch exception { Set status = exception.AsStatus() } Return status } /// Conversion de l'"objet" en json et son envoi via la connexion UDP Method SendDataJSONObject(object As %RegisteredObject, format As %String = "iw") As %Status { Try { Set status = ##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(.stream,object,,,,format) $$$ThrowOnError(status) Set status = ..SendDataStream(stream) } Catch exception { Set status = exception.AsStatus() } Return status } /// Un texte est pris en entrée, /// Les traces de l'objet associé sont affichées Method ShowText(text As %String) { Set nbParty = $SYSTEM.SQL.CEILING($LENGTH(text)/1100) For ii=1:1:nbParty { $$$TRACE($EXTRACT(text,ii,ii+1100)) } } } J'espère que cet article a été, sinon utile, du moins intéressant. Je n'ai pas testé EnsLib.UDP.InboundAdapter, donc n'hésitez pas à ajouter plus d'informations à la discussion.Corentin
Article
Irène Mykhailova · Mars 25, 2022

Hacking Health Camp 2022 (jour 1)

![image](https://user-images.githubusercontent.com/47849411/160096345-a2da2de7-ed51-4208-aa78-fc8130cb4c71.png) Salut les développeurs, Nous sommes au [Hacking Health Camp 2022](http://hackinghealth.camp/) à Strasbourg et ce durant tout le week-end. Toute l'équipe de modération est sur place @Irène.Mykhailova, @Guillaume.Rongier7183, @Lorenzo.Scalese : ![image](https://user-images.githubusercontent.com/47849411/160095588-0504fbae-fb07-4f46-83b7-b9cecd26d62d.png) Et on s'éclate ! ![image](https://user-images.githubusercontent.com/47849411/160095389-e397de16-f7fe-48f1-95de-58a1abdc7f4c.png) **Nous vous tiendrons informé tout au long de la journée du déroulement de cet évènement.** Pour rappel, vous trouverez ici notre site dédié au hackathon : https://hackinghealth-2022.isccloud.io/ *MIS A JOUR 13:00* Donc, la première partie des conférences est terminée et il est temps d'aller explorer les lieux ![image](https://user-images.githubusercontent.com/9782856/160118890-eff41fbb-1f28-4cac-89c9-c8f00d6d8b62.png) et de prendre une photo des équipes InterSystems qui se mobilisent tout le weekend : ![image](https://user-images.githubusercontent.com/9782856/160120883-94ef050f-8a41-4d50-b092-fb96f767ecc6.jpg) Nous attendons la présentation de David Hancock sur le sujet IMPROVING YOUR HEALTH SYSTEM OUTCOMES BY IMPROVING YOUR DIGITAL HEALTH SYSTEMS après cette pause déjeuner. *MIS A JOUR 18:00* David a sensibilisé et motivé les troupes sur l'importance de l'interop. ![image](https://user-images.githubusercontent.com/9782856/160241318-62fa8d43-e182-46fd-91a5-5d12e9ec9bea.png) ![image](https://user-images.githubusercontent.com/9782856/160170823-2f2b00a6-a3d6-4362-aa46-35953074c409.png) ![IMG_20220325_234205](https://user-images.githubusercontent.com/9782856/160240286-661c35b4-52ce-4790-a540-3f0cd9f31455.jpg) La conférence est terminée, les coaches se sont réunis (comme les Avengers ;) ) et on attend avec impatience les pitchs des projets. ![IMG_20220325_234317](https://user-images.githubusercontent.com/9782856/160240356-09bd208b-dc38-4601-a855-dd1c770fc5a7.jpg) ![image](https://user-images.githubusercontent.com/9782856/160172262-f71e5d81-612f-4d02-a40c-fb87455d0e8f.png) ![IMG_20220326_133508](https://user-images.githubusercontent.com/9782856/160240411-b47ed6e3-8641-469d-906d-52674fd1e507.jpg) *MIS A JOUR 21:00* @Guillaume.Rongier7183 a mis le feu avec son intro FHIR ![IMG_20220325_184205](https://user-images.githubusercontent.com/9782856/160240172-3e4624c2-ea18-4147-9ce6-27fc334bf307.jpg) Les groupes se forment, 1 coeur = 1 projet et c'est parti pour 50h de folie créative et collaborative ![image](/sites/default/files/inline/images/img_20220325_195420.jpg) ![image](/sites/default/files/inline/images/img_20220325_202122.jpg) Et c'est tout pour aujourd'hui. A demain! Quelle chance.... J'aurais bien aimé en être, ça m'aurait rappelé quand j'y vivais et y travaillais :) En tous cas un grand merci pour le partage <3 @Jacques.Huser Je l'ai fait avec grand plaisir Je suis heureuse d'entendre que ce rapport a rappelé de bons souvenirs. Bonjour, j’ai un projet de logiciel pour l’hôpital et je cherche un développeur :) est-ce possible d’entrer en contact ? Bonjour, Je vous envoie mes coordonnées par messages privées, nous pourrons discuter de votre besoin sur un autre canal (mail par exemple). Cordialement, Guillaume
Article
Irène Mykhailova · Jan 12, 2023

Tests unitaires pour les transformations de données

Voulez-vous être sûr que vos transformations de données fonctionnent comme prévu avec une seule commande ? Et que diriez-vous d'écrire des tests unitaires pour vos transformations de données de manière simple et rapide ?  Les transformations de données sont généralement très présentes lorsqu'on parle d'interopérabilité. Ces transformations de données sont utilisées pour convertir les données entre différents systèmes ou applications dans votre code, et elles ont donc un rôle très important à jouer. ### Les stratégies de test Après avoir examiné le concept de la [Pyramide de tests] (https://martinfowler.com/bliki/TestPyramid.html) et certains [articles] (https://medium.com/@timothy.cochran/test-pyramid-the-key-to-good-automated-test-strategy-9f3d7e3c02d5) à ce sujet, on peut se faire une idée rapide du fait qu'il est préférable de disposer d'une base solide de tests automatisés de bas niveau moins coûteux que de tester uniquement l'interface utilisateur. Dans un contexte d'interopérabilité, j'ai constaté dans plusieurs projets qu'il vaut vraiment la peine d'investir un peu d'effort dans l'écriture de tests unitaires de transformation de données, surtout lorsque nous traitons différents scénarios (par exemple HL7, messages personnalisés, etc.). Cela nous permettra d'être sûrs que notre logique de transformation de données fonctionne comme prévu après l'introduction de nouveaux changements. Même après la résolution d'un problème avec une transformation de données, nous pouvons facilement créer un nouveau test avec le message qui a causé le problème, de sorte que nous sommes sûrs de ne pas obtenir la même erreur à l'avenir.   ![](/sites/default/files/inline/images/images/test-pyramid.png)   ### Un petit assistant qui utilise le cadre %UnitTest Pour vous aider à écrire et à exécuter des tests de transformation de données, je partage un exemple qui utilise le cadre [IRIS %UnitTest framework] (https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=TUNT_preface). L'objectif est de vous permettre de définir des ensembles différents de messages d'entrée et de messages de sortie correspondants prévus pour chaque transformation de données à tester, en tant que fichiers texte externes. Supposons que vous vouliez créer des tests unitaires pour une transformation de données appelée Sample.Health.a31ToPatient, alors vous devez procéder comme suit : 1. Créez une nouvelle classe étendue DataTransformTestCase. Définissez le paramètre <TestDirectory> comme le répertoire dans lequel vous allez stocker vos tests de transformation de données. 2. Dans votre <TestDirectory>, créez un sous-répertoire appelé  Sample.Health.a31ToPatient. Ce sous-répertoire stockera les ensembles d'entrées et de sorties attendues que vous souhaitez tester dans la transformation de données. 3. Dans le sous-répertoire Sample.Health.a31ToPatient is faut ajouter les messages d'entrée et la sortie attendue comme *.in.txt and *.out.txt. 4. Lancez vos tests ! Vous pouvez voir les résultats dans le portail %UnitTest Framework dans IRIS. Si vous obtenez une erreur, le fichier *.gen.txt sera généré, afin que vous puissiez comparer la sortie réelle à la sortie attendue et voir ce qui ne va pas.    ![](/sites/default/files/inline/images/images/dc_datatransformunittest-2.png)     ![](https://raw.githubusercontent.com/intersystems-community/iris-datatransform-unittest/master/img/datatransform-unittest-ok.gif) ### Lancez l'exemple vous-même ! Vous pouvez télécharger, lancer l'exemple et lire plus de détails dans [Open Exchange](https://openexchange.intersystems.com/package/iris-datatransform-unittest).  
Article
Irène Mykhailova · Juin 19, 2023

Tutoriel : Déploiement de votre application dockerisée sur Google Cloud

 Aujourd'hui, la grande majorité des applications sont déployées sur des services de cloud public. Les avantages sont multiples : réduction des ressources humaines et matérielles nécessaires, possibilité d'évoluer rapidement et à moindre coût, plus grande disponibilité, fiabilité, évolutivité élastique et options permettant d'améliorer la protection des actifs numériques. L'une des options les plus prisées est le Google Cloud. Il nous permet de déployer nos applications à l'aide de machines virtuelles (Compute Engine), de conteneurs Docker (Cloud Run) ou de Kubernetes (Kubernetes Engine). Le premier n'utilise pas Docker. Elle utilise plutôt une machine virtuelle sous Windows ou Linux où vous installez votre serveur d'application et déployez votre application. Les deuxième et troisième options utilisent Docker. Cependant, la dernière option fonctionne mieux pour les applications à grande échelle, avec de nombreuses instances Docker exécutées à l'aide de l'option auto-scale. La deuxième option, Cloud Run, convient mieux aux applications de petite et moyenne échelle. Cet article va vous guider dans le processus d'utilisation, de configuration et d'exécution des applications Docker sur GCP (Google Cloud Platform) à l'aide de Cloud Run. ## Qu'est-ce que le nuage Google Cloud ? Google Cloud est un fournisseur de services de Cloud Public. Pour en savoir plus sur GCP, accédez à cette URL () : ![](/sites/default/files/inline/images/images/image-20230509202223-1.png)   Il s'agit d'un diagramme interactif. Si vous souhaitez obtenir plus de détails sur le sujet, il vous suffit de cliquer sur une section qui vous intéresse. C'est une façon très divertissante d'en savoir plus. ## Obtention d'un exemple d'application Docker à déployer Nous allons utiliser une application Docker prête du catalogue InterSystems Open Exchange. Pour l'obtenir, suivez les étapes suivantes 1. Assurez-vous que Git est installé. 2. Allez sur https://openexchange.intersystems.com/package/iris-rest-api-template. 3. Clonez/git pull le référentiel dans n'importe quel répertoire local: git clone git@github.com:intersystems-community/iris-rest-api-template.git Le modèle iris-rest-api-template est une application backend avec une base de données IRIS et une API REST d'IRIS écrite en ObjectScript. Nous allons déployer cette application sur GCP Cloud Run. ## Obtention de vos références AWS Pour commencer, vous aurez besoin d'un compte GCP et d'un utilisateur disposant d'une clé d'accès. Pour ce faire, procédez comme suit : Go to https://console.cloud.google.com and enter with an existent user and password or click the link Create account if you don’t have one: ![](/sites/default/files/inline/images/images/image-20230509202520-2.png)   ## Installation de l'outil gcloud CLI et y affecter l'utilisateur créé L'outil gcloud CLI est utilisé pour tirer l'image Docker vers AWS ECR (c'est un peu comme le Docker Hub pour les images Docker AWS). Pour l'installer, procédez comme suit : 1. Allez sur https://cloud.google.com/sdk/docs/install et sélectionnez les instructions d'installation adaptées au système d'exploitation de votre ordinateur. ![](/sites/default/files/inline/images/images/image-20230509202635-3.png)   2. Choisissez l'option Run 'gcloud init' : ![](/sites/default/files/inline/images/images/image-20230509202717-4.png)   3. Connectez-vous avec votre utilisateur Google Cloud. 4. Autoriser l'accès à toutes les options requises. Vous êtes maintenant connecté à GCP en utilisant gcloud CLI. 5. À ce stade, choisissez l'option \[5\] (Créer un nouveau projet) : ![](/sites/default/files/inline/images/images/image-20230509202820-5.png)   6. Saisissez l'ID du projet avec la valeur iriscontainer. ![](/sites/default/files/inline/images/images/image-20230509202854-6.png)   7. Félicitations ! Le projet est créé et prêt à être exploité par CLI. 8. Allez dans le navigateur et choisissez le projet dans le menu déroulant de la console GCP : ![](/sites/default/files/inline/images/images/image-20230509202935-7.png)   9. Passez à l'onglet ALL (tous) et sélectionnez le projet iriscontainer : ![](/sites/default/files/inline/images/images/image-20230509203020-8.png)   10. Choisissez l'option Billing (facturation) dans le menu de gauche : ![](/sites/default/files/inline/images/images/image-20230509203100-9.png)   11. Cliquez sur le bouton LINK A BILLING ACCOUNT (LIER UN COMPTE DE FACTURATION) : ![](/sites/default/files/inline/images/images/image-20230509203128-10.png)   12. Sélectionnez un compte de facturation existant et cliquez sur le bouton SET ACCOUNT (Définir le compte) : ![](/sites/default/files/inline/images/images/image-20230509203205-11.png)   ## Téléchargement de votre application Docker sur le GCR (Google Container Registry)   1. Allez dans le terminal et activez Docker pour gcloud : gcloud auth configure-docker Les résultats de l'exécution sont présentés ci-dessous : ![](/sites/default/files/inline/images/images/image-20230509203332-12.png)   2. Allez au terminal et tapez : gcloud services <span class="hljs-built_in">enable</span> containerregistry.googleapis.com 3. Maintenant, allez dans votre terminal et trouvez le dossier où vous avez cloné le projet Git (dans mon cas, il s'agit de c:\projetos\isc\iris-rest-api-template) : ![](/sites/default/files/inline/images/images/image-20230509203511-13.png)   4. Créez une balise avant d'envoyer l'image : ![](/sites/default/files/inline/images/images/image-20230509203547-14.png)   5. Exécutez les images Docker pour obtenir le nom de votre image Docker : ![](/sites/default/files/inline/images/images/image-20230509203624-15.png)   6. Créer la balise : ![](/sites/default/files/inline/images/images/image-20230509203715-16.png)   7. Envoyer l'image Docker balisée à GCR : ![](/sites/default/files/inline/images/images/image-20230509203745-17.png)   Vous devriez obtenir les résultats d'exécution suivants : ![](/sites/default/files/inline/images/images/image-20230509203812-18.png)   À ce stade, votre projet Docker est une image Docker public sur GCR. ## Création de l'instance Docker sur GCP Cloud Run pour votre nouvelle image GCR Puisque votre image Docker est enfin sur GCR, vous pouvez l'utiliser pour lancer une instance. Dans cette section, nous allons créer une instance Docker fonctionnant sur GCP Cloud Run. Pour ce faire, suivez les instructions ci-dessous : 1. Allez dans la console GCP et recherchez Cloud Run dans la barre de recherche supérieure. Cliquez ensuite sur le lien Cloud Run : ![](/sites/default/files/inline/images/images/image-20230509203921-19.png)   2. Cliquez sur le bouton CREATE SERVICE (Créer un service) (en haut ou en bas de page) : ![](/sites/default/files/inline/images/images/image-20230509203950-20.png)   3. Dans Create service (Créer un service), sélectionnez Deploy one revision (déployer une révision) à partir d'une image de conteneur existante. 4. Cliquez sur SELECT (Sélectionner) dans le champ URL de l'image de conteneur : ![](/sites/default/files/inline/images/images/image-20230509204057-21.png)   5. Dans la boîte de dialogue de droite, cliquez sur l'onglet CONTAINER REGISTRY (registre des conteneurs). Après cela, développez le nœud d'arborescence gcr.io/iriscontainer/irissample, choisissez le dernier nœud enfant, et cliquez sur le bouton SELECT (il s'agit de l'image Docker que vous avez téléchargée) : ![](/sites/default/files/inline/images/images/image-20230509204137-22.png) 6. L'URL de l'image du conteneur est maintenant configurée. Cependant, d'autres paramètres sont encore disponibles, faites défiler l'écran pour les voir : ![](/sites/default/files/inline/images/images/image-20230509204225-23.png)   7. Appuyez sur la touche Tab pour accéder aux champs Nombre minimum et Nombre maximum d'instances et tapez 1 dans chacun d'eux (cette option nous permettra d'adapter les instances à la charge nécessaire) : ![](/sites/default/files/inline/images/images/image-20230509204309-24.png)   8. Appuyez à nouveau sur la touche Tab ou faites défiler l'écran vers le bas et cochez le bouton "All" ("Tout") (Autoriser l'accès direct à votre service à partir d'Internet). Cette option rendra l'instance disponible sur Internet. 9. Appuyez sur la touche Tab (ou faites défiler l'écran) et sélectionnez Allow unauthenticated invocations (Autoriser les invocations non authentifiées) (cette option est utile pour protéger les ressources avec des références si nécessaire) : ![](/sites/default/files/inline/images/images/image-20230509204345-25.png)   10. Cliquez sur la flèche de développement de la ligne Conteneur, Réseau, Sécurité et cliquez sur la touche Tab, ou faites défiler l'écran vers le bas pour accéder au port du conteneur. Mettez la valeur 52773 (cette option nous permettra d'envoyer les requêtes au port sans exposer ce port à la consommation extérieure) : ![](/sites/default/files/inline/images/images/image-20230509204424-26.png)   11. Appuyez à nouveau sur la touche Tab pour accéder à la rubrique Memory (mémoire) et sélectionnez 4 GiB et CPU 1 (ces options sont très importantes pour la performance car elles définissent le nombre de CPUs et la quantité de mémoire disponible pour l'instance Docker. Cependant, les coûts augmentent également si vous engagez plus de ressources) : ![](/sites/default/files/inline/images/images/image-20230509204502-27.png)   12. Dans l'environnement d'exécution, sélectionnez Défaut : ![](/sites/default/files/inline/images/images/image-20230509204558-28.png)   13. Faites défiler vers le bas ou appuyez sur la touche de tabulation pour atteindre le bouton CREATE. Appuyez ensuite sur ce bouton pour terminer. ![](/sites/default/files/inline/images/images/image-20230509204647-29.png)   14. À ce stade, vous devez attendre quelques instants pour que votre nouvelle demande soit publiée : ![](/sites/default/files/inline/images/images/image-20230509204734-30.png)   15. Vous avez réussi ! Votre application est maintenant disponible : ![](/sites/default/files/inline/images/images/image-20230509210926-1.png)   16. Copiez l'URL de l'application en haut de la page : ![](/sites/default/files/inline/images/images/image-20230509210958-2.png)   17. Ouvrez votre navigateur et tapez ce qui suit (dans mon cas, il s'agit de ): 18. Le portail de gestion IRIS (avec l'utilisateur _SYSTEM et le mot de passe SYS) est désormais actif : ![](/sites/default/files/inline/images/images/image-20230509211110-3.png)   ## Navigation dans le panneau de service Vous pouvez contrôler le service déployé. Pour ce faire, suivez les étapes suivantes : 1. Recherchez Cloud Run dans la barre de recherche supérieure et cliquez sur Cloud Run : ![](/sites/default/files/inline/images/images/image-20230509211219-4.png)   2. Cliquez sur le lien irissample pour voir les détails du service : ![](/sites/default/files/inline/images/images/image-20230509211246-5.png) 3. Dans l'onglet METRICS, il est possible de voir l'état général du service déployé, y compris la latence, le nombre de requêtes et l'utilisation de la CPU : ![](/sites/default/files/inline/images/images/image-20230509211316-6.png)   4. Dans l'onglet LOGS (journaux), vous pouvez voir les journaux de l'application : ![](/sites/default/files/inline/images/images/image-20230509211418-7.png)   5. Dans l'onglet YAML, vous pouvez vérifier et modifier la configuration du service au format YAML : ![](/sites/default/files/inline/images/images/image-20230509211510-8.png)   ## Arrêt du service N'OUBLIEZ PAS D'ARRÊTER LE SERVICE, POUR NE PAS ÊTRE FACTURÉ. Pour ce faire, suivez les instructions suivantes : 1. Recherchez Cloud Run dans la barre de recherche supérieure et cliquez sur Cloud Run : ![](/sites/default/files/inline/images/images/image-20230509211539-9.png)   2. Cochez la case irissample et cliquez sur le bouton DELETE : ![](/sites/default/files/inline/images/images/image-20230509211623-10.png) P.S. : supprimez également le projet ! ##   ## GCP et IRIS, l'architecture de référence Mark Bolinsky, l'architecte principal d'InterSystems, a publié un article présentant les meilleures pratiques et une proposition d'architecture IRIS d'InterSystems pour gérer les projets IRIS essentiels. Pour le consulter, suivez le lien https://community.intersystems.com/post/intersystems-iris-example-reference-architectures-google-cloud-platform-gcp. L'article explore des scénarios de petite, moyenne et grande échelle. ##   ## Mes impressions sur GCP   GCP est une option plus facile à utiliser qu'AWS lorsqu'il s'agit de déployer des instances IRIS. Il me semble qu'il s'agit également d'une solution moins onéreuse. D'un autre côté, AWS dispose de ressources et d'options plus avancées en matière de sécurité, de stockage et de services spécialisés tels que l'IA et le WAF. Cependant, il est plus facile de trouver des professionnels qui utilisent AWS, et c'est aussi Cloud Public le plus populaire sur le marché.  
Article
Irène Mykhailova · Mai 4, 2023

Exemple de FHIR et IntegratedML

Exemple d'utilisation de la base de données FHIR d'InterSystems IRIS for Health pour effectuer de modèles ML via InterSystems IRIS IntegratedML Description IntegratedML est une fonctionnalité intéressante pour former/tester et déployer des modèles ML. FHIR est un standard puissant pour l'interopérabilité des informations de santé. Ce projet vise à montrer comment utiliser les outils IRIS/IRIS for Health, par exemple les transformations DTL pour préparer les données FHIR à l'application de modèles ML dans IntegratedML. Voici quelques applications potentielles des idées présentées dans ce projet : Réutilisation et extension des transformations DTL dans d'autres bases de données FHIR pour des modèles ML personnalisés Utilisation des transformations DTL pour normaliser les messages FHIR et publier les modèles ML en tant que services Création d'un référentiel de modèles et de règles de transformation à utiliser dans n'importe quel jeu de données FHIR Installation Clone/git extrait le référentiel dans n'importe quel répertoire local. $ git clone https://github.com/jrpereirajr/fhir-integratedml-example.git Ouvrez le terminal dans ce répertoire et lancez: $ cd fhir-integratedml-example $ docker-compose up -d Si vous souhaitez obtenir un journal de ce qui s'est passé lors de l'installation, utilisez la commande suivante : $ docker-compose up -d > build-log.txt 2>&1 Initialisation d'un terminal IRIS Pour initialiser un terminal IRIS, procédez comme suit: Dans un terminal powershell/cmd lancez: docker exec -it fhir-integratedml-example_iris_1 bash Dans linux shell, créez une session IRIS: irissession iris Démonstration Afin de démontrer le concept du projet, deux modèles ont été configurés : Un modèle de prédiction de non-présentation à une consultation Un modèle de prédiction de l'insuffisance cardiaque Tout d'abord, des ensembles de données de formation ont été utilisés pour générer des ressources FHIR synthétiques. Ces jeux de données contenaient des informations sur les patients, les pathologies, les observations, les rendez-vous et les visites envoyés aux patients, représentés par différentes ressources FHIR. Cette étape émule une véritable base de données FHIR, dans laquelle les prédictions de non-présentation et d'insuffisance cardiaque pourraient être appliquées. Lorsque la base de données FHIR est prête à être utilisée, les données doivent être transformées en combinant les ressources FHIR qui sont pertinentes pour le problème, dans des tableaux uniques. Cette combinaison FHIR est réalisée par les transformations DTL telles que NoShowDTL et HeartFailureDTL : Comme les transformations DTL peuvent être exportées/importées, il est possible de partager les modèles ML appliqués aux données FHIR. Ces transformations pourraient également être étendues par d'autres équipes si nécessaire. Après avoir appliqué les transformations DTL, les ressources FHIR sont mappées en lignes simples, créant ainsi des tableaux qui pourraient être utilisés pour former des modèles ML pour les prédictions de non-présentation et d'insuffisance cardiaque. Pour former et tester des modèles à l'aide d'IntegratedML, utilisez les instructions SQL suivantes. Ils sont exécutés lors de l'installation, mais vous pouvez les réexécuter et essayer IntegratedML par vous-même. Modèle de non-présentation -- créer le jeu de données de formation CREATE OR REPLACE VIEW PackageSample.NoShowMLRowTraining AS SELECT * FROM PackageSample.NoShowMLRow WHERE ID < 1800 -- créer le jeu de données de test CREATE OR REPLACE VIEW PackageSample.NoShowMLRowTest AS SELECT * FROM PackageSample.NoShowMLRow WHERE ID >= 1800 -- éviter les erreurs dans la commande CREATE MODEL ; ignorer toute erreur ici DROP MODEL NoShowModel -- un modèle IntegratedML pour la colonne de prédiction Noshow (non-présentation) est créé à partir d'autres modèles, en utilisant le jeu de données PackageSample.NoShowMLRowTraining pour l'étape de formation ; le paramètre Graine "seed" ici est destiné à assurer la reproductibilité des résultats. CREATE MODEL NoShowModel PREDICTING (Noshow) FROM PackageSample.NoShowMLRowTraining USING {"seed": 6} -- le modèle est formé, tel qu'il a été défini dans la commande de création d'un modèle "CRÉER UN MODÈLE" TRAIN MODEL NoShowModel -- des informations sur le modèle formé sont affichées, comme par exemple le modèle ML sélectionné par IntegratedML SELECT * FROM INFORMATION_SCHEMA.ML_TRAINED_MODELS -- la fonction de prédiction (PREDICT) est utilisée pour voir comment utiliser le modèle dans les instructions SQL SELECT top 10 PREDICT(NoShowModel) AS PredictedNoshow, Noshow AS ActualNoshow FROM PackageSample.NoShowMLRowTest -- une validation sur un ensemble de données de test est effectuée et les mesures de performance du modèle sont calculées VALIDATE MODEL NoShowModel FROM PackageSample.NoShowMLRowTest -- les mesures de performance sont affichées SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS Modèle d'insuffisance cardiaque -- créer le jeu de données de formation CREATE OR REPLACE VIEW PackageSample.HeartFailureMLRowTraining AS SELECT DEATHEVENT,age,anaemia,creatininephosphokinase,diabetes,ejectionfraction,highbloodpressure,platelets,serumcreatinine,serumsodium,sex,smoking,followuptime FROM PackageSample.HeartFailureMLRow WHERE ID < 200 -- créer le jeu de données de test CREATE OR REPLACE VIEW PackageSample.HeartFailureMLRowTest AS SELECT DEATHEVENT,age,anaemia,creatininephosphokinase,diabetes,ejectionfraction,highbloodpressure,platelets,serumcreatinine,serumsodium,sex,smoking,followuptime FROM PackageSample.HeartFailureMLRow WHERE ID >= 200 -- éviter les erreurs dans la commande CREATE MODEL ; ignorer toute erreur ici DROP MODEL HeartFailureModel -- des informations sur le modèle formé sont affichées, comme par exemple le modèle ML sélectionné par IntegratedML CREATE MODEL HeartFailureModel PREDICTING (DEATHEVENT) FROM PackageSample.HeartFailureMLRowTraining USING {"seed": 6} -- le modèle est formé, tel qu'il a été défini dans la commande de création d'un modèle "CRÉER UN MODÈLE" TRAIN MODEL HeartFailureModel -- des informations sur le modèle formé sont affichées, comme par exemple le modèle ML sélectionné par IntegratedML SELECT * FROM INFORMATION_SCHEMA.ML_TRAINED_MODELS -- la fonction de prédiction (PREDICT) est utilisée pour voir comment utiliser le modèle dans les instructions SQL SELECT top 10 PREDICT(HeartFailureModel) AS PredictedHeartFailure, DEATHEVENT AS ActualHeartFailure FROM PackageSample.HeartFailureMLRowTest -- une validation sur un ensemble de données de test est effectuée et les mesures de performance du modèle sont calculées VALIDATE MODEL HeartFailureModel FROM PackageSample.HeartFailureMLRowTest -- les mesures de performance sont affichées SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS La dernière instruction SQL peut vous indiquer les paramètres de performance de la classification : La même transformation pourrait être appliquée pour transformer les ressources FHIR provenant de systèmes externes, par le biais d'une API REST par exemple (voir le code) : Dépannage Si vous obtenez des erreurs lors des requêtes API, indiquant que le modèle n'existe pas, il est probable que quelque chose d'anormal se produise lors de la création du conteneur pour les modèles de formation. Essayez de réexécuter la méthode de formation. Ouvrez le terminal IRIS et exécutez: ZN "FHIRSERVER" Do ##class(PackageSample.Utils).TrainNoShowModel() Do ##class(PackageSample.Utils).TrainHeartFailureModel() Générique Ressources FHIR utilisées comme modèles: http://hl7.org/fhir/ Jeu de données pour la formation de modèle de non-présentation: IntegratedML template Jeu de données pour la formation de modèle d'insuffisance cardiaque: kaggle
Article
Guillaume Rongier · Déc 5, 2022

Comment utiliser l'adaptateur FTP pour produire et consommer des messages FTP

FTP ( Protocole de transfert de fichiers) est un protocole de réseau permettant de transmettre des fichiers sur des connexions TCP/IP dans un réseau (y compris l'Internet) configuré pour transférer des fichiers via ce protocole. Dans une transaction FTP, un expéditeur de fichiers est appelé hôte local. Un récepteur de fichiers impliqué dans le FTP est un hôte distant, et il s'agit généralement d'un serveur. Bien que de nombreux transferts de fichiers puissent être effectués à l'aide du protocole HTTP (Hypertext Transfer Protocol), le FTP est encore couramment utilisé pour transférer des fichiers en coulisse pour d'autres applications, comme les services bancaires. Par conséquent, nous pouvons dire que FTP est une excellente option pour promouvoir l'interopérabilité basée sur les fichiers entre des systèmes situés dans le même réseau local ou dans l'Internet (un serveur FTP est accessible via l'Internet). Cette interopérabilité peut être enrichie, traitée et orchestrée par le système d'interopérabilité IRIS Interoperability. Pour comprendre comment IRIS peut aider, il est crucial de comprendre la fonctionnalité de l'Architecture d'Interopérabilité IRIS pour FTP, centrée sur les adaptateurs FTP d'entrée et de sortie. ## Adaptateur FTP d'entrée Selon [la documentation InterSystems](https://docs.intersystems.com/iris20221/csp/docbook/DocBook.UI.Page.cls?KEY=EFTP_inbound#EFTP_inbound_default_behavior), l'EnsLib.FTP.InboundAdapter permet à InterSystems IRIS de recevoir des fichiers via le protocole FTP. L'adaptateur reçoit une entrée FTP à partir de l'emplacement configuré, la lit et l'envoie sous forme de flux au service métier associé. Le service métier créé et configuré par vos soins utilise ce flux et communique avec le reste de la production. La figure suivante montre le flux global des messages entrants (à l'exclusion des réponses) : En détail : 1. Chaque fois que l'adaptateur rencontre une entrée provenant de sa source de données configurée, il appelle la méthode interne ProcessInput() de la classe de service métier, en passant le flux comme argument d'entrée. 2. Celle-ci est exécutée par la méthode interne ProcessInput() de la classe de service métier. Cette méthode effectue des tâches de production de base liées à la gestion des informations internes d'une manière requise par tous les services métier. Il n'est pas nécessaire de personnaliser ou de remplacer la méthode héritée par votre classe de service métier. 3. La méthode ProcessInput() appelle ensuite votre méthode OnProcessInput() personnalisée, en transmettant l'objet Stream en tant qu'entrée. Maintenant, avec le Stream, il est possible de développer les règles requises par le métier pour les fichiers reçus. 4. Le message de réponse suit le même chemin mais en sens inverse. ## Adaptateur FTP de sortie Selon la documentation d'InterSystems (https://docs.intersystems.com/iris20221/csp/docbook/DocBook.UI.Page.cls?KEY=EFTP\_outbound#EFTP\_outbound\_default\_behavior), l'adaptateur FTP de sortie EnsLib.FTP.OutboundAdapter permet à la production d'envoyer des fichiers via le protocole FTP. Pour utiliser cet adaptateur, vous devez créer et configurer une opération métier spécialement conçue pour ce dernier. Cette opération métier recevra alors un message de la production, recherchera le type de message et exécutera la méthode appropriée. Cette méthode exécute généralement des règles pour générer des fichiers avec le contenu et le format requis par le métier à envoyer à une application. ## Scénarios d'utilisation FTP les plus courants pour l'interopérabilité IRIS Le tableau suivant résume les principaux scénarios d'utilisation de FTP pour l'interopérabilité d'IRIS : Type d'adaptateur FTP Scénario D'entrée Réception de lots de données métier dans des fichiers CSV, XML, TXT et JSON. De sortie Envoi des résultats du traitement des données métier par lots en CSV, XML, TXT et JSON à partir des données envoyées précédemment D'entrée Processus ETL (extraction, transformation et chargement) de gros fichiers comme parquet, ORC et avro pour les lacs de données Data Lakes et les dépôts de données Data Marts. De sortie Génération de fichiers parquet, ORC et avro à partir des bases de données IRIS pour les envoyer vers les lacs de données Data Lakes et les dépôts de données Data Marts externes. D'entrée Consommation asynchrone de messages et de fichiers provenant de systèmes externes De sortie Production de messages et de fichiers asynchrones vers des systèmes externes De sortie Envoi/publication des fichiers générés (rapports PDF, fichiers numérisés, images, etc.) vers les dossiers distants de certains organismes. ## Application modèle Le modèle IRIS-FTP (https://openexchange.intersystems.com/package/iris-ftp-sample) est un exemple d'utilisation de l'adaptateur FTP ( d'entrée et de sortie). ### Production Ce modèle a une production d'interopérabilité vers : Recevoir des données CSV à l'aide d'un adaptateur FTP d'entrée dans un service mètier LoadCSVFTPBusinessService ; envoyer son contenu à une opération mètier LoadCSVFTPBusinessOperation ; persister le contenu dans une classe persistante à l'aide de CSVgen. Effectuer un changement de données CDC (Change Data Capture) dans le tableau Paiement en utilisant un adaptateur SQL d'entrée dans une opération métier PaymentSQLBusinessService et envoyer les données du tableau (dans un fichier JSON) à une opération métier SendSQLDataToFTPBusinessOperation pour mettre le fichier JSON dans un serveur FTP. Vous pouvez voir cette production ici :   ### Installation du modèle Pour installer le modèle en utilisant ZPM, suivez les étapes suivantes : 1. Ouvrez l'espace de nommage IRIS Namespace avec l'interopérabilité activée. 2. Ouvrez le terminal et lancez un appel : USER>zpm "install iris-ftp-sample" Pour installer le modèle en utilisant Docker, il faut procéder comme suit : 1. Clone/git tire le repo dans n'importe quel répertoire local : $ clone git https://github.com/yurimarx/iris-ftp-sample.git 2. Ouvrez le terminal dans ce répertoire et exécuter : $ compilation de docker-compose 3. Lancez le conteneur IRIS avec votre projet : $ docker-compose up -d ### Lancement du modèle Pour exécuter le modèle, suivez les procédures suivantes : 1. Créez les informations d'identification pour accéder au serveur FTP (Allez vers Interopérabilité > Configurer > Informations d'identification) : * ID : FTPCredentials * Nom de l'utilisateur : irisuser * Mot de passe : sys Voici le résultat : 2. Ouvrez la production et lancez-la, comme indiqué dans l'image ci-dessous : ### Envoi d'un fichier à l'aide du client FTP et visualisation des données chargées dans un tableau SQL 1. Ouvrez un client FTP (j'utilise généralement Filezilla pour le faire) et mettez le fichier input/countries.csv dans le dossier FTP racine : * Hôte : localhost * Nom de l'utilisateur : irisuser * Mot de passe : sys Ouvrez un client FTP (j'utilise généralement Filezilla pour le faire) et mettez le fichier input/countries.csv dans le dossier FTP racine : 2. Accédez à System Explorer > SQL et tapez la sélection du pays, de la latitude, de la longitude et du nom **SELECT country, latitude, longitude, name FROM dc_irisftpsample.Country**. Consultez l'image ci-dessous pour voir à quoi devraient ressembler les données CSV chargées dans le tableau SQL : ### Insertion de données dans un tableau et un fichier avec le contenu inséré dans un fichier sur le serveur FTP 1. Accédez à System Explorer > SQL et tapez les valeurs de paiement (montant payeur, destinataire, date de transaction): **insert into dc\_irisftpsample.Payment(amount payer, receiver, transactiondate) values(100.0,'Yuri','Fabiana', CURRENT\_TIMESTAMP)**. Vous pouvez voir ici la représentation à l'écran du fichier JSON contenant les données insérées dans le serveur FTP :   ## En coulisses : le code source ### Fichiers et dossiers principaux Dans le dossier iris-ftp-sample nous trouvons les éléments suivants : Fichier Description Readme.md Informations sur le modèle et instructions d'installation/utilisation Module.xml Manifeste d'installation de ZPM Dockerfile Commandes Docker pour créer un conteneur IRIS d'InterSystems Docker-compose.yml Commandes pour créer une composition de 2 instances docker : 1 instance docker de serveur FTP et 1 instance docker d'InterSystems IRIS dans le même réseau Docker Input/countries.csv Fichier modèle à utiliser pour tester le chargement des données par FTP et csvgen avec une production FTP modèle. src/dc/irisftpsample Le dossier du code source dans le paquet dc.irisftpsample FTPSampleProduction.cls Le code source de la Production FTP (conteneur permettant d'exécuter des services et des opérations métier à l'aide d'adaptateurs FTP d'entrée et de sortie). LoadCSVFTPBusinessService.cls Classe ObjectScript qui utilise un adaptateur FTP d'entrée pour recevoir des fichiers CSV et envoyer leur contenu à l'opération LoadCSVFTPBusinessOperation.cls. LoadCSVFTPBusinessOperation.cls Reçoit le contenu d'un fichier CSV et charge ses données dans un tableau SQL en utilisant CSVgen SendSQLDataToFTPBusinessOperation.cls Reçoit un fichier JSON avec le contenu du tableau "Paiement" de PaymentSQLBusinessService et envoie ce fichier JSON au serveur FTP. Payment.cls Classe persistante ( tableau SQL "Paiement") pour les données de paiement persistantes à envoyer au serveur FTP. ### Cas d'utilisation modèle 1 : Recevoir un fichier CSV du serveur FTP et charger les données CSV dans un tableau SQL. Pour ce cas d'utilisation, nous avons la séquence suivante : ![Texto Descrição gerada automaticamente](https://lh3.googleusercontent.com/FcRG7hMyXgkhNfND9i0m44ihj1calT5B1rsd17e7DMC_1VSMYWa3d4hszNn3eZ9I7K41d9eWzq-oN5OkB0vG-PFNwAJ9Tx8zof9XqmN6GnSBTMTLFRA7hwXJCpMxV23OnEH4RwlUSj3kJ9aJpnWsMKwr2aoIo1AI1NIgWc91ZNG9Sr7HgWbbyb7BSV7frbpxkROAEA)   1. L'adaptateur FTP d'entrée lit le fichier CSV et le supprime du serveur FTP en utilisant ces paramètres : Le Chemin du fichier (File Path) est l'endroit où l'adaptateur FTP se connectera pour trouver les fichiers. La fonction de suppression du serveur (Delete From Server) permet de supprimer le fichier du serveur après sa lecture. File Spec est le modèle qui correspond aux fichiers à lire. L'intervalle d'appel (Call Interval) est l'intervalle du tirage (vérification des nouveaux fichiers). Le serveur FTP est le nom de l'IP du serveur dans le réseau ou l'Internet. Port FTP est le port à connecter Les informations d'identification constituent la référence aux informations d'identification pour la configuration des noms d'utilisateur et des mots de passe. ![Interface gráfica do usuário, Texto, Aplicativo Descrição gerada automaticamente](https://lh3.googleusercontent.com/qDiMmnrAB9IIhPea36V_DKQ4aiP37XlINt0eWA_MwGmzPNU3KXXqzTncwBuZRn5Xs7YVxINyV6kuCYBcjunxp7B83IWh1fWWvfof7AidV587F42zV2S-3OHtXNRggWNdnUQpzsq8XCKOsLjW3Epf8uHWgUmJVIuxVpIpNBPMQuXpA4P6MXT7gs3Ansh0-t5s9zXO_w) ![Interface gráfica do usuário, Aplicativo Descrição gerada automaticamente](https://lh3.googleusercontent.com/T8wuB1tmQWyWrRWQkJvcPLmVZgZO2aEuPHhRHHs7zZyaGVq7ZDRem55hS9fmY65PRETfPoAaPaoojjmjaAVZkx8EQl5MhzYEg0-c1sEw77vUuQN7LhfNLCaE3Ib4qq7wDtSLQGpnjOuj1Wq5zbgJA8ThTyXseQ7cmjLQs-XHx5ERWJPri5lkd6KPnk6FEkCBRfsbeQ) 2. Envoyez le flux d'un fichier CSV à la méthode OnProcessInput du service LoadCSVFTPBusinessService. 3. Envoyez le flux d'un fichier CSV dans un message Ens.StreamContainer dans l'opération LoadCSVFTPBusinessOperation. Class dc.irisftpsample.LoadCSVFTPBusinessService Extends Ens.BusinessService { Parameter ADAPTER = "EnsLib.FTP.InboundAdapter"; Method OnProcessInput(pInput As %Stream.Object, pOutput As %RegisteredObject) As %Status { Set tSC = $$$OK, tSource = pInput.Attributes("Filename"), tFileLocation = pInput.Attributes("FTPDir"), pInput=##class(Ens.StreamContainer).%New(pInput) Set tSC = ..SendRequestSync("LoadCSVFTPBusinessOperation", pInput, .pOutput, -1) Quit tSC } } 4. Envoyez le flux d'un fichier CSV dans un message Ens.StreamContainer à la méthode LoadCSVIntoTable. L'adaptateur utilise le mappage XData pour déterminer la méthode à appeler pour chaque type de message dans la classe dc.irisftpsample.LoadCSVFTPBusinessOperation : XData MessageMap { LoadCSVIntoTable } 5. Envoyez le flux comme un fichier CSV temporaire et appelez la méthode Generate pour placer le fichier CSV dans le tableau SQL dc.irisftpsample.Country. Class dc.irisftpsample.LoadCSVFTPBusinessOperation Extends Ens.BusinessOperation { Parameter ADAPTER = "Ens.OutboundAdapter"; Parameter INVOCATION = "Queue"; Property Header As %Boolean [ InitialExpression = 1 ]; Property Delimiter As %String [ InitialExpression = "," ]; Property Classname As %String(MAXLEN = ""); Property Prowtype As %String(MAXLEN = ""); Property GuessTypes As %Boolean [ InitialExpression = 1 ]; Property Append As %Boolean [ InitialExpression = 1 ]; Property UseLoadData As %Boolean [ InitialExpression = 1 ]; Parameter SETTINGS = "Header:Basic,Delimiter:Basic,Classname:Basic,Prowtype:Basic,GuessTypes:Basic,Append:Basic,UseLoadData:Basic"; Method LoadCSVIntoTable(pInput As Ens.StreamContainer, Output pOutput As Ens.StringResponse) As %Status { Set tSC = $$$OK Set pOutput = ##class(Ens.StringResponse).%New() Try { Set tSC = ##class(community.csvgen).StreamToFile(pInput.Stream, .pFile) Set tSC = ##class(community.csvgen).Generate(pFile, ..Delimiter, ..Classname, ..Prowtype, ..GuessTypes, ..Append, ..UseLoadData, ..Header) Set pOutput.StringValue = "OK" } Catch err { Set pOutput.StringValue = $System.Status.GetOneErrorText(tSC, 1) } Quit tSC } XData MessageMap { LoadCSVIntoTable } } * Remarque 1 : Le paramètre Adapter est utilisé pour choisir l'adaptateur à utiliser par la Business Operation. * Remarque 2 : les paramètres tels que Header, Delimiter, Classname, Prowtype, GuessTypes, Append et UseLoadData sont requis par csvgen et configurés par l'utilisateur de Production car ces paramètres sont référencés dans le paramètre SETTINGS de la section Basic : ![Interface gráfica do usuário, Aplicativo Descrição gerada automaticamente](https://lh5.googleusercontent.com/Y32dfVzlp-2hvFLlQe4L4RYSDnRbty-E4MTaxWSuspxwJ4CepuoF6keioXJ5dQ8Gm_Ez81WFqiRabbeClbCw3r9QhzXn71GhG9n2x3rvVOEdOKPO3Ew6Ioti7cm2kxBZymE51af2rsCFURd0Mqv_myPZRQ7TVOHqbkzJzZJBaAE1U66aq3s8q4zEQZ6LY9ZOaXqJfg) ![Uma imagem contendo Interface gráfica do usuário Descrição gerada automaticamente](https://lh6.googleusercontent.com/KHcXwkbSgz27RXDFluFXdC_WtfJVaess9CqK3NYQf67LxsNXRITAVuPJ_j54ezzH5yU3R9FMUjnXJTJ8ecQX_ImhXqR3bEEWIwiXyMKLn8IZoH9NB0-ubbV32hG2XHoUPw5Y4KKu2YHcYdtGlvPL_I-Ept5fIwAiBk6D_FtrY3RYOjTZ8LPgiws1EZGaX594qCiqKQ) Consultez les détails sur les informations requises par CSVgen ici : . ##   ### Cas d'utilisation modèle 2 : Application de la méthode CDC (Change Data Capture) sur le tableau des paiements en utilisant l'adaptateur SQL d'entrée et en envoyant les données du tableau à une opération métier pour les mettre sur le serveur FTP. Pour ce cas d'utilisation, nous avons la séquence suivante : ![Linha do tempo Descrição gerada automaticamente](https://lh6.googleusercontent.com/TR5MsEC_PHWjsrPBugqruYOtB4XvMkowd4M1z_dIfLrQ00gg2__Co3A65w_MgleDADV6obyzY3hiHsFQtNtwlJXCDfX0gu7WW3s1xygOo5hmlWkVJPdGYmS3ucBrdHH8RF2wL6rdgLw0A7sfCjoIerpR8Rxnf4733nEHOR6f9n3QeW-5jzOB4SgFcoTrBFoQEI9AUw) 1. IRIS effectue une opération CDC dans le tableau "Paiement" et génère un fichier JSON avec le contenu du tableau, en supprimant les données lues. L'adaptateur utilise ces paramètres pour déterminer le tableau et les données à lire : L'intervalle d'appel (Call Interval) : les nouvelles données sont vérifiées en quelques secondes DSN : l'espace de nom si le tableau existe. Target Config Names : le composant de la production qui recevra les données (dans ce cas, SendSQLDataToFTPBusinessOperation). Requête : la commande de sélection pour obtenir les données à lire (dans ce cas, il s'agit de données relative au paiement (montant payeur, destinataire,date de transaction), à savoir SELECT ID, amount, payer, receiver, transactiondate FROM dc_irisftpsample.Payment). Requête de suppression : la commande de suppression utilisée pour supprimer les données lues, si nécessaire (dans ce cas DELETE FROM dc_irisftpsample.Payment).  Nom du champ clé : la clé primaire du tableau. ![Interface gráfica do usuário, Aplicativo Descrição gerada automaticamente](https://lh3.googleusercontent.com/gH_TIP7P_SchyAtMoAm67XfT3UBsTgzrEUOhhmxsAIF_PN6QB4dnz4KMRwCsXy3__CkAThm6MwBFs6hBI51RHv6kFsFwYvNMsQ9ykHVvqqXHZE2O_wRyaG0rlTT4KWpI9tJOT17sRVg9GUTuxZ-L-UkWRncJokK-w4q3e91bW-V0F5i3kJlqFvv0jiP806dh6PDUeA) ![Interface gráfica do usuário, Texto, Aplicativo Descrição gerada automaticamente](https://lh5.googleusercontent.com/ysv8VIEe-a5pI9kBF-1i7DOu0PnYuRVHex25swwjkYN2MWcz0iIh1wSgMnIDOkhJR0QxMPpRawLYwhcjvCenFttZHPHNzzEaLpzOYpzPgK26Xrkv71HO5htmLUc83JHS-EkQnOVGKLvGq2B3XukHSjU-2xW8zQ_4o2e3q4pBo9j-PJhY4WWp5tApZ9WHKUxbwU_fQA) 2. Envoyez le flux de données au format JSON au PaymentSQLBusinessService. 3. Envoyer le flux du fichier JSON dans un message Ens.StreamContainer dans l'opération métier SendSQLDataToFTPBusinessOperation. 4. Envoyez le flux du fichier JSON à l'adaptateur FTP de sortie : Class dc.irisftpsample.SendSQLDataToFTPBusinessOperation Extends Ens.BusinessOperation { Parameter ADAPTER = "EnsLib.FTP.OutboundAdapter"; Property Filename As %String(MAXLEN = 1000); Parameter SETTINGS As %String = "Filename"; Method OnMessage(pRequest As Ens.StreamContainer, Output pResponse As %Persistent) As %Status { Set tFilename= ..Adapter.CreateTimestamp(##class(%File).GetFilename("/tmp/sqltojson"),..Filename) Quit ..Adapter.PutStream(tFilename, pRequest.Stream) } } * Remarque 1 : Le paramètre ADAPTER définit que l'opération métier utilisera l'adaptateur FTP de sortie. * Remarque 2: Le paramètre Filename définit le nom du fichier qui se trouve sur le serveur FTP. 5. Placez le fichier JSON dans le dossier FTP racine avec le nom SQLTOJSON\_YYYY-MM-DD\_HH.mm.ss.zzz.json en utilisant les paramètres suivants : Le serveur FTP est le nom de l'IP du serveur dans le réseau ou l'Internet. Port FTP est le port à connecter Les informations d'identification constituent la référence aux informations d'identification pour la configuration du nom d'utilisateur et du mot de passe Le chemin du fichier est l'endroit où l'adaptateur FTP se connectera pour trouver les fichiers Nom du fichier est le modèle du nom du fichier sur le serveur FTP Protocole : FTP ou SFTP (FTP avec certificat SSL)  Utiliser PASV : connexions passives avec le serveur FTP ![Interface gráfica do usuário, Aplicativo Descrição gerada automaticamente](https://lh3.googleusercontent.com/eFlzLrV0uN5RC_fIA3PrmlrrJlDy_2o_Ck49hQqjRwZUF-Bzw2ySo7vRmfB9HJI-aEH16ZkQIcN0GlTK19JpIGLcLCQoJ_psufLKtUsv3R3_D-x9CG8mTGEWBSYOv-2iYaVjFejLFMPO-UnOISqw9jfIuhY8NmiUME-XWunTxsgOJ9nJfsAOL39hS7f1pnPb3TRdFg) ![Interface gráfica do usuário, Texto, Aplicativo Descrição gerada automaticamente](https://lh5.googleusercontent.com/djKJbBZGctZITIEvmSk8DCZ3fgWv_bBJfIcS4ZeaKE9wrOaRf6OVE3x-2dYOEGM69Q7Db7kPtVGNh6FOC1AIPgaqfbfh5fROaJXeT2xnvvBLuVt2U9swnlSNl78vUJcQ6GWim7HvZADkt_lPfBa-7BseCrS8LDiSXvP593OhddGGNEQ5owvVX3Og3TFbyKoW6Oa88g) ## Pour en savoir plus Documentation officielle pour les adaptateurs FTP : https://docs.intersystems.com/iris20221/csp/docbook/DocBook.UI.Page.cls?KEY=EFTP Apprentissage officiel en ligne sur la production, les services métiers et les opérations : https://learning.intersystems.com/course/view.php?id=1437 Dépôt Git pour CSVGEN : https://github.com/evshvarov/csvgen À propos de FTP : https://www.techtarget.com/searchnetworking/definition/File-Transfer-Protocol-FTP  
Article
Guillaume Rongier · Fév 17, 2023

Déploiement de modèles ML/DL dans une pile de services de démonstration d'IA consolidée

**Keywords**:  IRIS, IntegratedML, Flask, FastAPI, Tensorflow servant, HAProxy, Docker, Covid-19 ## Objective: Nous avons abordé quelques démonstrations rapides d'apprentissage profond et d'apprentissage automatique au cours des derniers mois, notamment un simple classificateur d'images radiographiques Covid-19 et un classificateur de résultats de laboratoire Covid-19 pour les admissions possibles en soins intensifs. Nous avons également évoqué une implémentation de démonstration IntegratedML du classificateur ICU. Alors que la randonnée de la "science des données" se poursuit, le moment est peut-être venu d'essayer de déployer des services d'IA du point de vue de "l'ingénierie des données" - pourrions-nous regrouper tout ce que nous avons abordé jusqu'à présent dans un ensemble d'API de services ? Quels sont les outils, les composants et l'infrastructure communs que nous pourrions exploiter pour réaliser une telle pile de services dans son approche la plus simple possible ?   ## Cadre ### **Dans le cadre de ce qui suit:** Pour commencer, nous pouvons simplement utiliser docker-compose pour déployer les composants dockerisés suivants dans un serveur AWS Ubuntu * **HAProxy** - équilibreur de charge * **Gunicorn** vs. **Univorn ** - passerelle web** **serveurs * **Flask** vs. **FastAPI** - serveurs d'application pour l'interface utilisateur des applications Web, les définitions des API de service, la génération des cartes thermiques, etc. * **Tensorflow Model Serving** vs. **Tensorflow-GPU Model Serving** - serveurs d'applications pour les classifications d'images, etc. * IRIS **IntegratedML** - AutoML consolidé App+DB avec interface SQL. * Python3 dans **Jupyter Notebook** pour émuler un client pour le **benchmarking**. * Docker et **docker-compose**. * **AWS Ubuntu** 16.04 avec un GPU Tesla T4   Remarque:   Tensorflow Serving avec GPU n'est utilisé qu'à des fins de démonstration - vous pouvez simplement désactiver l'image liée au GPU (dans un fichier docker) et la configuration (dans le fichier docker-compose.yml). ### **Out of scope** (Hors de portée) ou sur la prochaine liste de souhaits : * **Les serveurs web **Nginx** ou **Apache** etc. sont omis dans la démo pour le moment. * **RabbitMQ** et Redis - courtier de file d'attente pour une messagerie fiable qui peut être remplacé par IRIS ou Ensemble. **IAM** ([Intersystems API Manger](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AIAM)) ou **Kong** est sur la liste des souhaits. * **SAM**(Intersystems [System Alert & Monitoring](https://docs.intersystems.com/sam/csp/docbook/DocBook.UI.Page.cls?KEY=ASAM)) * **ICM** ([Intersystems Cloud Manager](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=PAGE_DEPLOYMENT_ICM)) avec l'opérateur **Kubernetes** - toujours l'un de mes préférés depuis sa naissance * **FHIR** (serveur FHIR R4 basé sur Intesystems IRIS et FHIR Sandbox pour les applications SMART sur FHIR) * Outils de développement **CI/CD** ou **Github Actions**. De toute façon, un "ingénieur en apprentissage automatique" ("Machine Learning Engineer") mettra inévitablement la main sur ces composants pour fournir des environnements de production tout au long des cycles de vie des services. Nous pourrons en savoir plus au fil du temps.   ## Dépôt Github Le code source complet se trouve à l'adresse suivante : Le [référentiel integratedML-demo-template](https://github.com/intersystems-community/integratedml-demo-template) est également réutilisé avec le nouveau référentiel.    ## Modèle de déploiement Le schéma de déploiement logique de ce cadre de test "Démonstration de l'IA dans les Dockers" est présenté ci-dessous. Pour la démonstration, j'ai délibérément créé deux piles distinctes pour la classification de l'apprentissage profond et le rendu web, puis j'ai utilisé un HAProxy comme équilibreur de charge pour distribuer les requêtes API entrantes entre ces deux piles de manière indépendante. * Guniorn + Flask + Tensorflow Serving * Univcorn + FaskAPI + Tensorflow Serving GPU IRIS avec IntegratedML est utilisé pour les échantillons de démonstration d'apprentissage automatique, comme dans l'article précédent de prédiction de l'ICU. J'ai omis certains composants communs dans la démo actuelle qui seraient nécessaires ou envisagés pour les services de production : * Serveurs Web : Nginx ou Apache, etc. Ils seront nécessaires entre HAProxy et Gunicorn/Uvicorn, pour une gestion correcte des sessions HTTP, c'est-à-dire pour éviter les attaques DoS, etc. * Gestionnaire de file d'attente et bases de données : RabbitMQ et/ou Redis, etc., entre Flask/FastAPI et le serveur backend, pour un service asynchrone fiable et la persistance des données/configurations, etc. * Passerelle API : IAM ou Kong clusters, entre l'équilibreur de charge HAProxy et le serveur web pour la gestion des API sans créer de point singulier de défaillance. * Surveillance et alerte : SAM serait bien. * Provisionnement pour CI/CD devops : ICM avec K8s serait nécessaire pour le déploiement et la gestion neutre en nuage, et pour CI/CD avec d'autres outils devops communs. En fait,  IRIS lui-même peut certainement être utilisé comme gestionnaire de file d'attente de niveau entreprise ainsi que comme base de données performante pour une messagerie fiable. Dans l'analyse des modèles, il apparaît qu'IRIS peut remplacer les courtiers de file d'attente et les bases de données RabbitMQ/Redis/MongoDB, etc., et qu'il serait donc mieux consolidé avec une latence bien moindre et de meilleures performances globales. Et plus encore, IRIS Web Gateway (anciennement CSP Gateway) peut certainement être positionné à la place de Gunicorn ou Unicorn, etc, n'est-ce pas ?     ## Topologie de l'environnement Il existe quelques options courantes pour mettre en œuvre le modèle logique ci-dessus dans tous les composants Docker. Les plus courantes sont les suivantes :   * docker-compose * docker swarm etc * Kubernetes etc  * ICM avec K8s Operation Cette démonstration commence avec "docker-compose" pour un PoC fonctionnel et un certain benchmarking. Nous aimerions certainement utiliser K8s et peut-être aussi ICM au fil du temps.  Comme décrit dans son fichier [docker-compose.yml](https://github.com/zhongli1990/covid-ai-demo-deployment/blob/master/docker-compose.yml), une implémentation physique de sa topologie d'environnement sur un serveur AWS Ubuntu ressemblerait à ceci :   Le diagramme ci-dessus montre comment ces **ports de service** de toutes les instances Docker sont mappés et exposés directement sur le serveur Ubuntu à des fins de démonstration. En production, la sécurité devrait être renforcée. Et pour les besoins de la démonstration, tous les conteneurs sont connectés au même réseau Docker, alors qu'en production, il serait séparé en routable externe et non-routable interne.   ## Composants "Dockerisés"  Le tableau ci-dessous montre comment les **volumes de stockage** de la machine hôte sont montés sur chaque instance de conteneur comme spécifié dans ce fichier [docker-compose.yml](https://github.com/zhongli1990/covid-ai-demo-deployment/blob/master/docker-compose.yml) :  ubuntu@ip-172-31-35-104:/zhong/flask-xray$ tree ./ -L 2 ./ ├── covid19 (Les conteneurs Flask+Gunicorn et Tensorflow Serving seront montés ici) │ ├── app.py (Flask main app: Les interfaces de l'application web et du service API sont définies et mises en œuvre ici) │ ├── covid19_models (Les modèles Tensorflow sont publiés et versionnés ici pour la classification des images Le conteneur Tensorflow Serving avec CPU) │ ├── Dockerfile (Le serveur Flask avec Gunicorn: CMD ["gunicorn", "app:app", "--bind", "0.0.0.0:5000", "--workers", "4", "--threads", "2"]) │ ├── models (Modèles au format .h5 pour l'application Flask et démonstration API de la génération de heatmaps par grad-cam sur des radiographies.) │ ├── __pycache__ │ ├── README.md │ ├── requirements.txt (Paquets Python nécessaires pour les applications complètes de Flask+Gunicorn) │ ├── scripts │ ├── static (Fichiers statiques Web) │ ├── templates (Modèles de rendu Web) │ ├── tensorflow_serving (Fichier de configuration pour le service de tensorflow) │ └── test_images ├── covid-fastapi (Les conteneurs FastAPI+Uvicorn et Tensorflow Serving avec GPU seront définis ici) │ ├── covid19_models (Les modèles Tensorflow au service des GPU sont publiés et versionnés ici pour la classification des images) │ ├── Dockerfile (Le serveur Uvicorn+FastAPI sera lancé ici: CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4" ]) │ ├── main.py (FastAPI app: les interfaces de l'application web et du service API sont définies et mises en œuvre ici) │ ├── models (Modèles au format .h5 pour l'application FastAPI et démonstration API de la génération de heatmaps par grad-cam sur des radiographies) │ ├── __pycache__ │ ├── README.md │ ├── requirements.txt │ ├── scripts │ ├── static │ ├── templates │ ├── tensorflow_serving │ └── test_images ├── docker-compose.yml (Full stack Docker definition file. La version 2.3 est utilisée pour tenir compte du GPU Docker "nvidia runtime", sinon la version 3.x peut être utilisée) ├── haproxy (Le service docker HAProxy est défini ici. Note : une session collante peut être définie pour le backend LB. ) │ ├── Dockerfile │ └── haproxy.cfg └── notebooks (Le service conteneur Jupyter Notebook avec Tensorflow 2.2 et Tensorboard etc) ├── Dockerfile ├── notebooks (Exemples de fichiers notebook pour émuler des applications API Client externes pour les tests fonctionnels et les tests de référence API en Python sur l'équilibreur de charge, etc) └── requirements.txt Remarque: Le [docker-compose.yml](https://github.com/zhongli1990/covid-ai-demo-deployment/blob/master/docker-compose.yml) ci-dessus est destiné à la démonstration d'apprentissage profond de Convid X-Rays. Il est utilisé avec le [docker-compose.yml](https://github.com/intersystems-community/integratedml-demo-template/blob/master/docker-compose.yml) d'un autre [integratedML-demo-template](https://github.com/intersystems-community/integratedml-demo-template) pour former la pile de services complète, comme indiqué dans la topologie de l'environnement.     ## Démarrage des services  Un simple **docker-compose up -d** permettrait de démarrer tous les services de conteneurs : ubuntu@ip-172-31-35-104:~$ docker psID DE CONTENEUR        IMAGE                                 COMMANDE                  STATUT CRÉÉ                PORTS                                                                              NOMS31b682b6961d        iris-aa-server:2020.3AA               "/iris-main"             Il y a 7 semaines         Jusqu'à 2 jours (en bonne santé)   2188/tcp, 53773/tcp, 54773/tcp, 0.0.0.0:8091->51773/tcp, 0.0.0.0:8092->52773/tcp   iml-template-master_irisimlsvr_16a0f22ad3ffc        haproxy:0.0.1                         "/docker-entrypoint.…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8088->8088/tcp                                                             flask-xray_lb_171b5163d8960        ai-service-fastapi:0.2.0              "uvicorn main:app --…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8056->8000/tcp                                                             flask-xray_fastapi_1400e1d6c0f69        tensorflow/serving:latest-gpu         "/usr/bin/tf_serving…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8520->8500/tcp, 0.0.0.0:8521->8501/tcp                                     flask-xray_tf2svg2_1eaac88e9b1a7        ai-service-flask:0.1.0                "gunicorn app:app --…"   Шl y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8051->5000/tcp                                                             flask-xray_flask_1e07ccd30a32b        tensorflow/serving                    "/usr/bin/tf_serving…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8510->8500/tcp, 0.0.0.0:8511->8501/tcp                                     flask-xray_tf2svg1_1390dc13023f2        tf2-jupyter:0.1.0                     "/bin/sh -c '/bin/ba…"   Il y a 8 semaines         Jusqu'à 2 jours             0.0.0.0:8506->6006/tcp, 0.0.0.0:8586->8888/tcp                                     flask-xray_tf2jpt_188e8709404ac        tf2-jupyter-jdbc:1.0.0-iml-template   "/bin/sh -c '/bin/ba…"   Il y a 2 $ois         Jusqu'à 2 jours             0.0.0.0:6026->6006/tcp, 0.0.0.0:8896->8888/tcp                                     iml-template-master_tf2jupyter_1 **docker-compose up --scale fastapi=2 --scale flask=2 -d**   par exemple, sera mis à l'échelle horizontalement jusqu'à 2 conteneurs Gunicorn+Flask et 2 conteneurs Univcorn+FastAPI : ubuntu@ip-172-31-35-104:/zhong/flask-xray$ docker psID DE CONTENEUR        IMAGE                                 COMMANDE                  STATUT CRÉÉ                PORTS                                                                              NOMSdbee3c20ea95        ai-service-fastapi:0.2.0              "uvicorn main:app --…"   Il y a 4 minutes Jusqu'à 4 minutes          0.0.0.0:8057->8000/tcp                                                             flask-xray_fastapi_295bcd8535aa6        ai-service-flask:0.1.0                "gunicorn app:app --…"   Il y a 4 minutes Jusqu'à 4 minutes          0.0.0.0:8052->5000/tcp                                                             flask-xray_flask_2 ... ... L'exécution d'un autre "docker-compose up -d" dans le répertoire de travail de "integrtedML-demo-template" a fait apparaître le conteneur irisimlsvr et tf2jupyter dans la liste ci-dessus.   ## Tests ### **1. Application web de démonstration de l'IA avec une interface utilisateur simple** Après avoir démarré les services docker ci-dessus, nous pouvons visiter une application web de démonstration pour [X-Ray Covid-19 lung detection](https://community.intersystems.com/post/run-some-covid-19-lung-x-ray-classification-and-ct-detection-demos) hébergée dans une instance AWS EC2 à une adresse temporaire à Voici ci-dessous quelques écrans capturés depuis mon mobile. L'interface utilisateur de démonstration est très simple : en gros, je clique sur "Choose File" puis sur le bouton "Submit" pour télécharger [une image radiographique](https://github.com/zhongli1990/Covid19-X-Rays/tree/master/all/test), puis l'application affiche un rapport de classification. S'il s'agit d'une radiographie Covid-19, une [carte thermique sera affichée] (https://community.intersystems.com/post/explainability-and-visibility-covid-19-x-ray-classifiers-deep-learning) pour reproduire la zone de lésion "détectée" par DL ; sinon, le rapport de classification ne montrera que l'image radiographique téléchargée.          L'application web est une page serveur Python dont la logique est principalement codée dans le fichier [main.py de FastAPI](https://github.com/zhongli1990/covid-ai-demo-deployment/blob/master/covid-fastapi/main.py), ainsi que dans le fichier [app.py de Flask](https://github.com/zhongli1990/covid-ai-demo-deployment/blob/master/covid19/app.py). Quand j'aurai un peu plus de temps libre, je pourrais documenter en détail les différences de codage et de convention entre Flask et FastAPI. En fait, j'espère pouvoir faire un hébergement de démonstration Flask vs FastAPI vs IRIS pour l'IA.    ### **2. Test des API de démonstration**       FastAPI (exposé au port 8056) a intégré des documents Swagger API, comme indiqué ci-dessous. C'est très pratique. Tout ce que j'ai à faire est d'utiliser "/docs" dans son URL, par exemple :  ![](/sites/default/files/inline/images/images/image(875).png) J'ai intégré quelques paramètres (tels que /hello et /items) et quelques interfaces API de démonstration (telles que /healthcheck, /predict, et predict/heatmap).   **Testons rapidement ces API**, en exécutant quelques lignes Python (en tant qu'émulateur d'application client API) dans l'un des [fichiers d'échantillons de Jupyter Notebook que j'ai créés] (https://github.com/zhongli1990/covid-ai-demo-deployment/tree/master/notebooks/notebooks) pour ce service de démonstration de l'IA.   Ci-dessous, j'exécute ce fichier à titre d'exemple :  Tout d'abord pour tester que le backend TF-Serving (port 8511) et TF-Serving-GPU (port 8521) sont en place et fonctionnent :  !curl http://172.17.0.1:8511/v1/models/covid19 # servant tensorflow !curl http://172.17.0.1:8521/v1/models/covid19 # servant tensorflow-gpu { "model_version_status": [ { "version": "2", "state": "AVAILABLE", "status": { "error_code": "OK", "error_message": "" } } ] } { "model_version_status": [ { "version": "2", "state": "AVAILABLE", "status": { "error_code": "OK", "error_message": "" } } ] }   Ensuite, vérifiez que les services API suivants sont en place et fonctionnent : Gunicorn+Flask+TF-Serving Unicorn+FastAPI+TF-Serving-GPU Equilibreur de charge HAProxy en face des services gênants ci-dessus r = requests.get('http://172.17.0.1:8051/covid19/api/v1/healthcheck') # tf servant le docker avec le cpu print(r.status_code, r.text) r = requests.get('http://172.17.0.1:8056/covid19/api/v1/healthcheck') # tf-servant le docker avec le gpu print(r.status_code, r.text) r = requests.get('http://172.17.0.1:8088/covid19/api/v1/healthcheck') # tf-servant le docker avec le HAproxy print(r.status_code, r.text) Et les résultats attendus seraient : 200 L'API du détecteur Covid19 est en ligne ! 200 "L'API du détecteur Covid19 est en ligne !\n\n" 200 "L'API du détecteur Covid19 est en ligne !\n\n"   Tester une interface API fonctionnelle, telle que **/predict/heatmap ** pour renvoyer le résultat de la classification et de la heatmap d'une image radiographique d'entrée. L'image entrante est codée en based64 avant d'être envoyée via HTTP POST conformément aux définitions de l'API : %%time # Importation de la bibliothèque des requêtes import argparse import base64 import requests # définition d'un point d'entrée ap pour api API_ENDPOINT = "http://172.17.0.1:8051/covid19/api/v1/predict/heatmap" image_path = './Covid_M/all/test/covid/nejmoa2001191_f3-PA.jpeg' #image_path = './Covid_M/all/test/normal/NORMAL2-IM-1400-0001.jpeg' #image_path = './Covid_M/all/test/pneumonia_bac/person1940_bacteria_4859.jpeg' b64_image = "" # Encoding the JPG,PNG,etc. image to base64 format with open(image_path, "rb") as imageFile: b64_image = base64.b64encode(imageFile.read()) # données à envoyer à l'api data = {'b64': b64_image} # envoi d'une requête post et enregistrement de la réponse en tant qu'objet réponse r = requests.post(url=API_ENDPOINT, data=data) print(r.status_code, r.text) # extraction de la réponse print("{}".format(r.text)) Toutes ces [images de test ont également été téléchargées sur GitHub] (https://github.com/zhongli1990/Covid19-X-Rays/tree/master/all/test). Le résultat du code ci-dessus sera comme ça: 200 {"Input_Image":"http://localhost:8051/static/source/0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png","Output_Heatmap":"http://localhost:8051/static/result/Covid19_98_0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png.png","X-Ray_Classfication_Raw_Result":[[0.805902302,0.15601939,0.038078323]],"X-Ray_Classification_Covid19_Probability":0.98,"X-Ray_Classification_Result":"Covid-19 POSITIVE","model_name":"Customised Incpetion V3"} {"Input_Image":"http://localhost:8051/static/source/0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png","Output_Heatmap":"http://localhost:8051/static/result/Covid19_98_0198f0ae-85a0-470b-bc31-dc1918c15b9620200906-170443.png.png","X-Ray_Classfication_Raw_Result":[[0.805902302,0.15601939,0.038078323]],"X-Ray_Classification_Covid19_Probability":0.98,"X-Ray_Classification_Result":"Covid-19 POSITIVE","model_name":"Customised Incpetion V3"} CPU times: user 16 ms, sys: 0 ns, total: 16 ms Wall time: 946 ms   ### **3. Les applications de démonstration de services pour les tests benchmarkés** Nous avons mis en place une instance d'équilibreur de charge HAProxy. Nous avons également démarré un service Flask avec 4 travailleurs, et un service FastAPI avec 4 travailleurs également. Pourquoi ne pas créer 8x processus Pyhon directement dans le fichier Notebook, pour émuler 8x clients API simultanés envoyant des requêtes dans les API du service de démonstration, pour voir ce qui se passe ?  #from concurrent.futures import ThreadPoolExecutor as PoolExecutor from concurrent.futures import ProcessPoolExecutor as PoolExecutor import http.client import socket import time start = time.time() #laodbalancer: API_ENDPOINT_LB = "http://172.17.0.1:8088/covid19/api/v1/predict/heatmap" API_ENDPOINT_FLASK = "http://172.17.0.1:8052/covid19/api/v1/predict/heatmap" API_ENDPOINT_FastAPI = "http://172.17.0.1:8057/covid19/api/v1/predict/heatmap" def get_it(url): try: # boucle sur les images for imagePathTest in imagePathsTest: b64_image = "" with open(imagePathTest, "rb") as imageFile: b64_image = base64.b64encode(imageFile.read()) data = {'b64': b64_image} r = requests.post(url, data=data) #print(imagePathTest, r.status_code, r.text) return r except socket.timeout: # dans un scénario du monde réel, vous feriez probablement quelque chose si le # socket passe en timeout pass urls = [API_ENDPOINT_LB, API_ENDPOINT_LB, API_ENDPOINT_LB, API_ENDPOINT_LB, API_ENDPOINT_LB, API_ENDPOINT_LB, API_ENDPOINT_LB, API_ENDPOINT_LB] with PoolExecutor(max_workers=16) as executor: for _ in executor.map(get_it, urls): pass print("--- %s seconds ---" % (time.time() - start)) Il a donc fallu 74 secondes pour traiter 8x27 = 216 images de test. Cette pile de démonstration à charge équilibrée était capable de traiter 3 images par seconde (en renvoyant les résultats de la classification et de la heatmap aux clients) : --- 74.37691688537598 seconds --- À partir de la commande Top de la session Putty, nous pouvons voir que 8x processus de serveur (4x gunicorn + 4 unicorn/python) ont commencé à monter en puissance dès que les scripts de référence ci-dessus ont commencé à être exécutés.   ## Suivant Cet article n'est qu'un point de départ pour mettre en place une pile de déploiement "All-in-Docker AI demo" comme cadre de test. J'espère ensuite ajouter d'autres interfaces de démonstration API, telles que l'interface de prédiction des soins intensifs Covid-19, idéalement conforme à la norme FHIR R4, etc. Cela pourrait également être un banc d'essai pour explorer une intégration plus étroite avec les capacités ML hébergées par IRIS. Au fil du temps, il peut être utilisé comme un cadre de test (et un cadre assez simple) pour intercepter de plus en plus de modèles ML ou DL spécialisés au fur et à mesure que nous avançons sur divers aspects de l'IA, notamment l'imagerie médicale, la santé de la population ou la prédiction personnalisée, le traitement automatique des langues, etc. J'ai également dressé une liste de souhaits à la toute fin de [l'article précédent (dans sa section "Next" (Suivant))](https://community.intersystems.com/post/run-some-covid-19-icu-predictions-ml-vs-integratedml-part-ii).   
Article
Guillaume Rongier · Mars 29, 2022

Les globales sont des épées magiques pour la gestion des données (partie 1)

![](/sites/default/files/inline/images/eskalibur.jpg) Les globales, ces épées magiques destinées à stocker des données, existent depuis un certain temps, mais peu de gens savent les utiliser efficacement ou connaissent cette super-arme. Si vous utilisez les globales pour des tâches où ils sont vraiment utiles, les résultats peuvent être étonnants, que ce soit en termes d'amélioration des performances ou de simplification spectaculaire de la solution globale ([1](http://www.odbms.org/blog/2013/01/the-gaia-mission-one-year-later-interview-with-william-omullane/), [2](http://www.intersystems.com/ru/our-products/cache/tech-guide/chapter-1/)). Les globales offrent une façon particulière de stocker et de traiter les données, qui est complètement différente des tableaux SQL. Ils ont été introduits en 1966 dans le langage de programmation [M(UMPS)](https://en.wikipedia.org/wiki/MUMPS), qui était initialement utilisé dans les bases de données médicales. Il est toujours [utilisé de la même manière](https://en.wikipedia.org/wiki/VistA), mais a également été adopté par d'autres secteurs où la fiabilité et les hautes performances sont des priorités absolues : finance, commerce, etc. Plus tard, M(UMPS) a évolué vers [Caché ObjectScript](http://www.intersystems.com/our-products/cache/tech-guide/chapter-3/#cache-objectscript) (COS). COS a été développé par InterSystems comme un [superset de M](http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCOS_intro#GCOS_iso_m). Le langage original est toujours accepté par la communauté des développeurs et existe dans quelques implémentations. Il y a plusieurs signes d'activité sur le web : [MUMPS Google group](https://groups.google.com/d/forum/comp.lang.mumps), [Mumps User's group](http://mumps.org/)), [effective ISO Standard](http://71.174.62.16/Demo/AnnoStd), etc. Les DBMS (systèmes de base de données) globales modernes prennent en charge les transactions, la journalisation, la réplication et le partitionnement. Cela signifie qu'ils peuvent être utilisés pour construire des systèmes distribués modernes, fiables et rapides. Les globales ne vous limitent pas aux frontières du modèle relationnel. Ils vous donnent la liberté de créer des structures de données optimisées pour des tâches particulières. Pour de nombreuses applications, l'utilisation raisonnable des globales peut être une véritable solution miracle offrant des vitesses dont les développeurs d'applications relationnelles classiques ne peuvent que rêver. Les globales, en tant que méthode de stockage des données, peuvent être utilisés dans de nombreux langages de programmation modernes, tant de haut niveau que de bas niveau. Par conséquent, cet article se concentrera spécifiquement sur les globales et non sur le langage dont ils sont issus. ## 2. Comment fonctionnent les globales Commençons par comprendre comment fonctionnent les globales et quels sont leurs avantages. Les globales peuvent être vus sous différents angles. Dans cette partie de l'article, nous les verrons comme des arbres ou des stockages de données hiérarchiques. En termes simples, une globale est une liste de données persistantes. Une liste qui est automatiquement sauvegardé sur le disque. Il est difficile d'imaginer quelque chose de plus simple pour stocker des données. Dans le code du programme (écrit en langage COS/M), la seule différence avec un tableau associatif ordinaire est le symbole **^** qui précède leur nom. Il n'est pas nécessaire de connaître SQL pour enregistrer des données dans une globale, car toutes les commandes nécessaires sont très simples et peuvent être apprises en une heure. Commençons par l'exemple le plus simple, un arborescence mono niveau avec deux branches. Les exemples sont écrits en COS. Set ^a("+7926X") = "John Sidorov" Set ^a("+7916Y") = "Sergey Smith"   Lorsque des données sont insérées dans une globale (la commande Set), 3 choses se produisent automatiquement: 1. **Sauvegarde** des données sur le disque. 2. **Indexation**. Ce qui est entre les parenthèses est un indice, ce qui est à droite du signe égal est la valeur du nœud. 3. **Tri**. Les données sont triées par une clé. La prochaine traversée mettra "Sergey Smith" en première position, suivi de "John Sidorov". Lorsque l'on obtient une liste d'utilisateurs à partir d'une globale, la base de données ne passe pas de temps à trier. Vous pouvez en fait demander une liste triée à partir de n'importe quelle clé, même une clé inexistante (la sortie commencera à partir de la première clé réelle suivant celle-ci). Toutes ces opérations sont effectuées à une vitesse incroyable. Sur mon système personnel (i5-3340, 16GB, HDD WD 1TB Blue), j'ai réussi à atteindre 1 050 000 insertions/sec en un seul processus. Sur les systèmes multi-cœurs, les vitesses peuvent atteindre [des dizaines de millions](http://www.intersystems.com/library/library-item/data-scalability-with-intersystems-cache-and-intel-processors/) of insertions/sec. Bien sûr, la vitesse de saisie des données en elle-même ne dit pas grand-chose. Nous pouvons, par exemple, saisir des données dans des fichiers texte - c'est ainsi que le traitement fonctionne chez Visa, selon les rumeurs. Cependant, avec les globales, nous obtenons un stockage structuré et indexé avec lequel vous pouvez travailler en profitant de sa grande vitesse et de sa facilité d'utilisation. * **La plus grande force des globales est la vitesse d'insertion de nouveaux nœuds dans ceux-ci.** * **Les données sont toujours indexées dans une globale. Les traversées en profondeur et les traversées arborescentes mono-niveau sont toujours très rapides.** Ajoutons quelques branches de deuxième et troisième niveau de la globale. Set ^a("+7926X", "city") = "Moscow" Set ^a("+7926X", "city", "street") = "Req Square" Set ^a("+7926X", "age") = 25 Set ^a("+7916Y", "city") = "London" Set ^a("+7916Y", "city", "street") = "Baker Street" Set ^a("+7916Y", "age") = 36 ![](/sites/default/files/inline/images/sergey_john_2_full_opt.png) Apparemment, vous pouvez construire des arbres à plusieurs niveaux en utilisant des globales. L'accès à n'importe quel nœud est presque instantané grâce à l'indexation automatique après chaque insertion. Les branches de l'arbre à n'importe quel niveau sont triées par une clé. Comme vous pouvez le constater, les données peuvent être stockées à la fois sous forme de clés et de valeurs. La longueur combinée d'une clé (la somme des longueurs de tous les index) peut atteindre [511 octets](http://docs.intersystems.com/ens20152/csp/docbook/DocBook.UI.Page.cls?KEY=GGBL_structure#GGBL_structure_maxsubscrlen) et les valeurs peuvent atteindre une taille de [3,6 MB](http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=GORIENT_appx_limits_long_string) dans Caché. Le nombre de niveaux dans un arbre (nombre de dimensions) est plafonné à 31. Une autre chose intéressante : vous pouvez construire un arbre sans définir les valeurs des nœuds de niveau supérieur. Set ^b("a", "b", "c", "d") = 1 Set ^b("a", "b", "c", "e") = 2 Set ^b("a", "b", "f", "g") = 3 Les cercles vides sont des nœuds sans valeur. Pour mieux comprendre les globales, comparons-les à d'autres arbres : les arbres de jardin et les arbres de noms de systèmes de fichiers. Comparons les globales aux structures hiérarchiques les plus familières : les arbres réguliers qui poussent dans les jardins et les champs, ainsi que les systèmes de fichiers. ![](/sites/default/files/inline/images/garden_tree.png) Comme nous pouvons le constater, les feuilles et les fruits ne poussent qu'à l'extrémité des branches des arbres ordinaires. Systèmes de fichiers - les informations sont également stockées à l'extrémité des branches, également connues comme des noms de fichiers complets. Et voici la structure de données d'une globale. Differences: 1. **Nœuds internes:** les informations d'une globale peuvent être stockées dans tous les nœuds et pas seulement aux extrémités des branches. 2. **Nœuds externes:** les globales doivent avoir des extrémités de branche définies (extrémités avec des valeurs), ce qui n'est pas obligatoire pour les systèmes de fichiers et les arbres de jardin. En ce qui concerne les noeuds internes, nous pouvons traiter la structure de globale comme un sur-ensemble des arbres de noms des systèmes de fichiers et des arbres de jardins. La structure de globale est donc plus flexible. En général, une globale est un **arbre structuré qui prend en charge la sauvegarde des données dans chaque nœud.** Afin de mieux comprendre le fonctionnement des globales, imaginons ce qui se passerait si les créateurs d'un système de fichiers utilisaient une approche identique à celle des globales pour stocker les informations ? 1. Si le dernier fichier d'un dossier était supprimé, le dossier lui-même serait également supprimé, ainsi que tous les dossiers de niveau supérieur qui ne contenaient que ce dossier supprimé. 2. Il n'y aurait pas besoin de dossiers du tout. Il y aurait des fichiers avec des sous-fichiers et des fichiers sans sous-fichiers. Si vous le comparez à un arbre normal, chaque branche deviendrait un fruit. ![](/sites/default/files/inline/images/garden_tree_global_0.png)   3. Des choses comme README.txt ne seraient probablement plus nécessaires. Tout ce que vous avez besoin de dire sur le contenu d'un dossier pourrait être écrit dans le fichier du dossier lui-même. En général, les noms de fichiers ne peuvent pas être distingués des noms de dossiers (par exemple, /etc/readme peut être soit un dossier, soit un fichier), ce qui signifie que nous pourrions nous contenter d'exploiter des fichiers. 4. Les dossiers comportant des sous-dossiers et des fichiers pourraient être supprimés beaucoup plus rapidement. Il existe des articles sur le net qui racontent à quel point il est long et difficile de supprimer des millions de petits fichiers ([1](https://serverfault.com/questions/822556/how-to-delete-millions-of-files-without-disturbing-the-server), [2](https://www.quora.com/How-can-someone-rapidly-delete-400-000-files), [3](https://superuser.com/questions/680119/deleting-millions-of-files)). En revanche, si vous créez un pseudo-système de fichiers basé sur une globale, cela prendra quelques secondes, voire des fractions de seconde. Lorsque j'ai testé la suppression de sous-arbres sur mon ordinateur personnel, j'ai réussi à supprimer 96 à 341 millions de nœuds d'un arbre à deux niveaux sur un disque dur (pas un disque dur externe). Et il convient de mentionner que nous parlons de la suppression d'une partie d'un arbre global, et non de la suppression d'un fichier entier contenant une globale. **La suppression des sous-arbres est encore un autre avantage des globaux : vous n'avez pas besoin de récursion pour cela**. C'est incroyablement rapide. Dans notre arbre, cela pourrait être fait avec une commande **Kill**. Kill ^a("+7926X") ![](/sites/default/files/inline/images/sergey_john_2_kill_opt.png) Vous trouverez ci-dessous un petit tableau qui vous permettra de mieux comprendre les actions que vous pouvez effectuer sur une globale. Commandes et fonctions clés liées aux globales dans COS Set Paramétrage (initialisation) des branches jusqu'à un noeud (si non défini) et valeur du noeud Merge Copie d'un sous-arbre Kill Suppression d'un sous-arbre ZKill Suppression de la valeur d'un nœud particulier. Le sous-arbre issu de ce noeud n'est pas affecté $Query Traversée complète et approfondie de l'arbre $Order Renvoie l'indice suivant au même niveau $Data Vérifier si un nœud est défini $Increment Incrémentation atomique de la valeur d'un nœud afin d'éviter la lecture et l'écriture pour ACID. La dernière recommandation est d'utiliser $Sequence à la place. Merci de votre attention, je serai heureux de répondre à vos questions. **Démenti**: Cet article reflète l'opinion privée de l'auteur et n'a aucun rapport avec la position officielle d'InterSystems.
Article
Sylvain Guilbaud · Mai 9

Traitement parallèle des requêtes - ( au niveau du système et en fonction des requêtes)

L'indication de requête parallèle augmente les performances de certaines requêtes sur les systèmes multiprocesseurs par le biais du traitement parallèle. L'optimiseur SQL détermine les cas où cela est bénéfique. Sur les systèmes à un seul processeur, cette indication n'a aucun effet. Le traitement parallèle peut être géré par: 1. Définition de l'option auto parallel pour l'ensemble du système. 2. L'utilisation du mot-clé %PARALLEL dans la clause FROM de certaines requêtes. Le mot clé %PARALLEL est ignoré lorsqu'il est appliqué aux requêtes suivantes: 1. Les requêtes INSERT, UPDATE et DELETE (cette fonctionnalité ne s'applique qu'aux requêtes SELECT) 2. Les requêtes impliquant des fonctions ou des variables spécifiques au processus 3. Une sous-requête corrélée à une requête englobante. 4. Une sous-requête contenant des prédicats complexes, tels que les prédicats FOR SOME et FOR SOME %ELEMENT. En plus des raisons mentionnées précédemment, voici quelques raisons pour lesquelles le traitement parallèle des requêtes peut être ignoré: * Certaines requêtes complexes ne bénéficient pas d'un traitement parallèle, même si elles semblent en bénéficier au départ. * Certaines configurations et paramètres de base de données ne supportent pas le traitement %PARALLEL. * Les dépendances et les relations au sein de la structure des données peuvent empêcher une parallélisation efficace. Dans des scénarios suivants, %PARALLEL n'effectuera pas de traitement parallèle: 1. La requête inclut à la fois les clauses TOP et ORDER BY, en optimisant le temps le plus rapide pour atteindre la première ligne. 2. La requête fait référence à une vue et renvoie un identifiant de vue. 3. La requête utilise des formats de stockage personnalisés ou des tables GLOBAL TEMPORARY ainsi que des tables avec un stockage de référence global étendu. 4. La requête accède à une table avec une sécurité au niveau de la ligne. 5. Les données sont stockées dans une base de données distante. 6. La collation NLS au niveau du processus ne correspond pas à la collation NLS de tous les globaux impliqués. Pour plus de détails sur les options, les considérations et les restrictions, reportez-vous à [Configure Parallel Query Processing (Interystems Documentation)](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSOC_parallel#GSOC_parallel_specific) et [Specify Optimization Hints in Queries](https://docs.intersystems.com/iris20242/csp/docbook/DocBook.UI.Page.cls?KEY=GSOC_hints#GSOC_hints_clausekeys_parallel) (Configuration du traitement parallèle des requêtes (Documentation Interystems) et Spécifier les conseils d'optimisation dans les requêtes). Ce sujet a été récemment exploré dans le cadre d'une discussion au sein de la [Communauté de développeurs d'InterSystems (DC)](https://community.intersystems.com/post/autoparallel-feature-sql-not-working), qui a inspiré cet article sur IRIS, Cache et Ensemble. InterSystems IRIS supporte le traitement parallèle à la fois pour le SQL intégré, le SQL dynamique et le SQL dans les QueryMethods. Lorsque le mot-clé %PARALLEL est utilisé dans la clause FROM d'une requête pour suggérer un traitement parallèle. L'optimiseur SQL déterminera si la requête peut bénéficier d'un traitement parallèle et l'appliquera le cas échéant. Pour utiliser efficacement le traitement %PARALLEL dans InterSystems IRIS, plusieurs paramètres et restrictions doivent être pris en compte, tant au niveau du système qu'au niveau de la requête, afin d'en tirer tous les avantages. Dans le cas où vous essayez d'obtenir le traitement %PARALLEL à l'aide du traitement parallèle des requêtes à l'échelle du système et que le mode adaptatif est désactivé, vous pouvez activer le traitement parallèle des requêtes à l'échelle du système via le Portail de gestion ou $SYSTEM.SQL.Util.SetOption() Exemple USER>w ##class(%SYSTEM.SQL.Util).GetOption("AutoParallel") 0 USER>d ##class(%SYSTEM.SQL.Util).SetOption("AutoParallel",1,.oldParVal) USER>w ##class(%SYSTEM.SQL.Util).GetOption("AutoParallel") 1 USER>zw oldParVal oldParVal=0 Autres aspects importants à prendre en compte lors de la mise en œuvre de la fonctionnalité %PARALLEL. * Lorsque le [AdaptiveMode](https://docs.intersystems.com/iris20242/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_AdaptiveMode) est activé, le traitement parallèle automatique est appliqué à toutes les requêtes SELECT, en les accompagnant de la mention %PARALLEL. Cependant, toutes les requêtes ne peuvent pas utiliser le traitement parallèle, car l'optimiseur SQL peut en décider autrement. * Lorsque nous essayons d'utiliser cette fonctionnalité %PARALLEL, nous devons également prendre en compte le paramètre [AutoParallelThreshold](https://docs.intersystems.com/iris20242/csp/docbook/Doc.View.cls?KEY=RACS_AutoParallelThreshold) (la valeur par défaut est 3200) et ce paramètre n'est pas utile dans le cas où [AutoParallel](https://docs.intersystems.com/iris20242/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_AutoParallel) est désactivé. * Le paramètre AutoParallelThreshold détermine si une requête est exécutée en parallèle, les valeurs les plus élevées réduisant les chances de traitement en parallèle. La valeur par défaut est 3200, elle peut être ajustée via $SYSTEM.SQL.Util.SetOption("AutoParallelThreshold",n,.oldval). * Dans les environnements partagés, le traitement parallèle est utilisé pour toutes les requêtes, quel que soit le seuil, lorsque le mode adaptatif AdaptiveMode est activé. * Lorsque le mode AdaptiveMode est activé (défini à 1) et que la fonctionnalité AutoParallel est désactivée, le Mode adaptatif remplace le paramètre AutoParallel et active le traitement parallèle. Exemple: Exemple de classe avec 100 000 enregistrements remplis Class SQLClass.MyTest Extends (%Persistent, %Populate) { Property Name As %String(MAXLEN = 255); Property Age As %Integer(MAXVAL = 100, MINVAL = 1); Property Address As %String(MAXLEN = 255); Property City As %String(MAXLEN = 255); Property State As %String(MAXLEN = 255); Property Zip As %String(MAXLEN = 255); Property Country As %String(MAXLEN = 255); Property Comment As %String(MAXLEN = 255); Property Hobby As %String(MAXLEN = 255); Property JobTitle As %String(MAXLEN = 255); Property Company As %String(MAXLEN = 255); Property PhoneNumber As %String(MAXLEN = 255); Property Email As %String(MAXLEN = 255); Property Gender As %String(MAXLEN = 1); Property Ethnicity As %String(MAXLEN = 255); Property Race As %String(MAXLEN = 255); Property Religion As %String(MAXLEN = 255); Property MaritalStatus As %String(MAXLEN = 255); Property Children As %Integer(MAXVAL = 10, MINVAL = 0); Property Income As %Integer(MAXVAL = 100000, MINVAL = 0); Property Occupation As %String(MAXLEN = 255); Property Education As %String(MAXLEN = 255); Property HomePhone As %String(MAXLEN = 255); Property MobilePhone As %String(MAXLEN = 255); Property WorkPhone As %String(MAXLEN = 255); Property WorkEmail As %String(MAXLEN = 255); Property HomeEmail As %String(MAXLEN = 255); Property HomeAddress As %String(MAXLEN = 255); Property HomeCity As %String(MAXLEN = 255); Property HomeState As %String(MAXLEN = 255); Property HomeZip As %String(MAXLEN = 255); Property HomeCountry As %String(MAXLEN = 255); Property WorkAddress As %String(MAXLEN = 255); Property WorkCity As %String(MAXLEN = 255); Property WorkState As %String(MAXLEN = 255); Property WorkZip As %String(MAXLEN = 255); Property WorkCountry As %String(MAXLEN = 255); Property WorkPhoneNumber As %String(MAXLEN = 255); Property WorkMobilePhone As %String(MAXLEN = 255); Property WorkFax As %String(MAXLEN = 255); Property WorkWebsite As %String(MAXLEN = 255); Property WorkComments As %String(MAXLEN = 255); Index IdxAge On Age; } **Test n° 1** **Exemple d'exécution sans % PARALLEL (pour afficher 10 000 enregistrements en SMP)** select * from SQLClass.MyTest where age>40 * 3.2069 secondes * 10404 références globales * 3325407 commandes exécutées **Exemple d'exécution avec %PARALLEL(pour afficher 10 000 enregistrements dans SMP)** select * from %PARALLEL SQLClass.MyTest where age>40 * 2.8681 secondes * 10404 références globales * 3325407 commandes exécutées **Test n° 2 :** **Exemple d'exécution sans % PARALLEL (pour afficher 1 enregistrement en SMP)** select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from SQLClass.MyTest where age>10 * 0.4037 secondes * 46559 références globales * 1459936 commandes exécutées **Exemple d'exécution avec %PARALLEL (pour afficher 1 enregistrement en SMP)** select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from %PARALLEL SQLClass.MyTest where age>10 * 0.0845 secondes * 46560 références globales * 1460418 commandes exécutées **Exemple avec SQL intégré** ClassMethod embeddedSQL() As %Status { // w ##Class(SQLClass.MyTest).embeddedSQL() Set sc = $$$OK DO ClearBuffers^|"%SYS"|GLOBUFF() set stime=$p($zts,",",2) &sql(select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from SQLClass.MyTest where age>10) w:'SQLCODE "Without %Parallel : ",($p($zts,",",2)-stime),! DO ClearBuffers^|"%SYS"|GLOBUFF() set stime=$p($zts,",",2) &sql(select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from %PARALLEL SQLClass.MyTest where age>10) w:'SQLCODE "With %Parallel : ",($p($zts,",",2)-stime),! Return sc } **Résultats (SQL intégré) :** USER> D ##Class(SQLClass.MyTest).embeddedSQL() 5466 blocs supprimés Sans %Parallel : .355737 5217 blocs supprimés Avec %Parallel : .3407056 USER> **Exemple avec SQL dynamique** ClassMethod dynamicSQL() As %Status { // w ##Class(SQLClass.MyTest).dynamicSQL() Set sc = $$$OK DO ClearBuffers^|"%SYS"|GLOBUFF() set stime=$p($zts,",",2), recCnt=0 Set rs=##class(%ResultSet).%New() Set sc=rs.Prepare("select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from SQLClass.MyTest where age>10") Set sc=rs.Execute() While(rs.Next()) { w "COUNT(Children) : ",rs.GetData(1),"; MAX(Children) : ",rs.GetData(2),"; MIN(Children) : ",rs.GetData(3),"; AVG(Children) : ",rs.GetData(4),! } w "Without %Parallel : ",($p($zts,",",2)-stime),!!! DO ClearBuffers^|"%SYS"|GLOBUFF() set stime=$p($zts,",",2), recCnt=0 Set sc=rs.Prepare("select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from SQLClass.MyTest where age>10") Set sc=rs.Execute() While(rs.Next()) { w "COUNT(Children) : ",rs.GetData(1),"; MAX(Children) : ",rs.GetData(2),"; MIN(Children) : ",rs.GetData(3),"; AVG(Children) : ",rs.GetData(4),! } w "With %Parallel : ",($p($zts,",",2)-stime),! Return sc } **Résultats (SQL dynamique):** USER>d ##Class(SQLClass.MyTest).dynamicSQL() 22163 blocs supprimés NOMBRE(Enfants) : 89908; MAX(Enfants) : 10; MIN(Enfants) : 0; AVG(Enfants) : 5.021989144458780086 Sans %Parallel : .4036913 5721 blocs supprimés NOMBRE(Enfants) : 89908; MAX(Enfants) : 10; MIN(Enfants) : 0; AVG(Enfants) : 5.021989144458780086 Avec %Parallel : .3693442
Question
Corentin Blondeau · Juin 4

Plusieurs espaces de nom ou un seul

Bonjour,Quels sont les avantages et les inconvénients d'avoir plusieurs namespaces par rapport à un seul?J'aimerais bien savoir les aspects positifs et négatifs des deux cas de figures, par exemple 1 ou 4 espaces de nom pour un total de 20 flux. Qu'en est t'il niveau sécurité, gestion serveur, gestion du code, autre?En terme de performances, est-ce que trop d'items sur une seul production ralenti le serveur? Ou trop de namespaces augmente la consommation mémoire pour rien?Comment évolue la consommation d'un namespace? Il y a un minimum?Je vous remercie de vos réponses.Corentin BLONDEAU Dans InterSystems IRIS, le choix entre un seul namespace ou plusieurs dépend des besoins en isolation, sécurité, gestion du code et performances. Si tous les flux sont homogènes d'un point de vue métier, il n'est pas nécessaire de recourir à plusieurs namespaces (plusieurs client utilisent en production des centaines ou des milliers de flux dans un seul namespace). L'utilisation de plusieurs namespaces offre cependant plusieurs avantages, notamment liés à la sécurité et à l'isolation des processus et données, dès l'instant où vous avez des flux répondant à des besoins métiers hétérogènes ou à des utilisations spécifiques. Avantages d'utiliser plusieurs namespaces Isolation des environnements Chaque namespace peut être dédié à un usage spécifique (développement, test, production). Cela réduit les risques de conflits entre configurations. Sécurité renforcée Les mappings contrôlent précisément l'accès aux données et au code. Un namespace peut restreindre l'accès à certaines bases de données via des règles de mapping global/routine. Gestion modulaire Configuration indépendante pour chaque flux d'intégration (interopérabilité) Mise à jour/redéploiement partiel sans impacter l'ensemble du système Inconvénients de multiples namespaces Complexité accrue de l’administration Plus il y a de namespaces, plus la gestion des mappings, des bases de données associées, des configurations de sécurité, des accès utilisateurs et des stratégies de sauvegarde devient complexe. Chaque namespace nécessite une configuration propre (databases, mappings, sécurité), ce qui multiplie les tâches d’administration et augmente le risque d’erreurs humaines lors des modifications ou migrations. Gestion des sauvegardes et restaurations La multiplication des namespaces entraîne une augmentation du nombre de fichiers IRIS.DAT à sauvegarder et restaurer. Cela peut rallonger les temps de sauvegarde/restauration et compliquer la gestion des versions et des points de reprise, car il faut garantir la cohérence entre plusieurs bases de données et namespaces. Verrouillage et contention Les locks sur les globals sont globaux à la base de données : si plusieurs namespaces accèdent à la même base de données via des mappings, ils peuvent se retrouver en concurrence sur les mêmes verrous, ce qui peut générer des contentions inattendues et compliquer le diagnostic des problèmes. Mise à jour et déploiement Mettre à jour du code ou des configurations dans plusieurs namespaces nécessite de répéter les opérations, ce qui augmente le risque d’incohérences entre environnements et la charge de travail pour les équipes DevOps. Surcoût de maintenance Chaque namespace ajoute des tâches de maintenance (monitoring, logs, gestion des utilisateurs, etc.). À grande échelle, cela peut devenir difficile à suivre et à automatiser sans outils adaptés Difficulté de suivre une trace complète dans Visual Trace : un inconvénient majeur de l’utilisation de plusieurs namespaces, lorsque les flux métier sont liés. Visual Trace ne permet de visualiser que les messages et les transactions d’un seul namespace à la fois. Si un flux métier traverse plusieurs productions réparties sur différents namespaces, il n’est pas possible de visualiser le parcours global du message ou de la transaction dans une seule vue Visual Trace. L’opérateur ou le développeur doit ouvrir séparément chaque namespace dans le Management Portal, puis rechercher manuellement les messages correspondants (souvent avec des identifiants différents ou des métadonnées partielles), ce qui rend le diagnostic et l’analyse chronologique beaucoup plus complexes. Il n’existe pas de corrélation automatique des traces entre namespaces : chaque Visual Trace est limité à l’espace de noms courant, sans suivi transversal natif. Dès lors que des flux d’intégration sont distribués sur plusieurs namespaces, la traçabilité globale via Visual Trace devient morcelée, ce qui complique considérablement le support, la maintenance et l’investigation des incidents métier. Isolation des processus L’isolation des processus dans chaque espace de noms (namespace) InterSystems IRIS est un sujet important pour la sécurité, la stabilité et la gestion de vos applications. Voici une explication détaillée : 1. Qu’est-ce qu’un namespace dans IRIS ? Un namespace est un environnement logique qui regroupe : Une ou plusieurs bases de données (globals, routines) Des mappings de code et de données Des configurations spécifiques (services, productions, etc.) 2. Isolation des processus : ce qui est isolé a. Espace d’adressage des données Chaque namespace a ses propres mappings de bases de données. Les globals et routines sont isolés par défaut : un processus dans le namespace A n’accède pas aux données du namespace B (sauf si mapping explicite). b. Contexte d’exécution Les processus lancés dans un namespace (par exemple, un job d’intégration, une session terminal, un service REST) s’exécutent dans le contexte de ce namespace. Les variables système, le $namespace courant, et les paramètres de session sont propres à chaque processus et à chaque namespace. c. Productions d’interopérabilité Les productions (Ensemble/IRIS Interoperability) sont isolées par namespace : chaque namespace a sa propre production, ses propres Business Services, Processes et Operations. Les queues, logs, et messages sont séparés. d. Sécurité Les droits d’accès (Rôle, Ressource) peuvent être définis par namespace. Les utilisateurs peuvent être limités à certains namespaces via la configuration des applications web/services. 3. Ce qui n’est PAS isolé Processus système : Les processus système (ex : le démon d’écriture, le garbage collector) sont globaux à l’instance IRIS. Caches partagés : Le cache de routines et de données est partagé entre tous les namespaces. Mémoire partagée (gmheap) : Les structures internes sont communes. Journalisation (Write Image Journal, Journaux d’audit) : Certains fichiers de journalisation sont globaux. 4. Exemple pratique Si vous lancez un job dans le namespace A, il ne voit que les données et le code de A (sauf mapping). Si un processus dans le namespace B plante, il n’impacte pas directement les jobs du namespace A. Les productions d’interopérabilité ne partagent pas leurs queues ou messages. 5. Limites de l’isolation Processus : Tous les processus sont visibles dans la vue système (^%SS, Management Portal), quel que soit leur namespace d’origine. Ressources système : Les processus de tous les namespaces consomment la même mémoire, CPU et I/O disque. 6. Résumé Élément Isolé par namespace Global à l’instance Données (globals) ✔️ Code (routines/classes) ✔️ Productions interop ✔️ Processus système ✔️ Caches (données/routines) ✔️ Mémoire partagée ✔️ Journalisation ✔️ L’isolation des processus dans chaque namespace est forte sur le plan applicatif (données, code, productions), mais partielle sur le plan système (ressources partagées). Pour une isolation maximale, il faut combiner la séparation par namespace avec des politiques de sécurité et, si nécessaire, des instances IRIS séparées. En conclusion, une architecture multi-namespaces est préférable pour des flux hétérogènes nécessitant une isolation forte, malgré une complexité opérationnelle accrue. Pour des flux homogènes, un namespace unique avec scaling vertical (CPU/mémoire) peut suffire. @Corentin.Blondeau, c'est la nature des 20 flux qui doit être considérée pour décider du nombre de namespaces. Si les 20 flux sont tous liés entre eux d'un point de vue métier, un seul namespace est recommandé. Si parmi ces 20 flux, une vraie séparation métier existe, il est recommandé de les séparer dans plusieurs namespaces, même si certains d'entre eux ne reçoivent qu'un flux, la séparation offrira plus de souplesse dans l'administration et la gestion opérationnelle. L'impact sur la consommation mémoire est marginal ; seuls les processus Ens.Actor, Ens.Alarm, Ens.Alert, Ens.MonitorService, Ens.ProductionMonitorService, Ens.ScheduleHandler seront dupliqués pour chaque nouveau namespace. Chaque instance IRIS pouvant supporter jusqu'à 2048 namespaces, il ne faut pas s'en priver dès l'instant où le bénéfice de l'isolation est au rendez-vous 😉 Merci de tes réponses.Pour la consommation mémoire, il n'y a pas aussi les accès avec les bases de données qui vont consommer plus? L'utilisation de multiples namespaces n'induit pas de surconsommation mémoire directe, mais une mauvaise gestion des mappings ou une configuration sous-optimale des caches peut dégrader les performances. Une planification attentive des accès aux données et des paramètres système adaptés (cache global, $ZSTORAGE, huge pages) permet de maintenir une empreinte mémoire efficace. 1. Mécanisme des namespaces et bases de données Un namespace est une entité logique sans surcoût mémoire intrinsèque. Chaque namespace accède à des bases de données (fichiers IRIS.DAT) via des mappings prédéfinis ou personnalisés. La consommation mémoire dépend principalement : Du cache des globals (mémoire partagée pour les données) Du cache des routines (mémoire partagée pour le code) De la configuration des processus individuels ($ZSTORAGE). 2. Impact des accès multi-bases Accès concurrents à plusieurs bases : Augmente l'utilisation du cache global si les données sont dispersées. Peut entraîner une fragmentation mémoire si les allocations/désallocations sont fréquentes. Mapping intelligent : Des mappings permettent d'optimiser l'accès aux données sans dupliquer les buffers, en partageant des données de référence ou du code entre plusieurs namespaces. 3. Optimisations recommandées Configuration du cache global : Allouer 25-30% de la RAM disponible pour les buffers 8KB (valeur par défaut). Utiliser des huge pages Linux pour réduire la surcharge mémoire. Limite par processus : Ajuster Maximum Per-Process Memory (KB) via le portal (256KB à 2TB). Surveiller les erreurs <STORE> pour détecter les besoins d'ajustement. Stratégie de déploiement : Regrouper les données fréquemment accédées dans une même base ou définir un mapping dans %ALL pour donner un accès à tous les namespaces aux données fréquentes. Utiliser des bases temporaires distinctes pour les opérations transactionnelles lourdes. 4. Comparaison avec les bases en mémoire InterSystems IRIS évite les écueils des bases purement en mémoire : Gestion dynamique : Seules les données actives sont conservées en mémoire via un mécanisme de buffer pool. Persistance native : Élimine le besoin de recharger les données après un redémarrage. Benchmarks : IRIS est très souvent plus rapide en requêtes SQL que des solutions in-memory pures.
Annonce
Irène Mykhailova · Sept 15, 2022

Votez au concours d'interopérabilité : Créer des solutions durables

Salut la communauté, C'est l'heure de voter ! Soumettez vos votes pour les meilleures applications du concours d'interopérabilité : Créer des solutions durables 🔥 VOTEZ POUR LES MEILLEURES APPLICATIONS 🔥 Comment voter ? Détails ci-dessous. Nomination des experts Le jury expérimenté d'InterSystems choisira les meilleures applications pour la nomination des prix dans le cadre de la nomination des experts. Veuillez accueillir nos experts : ⭐️ @Alexander.Koblov, Support Specialist⭐️ @Alexander.Woodhead, Technical Specialist⭐️ @Guillaume.Rongier7183, Sales Engineer⭐️ @Alberto.Fuentes, Sales Engineer⭐️ @Dmitry.Zasypkin, Senior Sales Engineer⭐️ @Daniel.Kutac, Senior Sales Engineer⭐️ @Eduard.Lebedyuk, Senior Cloud Engineer⭐️ @Steve.Pisani, Senior Solution Architect⭐️ @James.MacKeith, Principal Systems Developer⭐️ @Nicholai.Mitchko, Manager, Solution Partner Sales Engineering⭐️ @Timothy.Leavitt, Development Manager⭐️ @Benjamin.DeBoe, Product Manager⭐️ @Robert.Kuszewski, Product Manager⭐️ @Stefan.Wittmann, Product Manager⭐️ @Raj.Singh5479, Product Manager⭐️ @Jeffrey.Fried, Director of Product Management⭐️ @Evgeny.Shvarov, Developer Ecosystem Manager Community nomination: Pour chaque utilisateur, un score plus élevé est sélectionné parmi les deux catégories ci-dessous : Conditions Place 1ère 2ème 3ème Si vous avez un article publié sur DC et une application téléchargée sur Open Exchange (OEX) 9 6 3 Si vous avez au moins 1 article posté sur le DC ou 1 application téléchargée sur OEX 6 4 2 Si vous apportez une contribution valable au DC (commentaire/question, etc.). 3 2 1 Niveau Place 1ère 2ème 3ème Niveau VIP Global Masters ou ISC Product Manager 15 10 5 Niveau Ambassador GM 12 8 4 Niveau Expert GM ou DC Moderators 9 6 3 Niveau Specialist GM 6 4 2 Niveau Advocate GM or ISC Employee 3 2 1 Vote à l'aveugle ! Le nombre de votes pour chaque application sera caché à tous. Une fois par jour, nous publierons le classement dans les commentaires de ce post. L'ordre des projets sur la page du concours sera le suivant : plus une application a été soumise tôt au concours, plus elle sera en haut de la liste. P.S. N'oubliez pas de vous abonner à ce billet (cliquez sur l'icône de la cloche) pour être informé des nouveaux commentaires. Pour participer au vote, vous devez : Vous connecter à Open Exchange – les informations d'identification DC fonctionneront. Apporter une contribution valide à la communauté des développeurs - répondre ou poser des questions, écrire un article, contribuer à des applications sur Open Exchange - et vous pourrez voter. Consultez cet article sur les options permettant d'apporter des contributions utiles à la communauté des développeurs. Si vous changez d'avis, annulez le choix et donnez votre vote à une autre application ! Soutenez l'application que vous aimez ! Remarque : les participants au concours sont autorisés à corriger les bugs et à apporter des améliorations à leurs applications pendant la semaine de vote, alors ne manquez pas de vous abonner aux versions des applications !
Article
Lucas Enard · Sept 14, 2022

Un exemple simple de client Fhir en python

# 1. Fhir-client-python Ceci est un client fhir simple en python pour s'exercer avec les ressources fhir et les requêtes CRUD vers un serveur FHIR. Notez que pour la plupart, l'autocomplétion est activée, c'est la principale raison d'utiliser fhir.resources. [GitHub](https://github.com/LucasEnard/fhir-client-python) - [1. Fhir-client-python](#1-fhir-client-python) - [2. Préalables](#2-préalables) - [3. Installation](#3-installation) - [3.1. Installation pour le dévelopment](#31-installation-pour-le-développement) - [3.2. Portail de gestion et VSCode](#32-portail-de-gestion-et-VSCode) - [3.3. Avoir le dossier ouvert à l'intérieur du conteneur](#33-avoir-le-dossier-ouvert-à-l'intérieur-du-conteneur) - [4. Serveur FHIR](#4-serveur-FHIR) - [5. Présentation pas à pas](#5-présentation) - [5.1. Partie 1](#51-partie-1) - [5.2. Partie 2](#52-partie-2) - [5.3. Partie 3](#53-partie-3) - [5.4. Partie 4](#54-partie-4) - [5.5. Conclusion de la présentation](#55-conclusion-de-la-présentation) - [6. Comment ça marche](#6-comment-ça-marche) - [6.1. Les importations](#61-le-importations) - [6.2. Cree le client](#62-creaer-le-client) - [6.3. Travailler sur nos ressources](#63-travailler-sur-nos-ressources) - [6.4. Sauvegarder nos changements](#64-sauvegarder-nos-changements) - [7. Comment commencer le codage](#7-comment-commencer-le-codage) - [8. Ce qu'il y a dans le référentiel](#8-ce-qu'il-y-a-dans-le-référentiel) - [8.1. Dockerfile](#81-dockerfile) - [8.2. .vscode/settings.json](#82-vscodesettingsjson) - [8.3. .vscode/launch.json](#83-vscodelaunchjson) # 2. Préalables Assurez-vous que [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) et [Docker desktop](https://www.docker.com/products/docker-desktop) sont installé. Si vous travaillez à l'intérieur du conteneur, comme il est montré dans [3.3.](#33-having-the-folder-open-inside-the-container), vous n'avez pas besoin d'installer fhirpy et fhir.resources. Si vous n'êtes pas dans le conteneur, vous pouvez utiliser `pip` pour installer `fhirpy` et `fhir.resources`. Vérifiez [fhirpy](https://pypi.org/project/fhirpy/#resource-and-helper-methods) et [fhir.resources](https://pypi.org/project/fhir.resources/) pour plus d'information. # 3. Installation ## 3.1. Installation pour le développement Clone/git tire le repo dans n'importe quel répertoire local, par exemple comme indiqué ci-dessous : ``` git clone https://github.com/LucasEnard/fhir-client-python.git ``` Ouvrez le terminal dans ce répertoire et lancez : ``` docker build. ``` ## 3.2. Portail de gestion et VSCode Ce référentiel est prêt pour [VS Code](https://code.visualstudio.com/). Ouvrez le dossier `fhir-client-python` cloné localement dans VS Code. Si vous y êtes invité (coin inférieur droit), installez les extensions recommandées. ## 3.3. Avoir le dossier ouvert à l'intérieur du conteneur Vous pouvez être *à l'intérieur* du conteneur avant de coder si vous le souhaitez. Pour cela, il faut que docker soit activé avant d'ouvrir VSCode. Ensuite, dans VSCode, lorsque vous y êtes invité ( coin inférieur droit ), rouvrez le dossier à l'intérieur du conteneur afin de pouvoir utiliser les composants python qu'il contient. La première fois que vous effectuez cette opération, cela peut prendre plusieurs minutes, le temps que le conteneur soit préparé. Si vous n'avez pas cette option, vous pouvez cliquer dans le coin inférieur gauche et cliquer sur `Ouvrir à nouveau dans le conteneur` puis sélectionner `De Dockerfile` [Plus d'informations ici](https://code.visualstudio.com/docs/remote/containers) ![Architecture](https://code.visualstudio.com/assets/docs/remote/containers/architecture-containers.png) En ouvrant le dossier à distance, vous permettez à VS Code et à tous les terminaux que vous ouvrez dans ce dossier d'utiliser les composants python dans le conteneur. # 4. Serveur FHIR Pour réaliser cette présentation, vous aurez besoin d'un serveur FHIR. Vous pouvez soit utiliser le vôtre, soit vous rendre sur le site [InterSystems free FHIR trial](https://portal.live.isccloud.io) et suivre les étapes suivantes pour le configurer. En utilisant notre essai gratuit, il suffit de créer un compte et de commencer un déploiement, puis dans l'onglet `Overview` vous aurez accès à un endpoint comme `https://fhir.000000000.static-test-account.isccloud.io` que nous utiliserons plus tard. Ensuite, en allant dans l'onglet d'informations d'identification `Credentials`, créez une clé api et enregistrez-la quelque part. C'est maintenant terminé, vous avez votre propre serveur fhir pouvant contenir jusqu'à 20 Go de données avec une mémoire de 8 Go. # 5. Présentation pas à pas La présentation pas à pas du client se trouve à `src/client.py`. Le code est divisé en plusieurs parties, et nous allons couvrir chacune d'entre elles ci-dessous. ## 5.1. Partie 1 Dans cette partie, nous connectons notre client à notre serveur en utilisant fhirpy et nous obtenons nos ressources Patient à l'intérieur de la variable `patients_resources`. A partir de cette variable nous pourrons fecth n'importe quel Patient et même les trier ou obtenir un Patient en utilisant certaines conditions. ```python #Partie 1---------------------------------------------------------------------------------------------------------------------------------------------------- #Créer notre client connecté à notre serveur client = SyncFHIRClient(url='url', extra_headers={"x-api-key":"api-key"}) #Obtenir les ressources de notre patient dans lesquelles nous pourrons aller chercher et rechercher patients_resources = client.resources('Patient') ``` Afin de vous connecter à votre serveur, vous devez modifier la ligne : ```python client = SyncFHIRClient(url='url', extra_headers={"x-api-key":"api-key"}) ``` L`'url'` est un point de terminaison tandis que l'`"api-key"` est la clé d'api pour accéder à votre serveur. Notez que si **vous n'utilisez pas** un serveur InterSystems, vous pouvez vérifier comment changer `extra_headers={"x-api-key":"api-key"}` en `authorization = "api-key"`. Comme ça, nous avons un client FHIR capable d'échanger directement avec notre serveur. ## 5.2. Partie 2 Dans cette partie, nous créons un Patient en utilisant Fhir.Model et nous le complétons avec un HumanName, en suivant la convention FHIR, `use` et `family` sont des chaînes et `given` est une liste de chaînes. e la même manière, un patient peut avoir plusieurs HumanNames, donc nous devons mettre notre HumanName dans une liste avant de le mettre dans notre patient nouvellement créé. ```python #Partie 2---------------------------------------------------------------------------------------------------------------------------------------------------- #Nous voulons créer un patient et l'enregistrer sur notre serveur #Créer un nouveau patient en utilisant fhir.resources patient0 = Patient() #Créer un HumanName et le remplir avec les informations de notre patient name = HumanName() name.use = "official" name.family = "familyname" name.given = ["givenname1","givenname2"] patient0.name = [name] #Vérifier notre patient dans le terminal print() print("Our patient : ",patient0) print() #Sauvegarder (post) notre patient0, cela va le créer sur notre serveur client.resource('Patient',**json.loads(patient0.json())).save() ``` Après cela, nous devons sauvegarder notre nouveau Patient sur notre serveur en utilisant notre client. Notez que si vous lancez `client.py` plusieurs fois, plusieurs Patients ayant le nom que nous avons choisi seront créés. C'est parce que, suivant la convention FHIR, vous pouvez avoir plusieurs Patients avec le même nom, seul l' `id` est unique sur le serveur. Alors pourquoi n'avons-nous pas rempli notre Patient avec un `id` de la même façon que nous avons rempli son nom ? Parce que si vous mettez un id à l'intérieur de la fonction save(), la sauvegarde agira comme une mise à jour avant d'agir comme un sauveur, et si l'id n'est en fait pas déjà sur le serveur, il le créera comme prévu ici. Mais comme nous avons déjà des patients sue notre serveur, ce n'est pas une bonne idée de créer un nouveau patient et d'allouer à la main un Identifiant puisque la fonction save() et le serveur sont faits pour le faire à votre place. Nous conseillons donc de commenter la ligne après le premier lancement. ## 5.3. Partie 3 Dans cette partie, nous avons un client qui cherche dans nos `patients_resources` un patient nommé d'après celui que nous avons créé précédemment. ```python #Partie 3---------------------------------------------------------------------------------------------------------------------------------------------------- #Maintenant, nous voulons obtenir un certain patient et ajouter son numéro de téléphone et changer son nom avant de sauvegarder nos changements sur le serveur #Obtenez le patient en tant que fhir.resources Patient de notre liste de ressources de patients qui a le bon nom, pour la commodité, nous allons utiliser le patient que nous avons créé avant patient0 = Patient.parse_obj(patients_resources.search(family='familyname',given='givenname1').first().serialize()) #Créer le nouveau numéro de téléphone de notre patient telecom = ContactPoint() telecom.value = '555-748-7856' telecom.system = 'phone' telecom.use = 'home' #Ajouter le téléphone de notre patient à son dossier patient0.telecom = [telecom] #Changer le deuxième prénom de notre patient en "un autre prénom" patient0.name[0].given[1] = "anothergivenname" #Vérifiez notre Patient dans le terminal print() print("Notre patient avec le numéro de téléphone et le nouveau prénom : ",patient0) print() #Sauvegarder (mettre) notre patient0, ceci sauvera le numéro de téléphone et le nouveau prénom au patient existant de notre serveur client.resource('Patient',**json.loads(patient0.json())).save() ``` Une fois que nous l'avons trouvé, nous ajoutons un numéro de téléphone à son profil et nous changeons son prénom en un autre. Maintenant nous pouvons utiliser la fonction de mise à jour de notre client pour mettre à jour notre patient sur le serveur. ## 5.4. Partie 4 Dans cette partie, nous voulons créer une observation pour notre patient précédent, pour ce faire, nous cherchons d'abord notre `patients_resources` pour notre patient, puis nous obtenons son identifiant, qui est son identificateur unique. ```python #Partie 4---------------------------------------------------------------------------------------------------------------------------------------------------- #Maintenant nous voulons créer une observation pour notre client #Obtenir l'identifiant du patient auquel vous voulez attacher l'observation id = Patient.parse_obj(patients_resources.search(family='familyname',given='givenname1').first().serialize()).id print("id of our patient : ",id) #Placer notre code dans notre observation, code qui contient des codages qui sont composés de système, code et affichage coding = Coding() coding.system = "https://loinc.org" coding.code = "1920-8" coding.display = "Aspartate aminotransférase [Activité enzymatique/volume] dans le sérum ou le plasma" code = CodeableConcept() code.coding = [coding] code.text = "Aspartate aminotransférase [Activité enzymatique/volume] dans le sérum ou le plasma" #Créer une nouvelle observation en utilisant fhir.resources, nous entrons le statut et le code dans le constructeur car ils sont nécessaires pour valider une observation observation0 = Observation(status="final",code=code) #Définir notre catégorie dans notre observation, catégorie qui détient les codages qui sont composés de système, code et affichage coding = Coding() coding.system = "https://terminology.hl7.org/CodeSystem/observation-category" coding.code = "laboratoire" coding.display = "laboratoire" category = CodeableConcept() category.coding = [coding] observation0.category = [category] #Définir l'heure de notre date effective dans notre observation observation0.effectiveDateTime = "2012-05-10T11:59:49+00:00" #Régler l'heure de notre date d'émission dans notre observation observation0.issued = "2012-05-10T11:59:49.565+00:00" #Définir notre valueQuantity dans notre observation, valueQuantity qui est composée d'un code, d'un unir, d'un système et d'une valeur valueQuantity = Quantity() valueQuantity.code = "U/L" valueQuantity.unit = "U/L" valueQuantity.system = "https://unitsofmeasure.org" valueQuantity.value = 37.395 observation0.valueQuantity = valueQuantity #Définir la référence à notre patient en utilisant son identifiant reference = Reference() reference.reference = f"Patient/{id}" observation0.subject = reference #Vérifiez notre observation dans le terminal print() print("Notre observation : ",observation0) print() #Sauvegarder (poster) notre observation0 en utilisant notre client client.resource('Observation',**json.loads(observation0.json())).save() ``` Ensuite, nous enregistrons notre observation à l'aide de la fonction save(). ## 5.5. Conclusion de la présentation Si vous avez suivi ce parcours, vous savez maintenant exactement ce que fait client.py, vous pouvez le lancer et vérifier votre Patient et votre Observation nouvellement créés sur votre serveur. # 6. Comment ça marche ## 6.1. Les importations ```python from fhirpy import SyncFHIRClient from fhir.resources.patient import Patient from fhir.resources.observation import Observation from fhir.resources.humanname import HumanName from fhir.resources.contactpoint import ContactPoint import json ``` La première importation ***est*** le client, ce module va nous aider à nous connecter au serveur, à obtenir et exporter des ressources. Le module fhir.resources nous aide à travailler avec nos ressources et nous permet, grâce à l'auto-complétion, de trouver les variables dont nous avons besoin. La dernière importation est json, c'est un module nécessaire pour échanger des informations entre nos 2 modules. ## 6.2. Création du client ```python client = SyncFHIRClient(url='url', extra_headers={"x-api-key":"api-key"}) ``` L'`'url'` est ce que nous avons [appelé avant](#) un point de terminaison tandis que l'`"api-key"`' est la clé que vous avez générée plus tôt. Notez que si **vous n'utilisez pas** un serveur InterSystems, vous voudrez peut-être changer le `extra_headers={"x-api-key" : "api-key"}` en `authorization = "api-key"` Comme ça, nous avons un client FHIR capable d'échanger directement avec notre serveur. Par exemple, vous pouvez accéder à vos ressources Patient en faisant `patients_resources = client.resources('Patient')` , fà partir de là, vous pouvez soit obtenir vos patients directement en utilisant `patients = patients_resources.fetch()` ou en allant chercher après une opération, comme : `patients_resources.search(family='familyname',given='givenname').first()` cette ligne vous donnera le premier patient qui sort ayant pour nom de famille 'familyname' et pour nom donné 'givenname'. ## 6.3. Travailler sur nos ressources Une fois que vous avez les ressources que vous voulez, vous pouvez les analyser dans une ressource fhir.resources. Par exemple : ```python patient0 = Patient.parse_obj(patients_resources.search(family='familyname',given='givenname1').first().serialize()) ``` patient0 est un patient de fhir.resources, pour l'obtenir nous avons utilisé nos patients_resources comme cela a été montré précédemment où nous avons `sélectionné` un certain nom de famille et un prénom, après cela nous avons pris le `premier` qui est apparu et l'avons `sérialisé`. En mettant ce patient sérialisé à l'intérieur d'un Patient.parse_obj nous allons créer un patient de fhir.resources . Maintenant, vous pouvez accéder directement à n'importe quelle information que vous voulez comme le nom, le numéro de téléphone ou toute autre information.Pour ce faire, utilisez juste par exemple : ```python patient0.name ``` Cela renvoie une liste de HumanName composée chacune d'un attribut `use` un attribut `family` un attribut `given` as the FHIR convention is asking. Cela signifie que vous pouvez obtenir le nom de famille de quelqu'un en faisant : ```python patient0.name[0].family ``` ## 6.4. Sauvegarder nos changements Pour enregistrer toute modification de notre serveur effectuée sur un fhir.resources ou pour créer une nouvelle ressource serveur, nous devons utiliser à nouveau notre client. ```python client.resource('Patient',**json.loads(patient0.json())).save() ``` En faisant cela, nous créons une nouvelle ressource sur notre client, c'est-à-dire un patient, qui obtient ses informations de notre fhir.resources patient0. Ensuite, nous utilisons la fonction save() pour poster ou mettre notre patient sur le serveur. # 7. Comment commencer le codage Ce référentiel est prêt à être codé dans VSCode avec les plugins InterSystems. Ouvrez `/src/client.py` pour commencer à coder ou utiliser l'autocomplétion. # 8. Ce qu'il y a dans le référentiel ## 8.1. Dockerfile Le dockerfile le plus simple pour démarrer un conteneur Python. Utilisez `docker build .` pour construire et rouvrir votre fichier dans le conteneur pour travailler à l'intérieur de celui-ci. ## 8.2. .vscode/settings.json Fichier de paramètres. ## 8.3. .vscode/launch.json Fichier de configuration si vous voulez déboguer.