Recherche

Effacer le filtre
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.
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.
Article
Irène Mykhailova · Mars 24, 2023

Hacking Health Camp 2023 (jour 1)

Salut la Communauté ! Ce jour que vous avez attendu avec impatience, est enfin arrivé ! La 10ème édition de Hacking Health Camp est lancée aujourd'hui et nous, vos modérateurs favoris, sommes présents pour accompagner les équipes à mener à bien leurs projets ! Alors, restez à l'écoute pour plus de nouvelles! L'événement est l'occasion de nombreuses rencontres. Nouveaux visages et favoris familiers :) Cette fois, nous sommes tombés sur @Laurent.Viquesnel : @Lorenzo.Scalese, @Guillaume.Rongier7183 et @Laurent.Viquesnel. Mise à jour 17h00 Le HHC 2023 a commencé par les témoignages de beaux projets issus des précédentes éditions de Hacking Health Camp. Tout le monde pourrait s'inspirer de leur expérience entrepreneuriale. Pixacare - Promotion 2018 L’utilisation des photos en médecine est quotidienne. Ces photos sont souvent 95% des cas faitent avec un smartphone et cela crée les problemes avec de sécurité, de temps de gestion des images etc. Solution - c'est de gestion des photos intelligente et sécurisée. Présent aujourd’hui dans 20 établissements de santé. L’entreprise est toujours là aujourd’hui car elle a su développé un modèle économique pérenne. Importance d’avoir des investisseurs et de la visibilité pour pouvoir démarrer et se développer dans la durée. Les partenaires de l’écosystème : 5ans après : 20 salariés 2 millions € levés 22 centres utilisateurs 40 000 patients suivis e t +250k photo. Entreprendre en santé est du basejump …. Les barrières à l’entrée sont très élevées et les temps de décisions très long … mais très valorisant et valorisable ! Préférons le chameau à la licorne ! Fizimed Présence internationale La recommendation: profitez de ces 3 jours. N’ayez pas peur de la falaise. Les montagnes russes se lisseront avec le temps ! CES : Fizimed, Vedette française. Prix de l’innovation. Cet événement a véritablement boosté le développement de la start-up. France. Allemagne (produit 100% remboursé), US, Asie …. EANQA EANQA - Ne cherchez plus un psy, trouvez le vôtre ! Synapse Médecine Croissance forte ces 45 dernières années France, US, Japon. Environ 100 collaborateurs. 40 millions levée de fond. Solutions de prescriptions médicamenteuses. Notre mission : aide à la décision Persévérance works How the team your are building is the company you are building Osez ! Lancez vous! Faites ! Se tromper fait partie du process. L’humain est le plus important. HypnoVR HHC Promotion 2016 Un service rendu aux patients. Un contexte favorable : les thérapies digitales et non médicamenteuses. Une prise de conscience : un usage raisonné de la pharmacologie. La réalité virtuelle déjà évaluée. +de 20 ans d’utilisation Un logiciel qui évolue en permanence. Des cas d’usages qui se développent … avec toujours la difficulté de choisir entre l’exploitation et le focus. Et puis un jour, il faut vendre et être au clair sur ce qu’on propose : Licence logiciel Pack matériel Service Et on vend à qui ? Et ils achètent ? Yes 350 établissements nous font confiance ! Point essentiel : être bien entouré ! On ne fait pas tout seul ! L’équipe mais aussi l’écosystème. Et l’aventure vaut le coup !!!!! Notre ambition est de proposer HypnoVR partout dans le monde, car c’est une vraie alternative au traitement médicamenteux C'est tout pour le moment. Vous puvez voire tout la premiere partie sur YouTube : Mise à jour 16h00 Après la petite pause, le Hackathon a commencé ! Et pour rendre les choses intéressantes, cela a commencé par la présentation d'InterSystems par @Guillaume.Rongier7183 ! Vous pouvez regarder sa performance dans la vidéo ci-dessous : Plus d'info sur notre Challenge est disponible ici. Après cela, les pitchs des participants ont commencé. Au milieu de cette action, les organisateurs ont décidé d'organiser une méditation et un léger échauffement pour soulager les tensions. Après tous les pitches, les équipes commencent à se former. 1 coeur = 1 projet et c'est parti pour 50h de folie créative et collaborative ! Et c'est tout pour aujourd'hui. A demain !
Article
Lorenzo Scalese · Mai 12, 2022

Lecture des données AWS S3 sur le COVID en tant que table SQL dans IRIS

IRIS External Table est un projet Open Source de la communauté InterSystems, qui vous permet d'utiliser des fichiers stockés dans le système de fichiers local et le stockage d'objets en nuage comme AWS S3, en tant que tables SQL. ![IRIS External Table][IRIS External Table] Il peut être trouvé sur Github Open Exchange et est inclus dans InterSystems Package Manager ZPM. Pour installer External Table depuis GitHub, utilisez : git clone https://github.com/antonum/IRIS-ExternalTable.git iris session iris USER>set sc = ##class(%SYSTEM.OBJ).LoadDir("/IRIS-ExternalTable/src", "ck",,1) Pour installer avec ZPM Package Manager, utilisez : USER>zpm "install external-table" ## Travailler avec des fichiers locaux Créons un fichier simple qui ressemble à ceci : a1,b1 a2,b2 Ouvrez votre éditeur préféré et créez le fichier ou utilisez simplement une ligne de commande sous linux/mac : echo $'a1,b1\na2,b2' > /tmp/test.txt Dans IRIS SQL, créez une table pour représenter ce fichier : create table test (col1 char(10),col2 char(10)) Convertissez la table pour utiliser le stockage externe : CALL EXT.ConvertToExternal( 'test', '{ "adapter":"EXT.LocalFile", "location":"/tmp/test.txt", "delimiter": "," }') Et enfin - interrogez la table : select * from test Si tout fonctionne comme prévu, vous devriez voir un résultat comme celui-ci : col1 col2 a1 b1 a2 b2 Retournez maintenant dans l'éditeur, modifiez le contenu du fichier et réexécutez la requête SQL. Ta-Da !!! Vous lisez les nouvelles valeurs de votre fichier local en SQL. col1 col2 a1 b1 a2 b99 ## Lecture de données à partir de S3 Sur , vous pouvez avoir accès à des données constamment mises à jour sur le COVID, stockées par AWS dans la réserve publique de données. Essayons d'accéder à l'une des sources de données de cette réserve de données : `s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states` Si vous avez installé l'outil de ligne de commande AWS, vous pouvez répéter les étapes ci-dessous. Sinon, passez directement à la partie SQL. Vous n'avez pas besoin d'installer quoi que ce soit de spécifique à AWS sur votre machine pour suivre la partie SQL. $ aws s3 ls s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/ 2020-12-04 17:19:10 510572 us-states.csv $ aws s3 cp s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv . download: s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv to ./us-states.csv $ head us-states.csv date,state,fips,cases,deaths 2020-01-21,Washington,53,1,0 2020-01-22,Washington,53,1,0 2020-01-23,Washington,53,1,0 2020-01-24,Illinois,17,1,0 2020-01-24,Washington,53,1,0 2020-01-25,California,06,1,0 2020-01-25,Illinois,17,1,0 2020-01-25,Washington,53,1,0 2020-01-26,Arizona,04,1,0 Nous avons donc un fichier avec une structure assez simple. Cinq champs délimités. Pour exposer ce dossier S3 en tant que table externe - d'abord, nous devons créer une table "normal" avec la structure désirée : -- create external table create table covid_by_state ( "date" DATE, "state" VARCHAR(20), fips INT, cases INT, deaths INT ) Notez que certains noms de champs comme "Date" sont des mots réservés dans IRIS SQL et doivent être entourés de guillemets doubles. Ensuite - nous devons convertir cette table "régulière" en table "externe", basé sur le godet AWS S3 et le type de CSV. -- convertissez le tableau en stockage externe call EXT.ConvertToExternal( 'covid_by_state', '{ "adapter":"EXT.AWSS3", "location":"s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/", "type": "csv", "delimiter": ",", "skipHeaders": 1 }' ) Si vous regardez de près - les arguments des procédures EXT.ExternalTable sont le nom de la table et ensuite une chaîne JSON, contenant plusieurs paramètres, tels que l'emplacement pour rechercher les fichiers, l'adaptateur à utiliser, le délimiteur, etc. En plus de AWS S3 External Table supporte le stockage Azure BLOB, Google Cloud Buckets et le système de fichiers local. GitHub Repo contient des références pour la syntaxe et les options supportées pour tous les formats. Et enfin - faites une requête sur le tableau : -- faites une requête sur le tableau : select top 10 * from covid_by_state order by "date" desc [SQL]USER>>select top 10 * from covid_by_state order by "date" desc 2. select top 10 * from covid_by_state order by "date" desc date état fips cas morts 2020-12-06 Alabama 01 269877 3889 2020-12-06 Alaska 02 36847 136 2020-12-06 Arizona 04 364276 6950 2020-12-06 Arkansas 05 170924 2660 2020-12-06 California 06 1371940 19937 2020-12-06 Colorado 08 262460 3437 2020-12-06 Connecticut 09 127715 5146 2020-12-06 Delaware 10 39912 793 2020-12-06 District of Columbia 11 23136 697 2020-12-06 Florida 12 1058066 19176 L'interrogation des données de la tableau distant prend naturellement plus de temps que celle de la table "IRIS natif" ou global, mais ces données sont entièrement stockées et mises à jour sur le nuage et sont introduites dans IRIS en coulisse. Explorons quelques autres caractéristiques de la table externe. ## %PATH et tables, sur la base de plusieurs fichiers Dans notre exemple, le dossier du godet ne contient qu'un seul fichier. Le plus souvent, il contient plusieurs fichiers de la même structure, où le nom de fichier identifie soit l'horodatage, soit le numéro de périphérique, soit un autre attribut que nous voulons utiliser dans nos requêtes. Le champ %PATH est automatiquement ajouté à chaque table externe et contient le chemin d'accès complet au fichier à partir duquel la ligne a été récupérée. select top 5 %PATH, * from covid_by_state %PATH date state fips cases deaths s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-21 Washington 53 1 0 s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-22 Washington 53 1 0 s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-23 Washington 53 1 0 s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-24 Illinois 17 1 0 s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-24 Washington 53 1 0 Vous pouvez utiliser ce champ %PATH dans vos requêtes SQL comme n'importe quel autre champ. ## Données ETL vers des " tables réguliers " Si votre tâche consiste à charger des données de S3 dans une table IRIS - vous pouvez utiliser l'External Table comme un outil ETL. Il suffit de le faire : INSERT INTO internal_table SELECT * FROM external_table Dans notre cas, si nous voulons copier les données COVID de S3 dans la table local : --create local table create table covid_by_state_local ( "date" DATE, "state" VARCHAR(100), fips INT, cases INT, deaths INT ) --ETL from External to Local table INSERT INTO covid_by_state_local SELECT TO_DATE("date",'YYYY-MM-DD'),state,fips,cases,deaths FROM covid_by_state ## JOIN entre IRIS tables natif et External Table. Requêtes fédérées External Table est une table SQL. Il peut être joint à d'autres tables, utilisé dans des sous-sélections et des UNIONs. Vous pouvez même combiner la table IRIS "Régulier" et deux ou plusieurs tables externes provenant de sources différentes dans la même requête SQL. Essayez de créer une table régulier, par exemple en faisant correspondre les noms d'État aux codes d'État, comme Washington - WA. Et joignez-la à notre table basé sur S3. create table state_codes (name varchar(100), code char(2)) insert into state_codes values ('Washington','WA') insert into state_codes values ('Illinois','IL') select top 10 "date", state, code, cases from covid_by_state join state_codes on state=name Remplacez 'join' par 'left join' pour inclure les lignes pour lesquelles le code d'état n'est pas défini. Comme vous pouvez le constater, le résultat est une combinaison de données provenant de S3 et de votre table IRIS natif. ## Accès sécurisé aux données La réserve de données AWS Covid est publique. N'importe qui peut y lire des données sans aucune authentification ou autorisation. Dans la vie réelle, vous souhaitez accéder à vos données de manière sécurisée, afin d'empêcher les étrangers de jeter un coup d'œil à vos fichiers. Les détails complets de la gestion des identités et des accès (IAM) d'AWS n'entrent pas dans le cadre de cet article. Mais le minimum, vous devez savoir que vous avez besoin au moins de la clé d'accès au compte AWS et du secret afin d'accéder aux données privées de votre compte. AWS utilise l'authentification par clé de compte/secrète autentification pour signer les demandes. https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys Si vous exécutez IRIS External Table sur une instance EC2, la méthode recommandée pour gérer l'authentification est d'utiliser les rôles d'instance EC2 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html IRIS External Table sera en mesure d'utiliser les permissions de ce rôle. Aucune configuration supplémentaire n'est requise. Sur une instance locale/non EC2, vous devez spécifier AWS_ACCESS_KEY_ID et AWS_SECRET_ACCESS_KEY en spécifiant des variables d'environnement ou en installant et en configurant le client AWS CLI. export AWS_ACCESS_KEY_ID=AKIAEXAMPLEKEY export AWS_SECRET_ACCESS_KEY=111222333abcdefghigklmnopqrst Assurez-vous que la variable d'environnement est visible dans votre processus IRIS. Vous pouvez le vérifier en exécutant : USER>write $system.Util.GetEnviron("AWS_ACCESS_KEY_ID") Il doit renvoi la valeur de clé. ou installer AWS CLI, en suivant les instructions ici https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html et exécuter : aws configure External Table sera alors en mesure de lire les informations d'identification à partir des fichiers de configuration aws cli. Votre shell interactif et votre processus IRIS peuvent être exécutés sous différents comptes - assurez-vous d'exécuter `aws configure` sous le même compte que votre processus IRIS. [IRIS External Table]: https://github.com/intersystems-community/IRIS-ExternalTable/raw/master/images/ExternalTableDiagram.png
Article
Sylvain Guilbaud · Juin 12, 2023

Configuration du serveur Web Apache HTTPD pour HealthShare

Il y a souvent des questions concernant la configuration idéale du Serveur Web HTTPD Apache pour HealthShare.  Le contenu de cet article décrit la configuration initiale recommandée du serveur Web pour tout produit HealthShare.  APour commencer, la version 2.4.x (64 bits) d'Apache HTTPD est recommandée.  Des versions antérieures comme 2.2.x sont disponibles, mais la version 2.2 n'est pas recommandée pour les performances et l'évolutivité de HealthShare. ## **Configuration d'Apache** ### Module API Apache sans NSD {#ApacheWebServer-ApacheAPIModulewithoutNSD} HealthShare requiert l'option d'installation Apache API Module without NSD. La version des modules liés dynamiquement dépend de la version d'Apache : * CSPa24.so (Apache Version 2.4.x) La configuration de Caché Server Pages dans le fichier Apache httpd.conf doit être effectuée par l'installation de HealthShare qui est détaillée plus loin dans ce document. Cependant, la configuration peut être effectuée manuellement. Pour plus d'informations, veuillez consulter le guide de configuration d'Apache dans la documentation d'InterSystems : Recommended Option: Apache API Module without NSD (CSPa24.so) ## **Recommandations concernant le module multiprocesseur d'Apache (MPM)** {#ApacheWebServer-ApacheMulti-ProcessingModule(MPM)Recommendations} ### Apache Prefork MPM Vs. Worker MPM {#ApacheWebServer-ApachePreforkMPMVs.WorkerMPM} Le serveur web HTTPD Apache est livré avec trois modules multiprocesseurs (MPM) : Prefork, Worker et Event.  Les MPM sont responsables de la liaison avec les ports réseau de la machine, de l'acceptation des requêtes et de l'envoi de fils pour traiter ces requêtes. Par défaut, Apache est généralement configuré avec le MPM Prefork, qui n'est pas adapté à des charges de travail importantes en termes de transactions ou d'utilisateurs simultanés. Pour les systèmes de production HealthShare, le MPM Apache **_Worker_** doit être activé pour des raisons de performance et d'évolutivité. Worker MPM est préféré pour les raisons suivantes : * Prefork MPM utilise plusieurs processus enfants avec un fil d'exécution chacun et chaque processus gère une connexion à la fois. Lorsque Prefork est utilisé, les demandes simultanées souffrent car, comme chaque processus ne peut traiter qu'une seule demande à la fois, les demandes sont mises en attente jusqu'à ce qu'un processus serveur se libère. De plus, afin d'évoluer, il faut plus de processus enfants Prefork, ce qui consomme des quantités importantes de mémoire. * Worker MPM utilise plusieurs processus enfants avec de nombreux fils d'exécution chacun. Chaque fil d'exécution gère une connexion à la fois, ce qui favorise la concurrence et réduit les besoins en mémoire. Worker gère mieux la concurrence que Prefork, car il y aura généralement des fils d'exécution libres disponibles pour répondre aux demandes au lieu des processus Prefork à un seul fil d'exécution qui peuvent être occupés. ### Paramètres MPM pour Apache Worker {#ApacheWebServer-ApacheWorkerMPMParameters} En utilisant des fils d'exécution pour servir les demandes, Worker est capable de servir un grand nombre de demandes avec moins de ressources système que le serveur basé sur le processus Prefork.  Les directives les plus importantes utilisées pour contrôler le MPM de Worker sont **ThreadsPerChild** qui contrôle le nombre de fils d'exécution déployés par chaque processus enfant et **MaxRequestWorkers** qui contrôle le nombre total maximum de fils d'exécution pouvant être lancés. Les valeurs recommandées de la directive commune Worker MPM sont détaillées dans le tableau ci-dessous : <caption>Paramètres recommandés pour le serveur web HTTPD Apache</caption> Les Directives MPM pour Apache Worker Valeur recommandée Commentaires MaxRequestWorkers  Nombre maximal d'utilisateurs simultanés de HealthShare Clinical Viewer, ou des quatre autres composants de HealthShare, fixé à la somme de toutes les tailles de pool de services commerciaux entrants pour toutes les productions d'interfaces définies. * Note : Si toutes les inconnues au moment de la configuration commencent par une valeur de '1000'   MaxRequestWorkers fixe la limite du nombre de demandes simultanées qui seront servies, c'est-à-dire qu'il restreint le nombre total de fils d'exécution qui seront disponibles pour servir les clients. Il est important que MaxRequestWorkers soit défini correctement car s'il est trop faible, les ressources seront gaspillées et s'il est trop élevé, les performances du serveur seront affectées. Notez que lorsque le nombre de connexions tentées est supérieur au nombre de travailleurs, les connexions sont placées dans une file d'attente. La file d'attente par défaut peut être ajustée avec la directive ListenBackLog.   MaxSpareThreads 250   MaxSpareThreads traite les threads inactifs à l'échelle du serveur. S'il y a trop de threads inactifs dans le serveur, les processus enfants sont tués jusqu'à ce que le nombre de threads inactifs soit inférieur à ce nombre. L'augmentation du nombre de threads inutilisés par rapport à la valeur par défaut permet de réduire les risques de réactivation des processus.   MinSpareThreads 75   MinSpareThreads traite les fils d'exécution inactifs à l'échelle du serveur. S'il n'y a pas assez de fils d'exécution libres dans le serveur, des processus enfants sont créés jusqu'à ce que le nombre de fils d'exécution libres soit supérieur à ce nombre. En réduisant le nombre de fils d'exécution inutilisés par rapport à la valeur par défaut, on réduit les risques de réactivation des processus.   ServerLimit MaxRequestWorkers divisé par ThreadsPerChild   Valeur maximale de MaxRequestWorkers pour la durée de vie du serveur. ServerLimit est une limite stricte du nombre de processus enfants actifs, et doit être supérieure ou égale à la directive MaxRequestWorkers divisée par la directive ThreadsPerChild. Avec worker, n'utilisez cette directive que si vos paramètres MaxRequestWorkers et ThreadsPerChild nécessitent plus de 16 processus serveur (valeur par défaut).   StartServers 20   La directive StartServers définit le nombre de processus de serveur enfant créés au démarrage. Comme le nombre de processus est contrôlé dynamiquement en fonction de la charge, il y a généralement peu de raisons d'ajuster ce paramètre, sauf pour s'assurer que le serveur est prêt à gérer un grand nombre de connexions dès son démarrage.   ThreadsPerChild 25   Cette directive définit le nombre de threads créés par chaque processus enfant, 25 par défaut. Il est recommandé de conserver la valeur par défaut car l'augmenter pourrait conduire à une dépendance excessive vis-à-vis d'un seul processus.   Pour plus d'informations, veuillez consulter la documentation relative à la version d'Apache concernée : * [Apache 2.4 Directives communes de MPM](http://httpd.apache.org/docs/2.4/mod/mpm_common.html) ### Exemple de configuration MPM du travailleur Apache 2.4 {#ApacheWebServer-ExampleApache2.4WorkerMPMConfiguration} Cette section explique comment configurer Worker MPM pour un serveur Web RHEL7 Apache 2.4 nécessaire pour prendre en charge jusqu'à 500 utilisateurs simultanés de TrakCare. 1. Vérifiez d'abord le MPM en utilisant la commande suivante :![](/sites/default/files/inline/images/images/apache1.png) 2. Modifiez le fichier de configuration /etc/httpd/conf.modules.d/00-mpm.conf selon les besoins, en ajoutant et en supprimant le caractère de commentaire # afin que seuls les modules Worker MPM soient chargés. Modifiez la section Worker MPM avec les valeurs suivantes dans le même ordre que ci-dessous :![](/sites/default/files/inline/images/images/Apache2.png) 3. Redémarrer Apache 4. Après avoir redémarré Apache avec succès, validez les processus worker en exécutant les commandes suivantes. Vous devriez voir quelque chose de similaire à ce qui suit confirmant le processus httpd.worker :![](/sites/default/files/inline/images/images/Apache3.png) ## **Renforcement d'Apache** {#ApacheWebServer-ApacheHardening} ### Modules requis pour Apache {#ApacheWebServer-ApacheRequiredModules} L'installation du paquetage officiel d'Apache activera par défaut un ensemble spécifique de modules Apache. Cette configuration par défaut d'Apache chargera ces modules dans chaque processus httpd. Il est recommandé de désactiver tous les modules qui ne sont pas nécessaires à HealthShare pour les raisons suivantes : * réduire l'empreinte du processus du démon httpd. * réduire le risque d'un défaut de segmentation dû à un module malveillant. * réduire les vulnérabilités en matière de sécurité. Le tableau ci-dessous détaille les modules Apache recommandés pour HealthShare. Tout module qui ne figure pas dans la liste ci-dessous peut être désactivé : | Nom du module | Description | | ----------- | --------------------------------------------------------------------------------------------- | | alias | Mappage des différentes parties du système de fichiers de l'hôte dans l'arborescence du document et pour la redirection des URL. | | authz_host | Fournit un contrôle d'accès basé sur le nom d'hôte du client, l'adresse IP. | | dir | Permet de rediriger les barres obliques et de servir les fichiers d'index des répertoires. | | headers | Pour contrôler et modifier les en-têtes de demande et de réponse HTTP | | log_config | Journalisation des requêtes adressées au serveur. | | mime | Associe les extensions du nom de fichier demandé avec le comportement et le contenu du fichier| | negotiation | Permet de sélectionner le contenu du document qui correspond le mieux aux capacités du client.| | setenvif | Permet de définir des variables d'environnement en fonction des caractéristiques de la demande| | status | Affiche l'état du serveur et les statistiques de performance | ### Désactivation des modules Les modules inutiles doivent être désactivés pour renforcer la configuration qui réduira les vulnérabilités de sécurité. Le client est responsable de la politique de sécurité du serveur web. Au minimum, les modules suivants doivent être désactivés. | Nom du module | Description | | ---------- | -------------------------------------------------------------------------------------------------------------------- | | asis | Envoie des fichiers qui contiennent leurs propres titres HTTP | | autoindex | Génère des indices de répertoire et affiche la liste des répertoires lorsqu'aucun fichier index.html n'est présent | | env | Modifie la variable d'environnement transmise aux scripts CGI et aux pages SSI | | cgi | cgi - Exécution de scripts CGI | | actions | Exécution de scripts CGI en fonction du type de média ou de la méthode de demande, déclenchement d'actions sur les demandes | | include | Documents HTML analysés par le serveur (Server Side includes) | | filter | Filtrage intelligent des demandes | | version | Gestion des informations de version dans les fichiers de configuration à l'aide de IfVersion | | userdir | Mappage des requêtes vers des répertoires spécifiques à l'utilisateur. Par exemple, ~nom d'utilisateur dans l'URL sera traduit en un répertoire dans le serveur | ## **Apache SSL/TLS** {#ApacheWebServer-ApacheSSL/TLS} Pour protéger les données en transit, assurer la confidentialité et l'authentification, InterSystems recommande que toutes les communications TCP/IP entre les serveurs et les clients de HealthShare soient cryptées avec SSL/TLS, et InterSystems recommande également d'utiliser HTTPS pour toutes les communications entre le navigateur client des utilisateurs et la couche serveur web de l'architecture proposée.   Veillez à consulter les politiques de sécurité de votre organisation pour vous assurer de la conformité à toute exigence de sécurité spécifique de votre organisation.  Le client est responsable de la fourniture et de la gestion des certificats SSL/TLS. Si vous utilisez des certificats SSL, ajoutez le module ssl\_ (mod\_ssl.so). ## **Paramètres supplémentaires de durcissement d'Apache** {#ApacheWebServer-AdditionalHardeningApacheParameters} Pour renforcer la configuration d'Apache, apportez les modifications suivantes au fichier httpd.conf : * TraceEnable doit être désactivé pour éviter les problèmes potentiels de traçage intersites. ![](/sites/default/files/inline/images/images/Apache4.png) * ServerSignature doit être désactivé afin que la version du serveur web ne soit pas affichée. ![](/sites/default/files/inline/images/images/Apache5.png) ## Paramètres de configuration supplémentaires d'Apache {#ApacheWebServer-SupplementalApacheConfigurationParameters} ### Keep-Alive {#ApacheWebServer-Keep-Alive} Le paramètre Apache Keep-Alive permet d'utiliser la même connexion TCP pour la communication HTTP au lieu d'ouvrir une nouvelle connexion pour chaque nouvelle demande, c'est-à-dire que Keep-Alive maintient une connexion persistante entre le client et le serveur. Lorsque l'option Keep-Alive est activée, l'amélioration des performances provient de la réduction de l'encombrement du réseau, de la réduction de la latence des requêtes ultérieures et de la diminution de l'utilisation du CPU causée par l'ouverture simultanée de connexions. L'option Keep-Alive est activée par défaut et la norme HTTP v1.1 stipule qu'elle doit être présumée activée. Cependant, l'activation de la fonction Keep-Alive présente des inconvénients : Internet Explorer doit être IE10 ou supérieur pour éviter les problèmes de délai d'attente connus avec les anciennes versions d'IE. De même, les intermédiaires tels que les pare-feu, les équilibreurs de charge et les proxies peuvent interférer avec les "connexions TCP persistantes" et entraîner une fermeture inattendue des connexions.   Lorsque vous activez la fonction "Keep-Alive", vous devez également définir le délai d'attente de cette fonction. Le délai d'attente par défaut pour Apache est trop faible et doit être augmenté pour la plupart des configurations, car des problèmes peuvent survenir en cas de rupture des requêtes AJAX (c'est-à-dire hyperevent). Ces problèmes peuvent être évités en s'assurant que le délai d'attente du serveur est supérieur à celui du client.  En d'autres termes, c'est le client, et non le serveur, qui devrait fermer la connexion.  Des problèmes surviennent - principalement dans IE mais dans une moindre mesure dans d'autres navigateurs - lorsque le navigateur tente d'utiliser une connexion (en particulier pour un POST) dont il s'attend à ce qu'elle soit ouverte.  Voir ci-dessous les valeurs recommandées de KeepAlive et KeepAliveTimeout pour un serveur Web HealthShare. Pour activer KeepAlive dans Apache, apportez les modifications suivantes au fichier httpd.conf : ![](/sites/default/files/inline/images/images/Apache6.png) ### Psserelle CSP {#ApacheWebServer-CSPGateway} Pour le paramètre CSP Gateway KeepAlive, laissez la valeur par défaut No Action car le statut KeepAlive est déterminé par les titres de la réponse HTTP pour chaque requête.
Article
Guillaume Rongier · Avr 6, 2022

Les globales sont des épées magiques pour stocker des données. Listes éparses (partie 3)

![](/sites/default/files/inline/images/sworld3.jpg) Dans les parties précédentes ([1](https://community.intersystems.com/post/globals-are-magic-swords-managing-data-part-1) et [2](https://community.intersystems.com/post/globals-magic-swords-storing-data-trees-part-2)) nous avons parlé des globales en tant qu'arbres. Dans cet article, nous allons les considérer comme des listes éparses. [Une liste éparse](https://en.wikipedia.org/wiki/Sparse_matrix) - est un type de liste où la plupart des valeurs ont une valeur identique. En pratique, vous verrez souvent des listes éparses si volumineuses qu'il est inutile d'occuper la mémoire avec des éléments identiques. Il est donc judicieux d'organiser les listes éparses de telle sorte que la mémoire ne soit pas gaspillée pour stocker des valeurs en double. Dans certains langages de programmation, les listes éparses font partie intégrante du langage - par exemple, [in J](http://www.jsoftware.com/help/dictionary/d211.htm), [MATLAB](http://www.mathworks.com/help/matlab/ref/sparse.html). Dans d'autres langages, il existe des bibliothèques spéciales qui vous permettent de les utiliser. Pour le C++, [il s'agit de Eigen](http://eigen.tuxfamily.org/dox-devel/GettingStarted.html) et d'autres bibliothèques de ce type. Les globales sont de bons candidats pour la mise en œuvre de listes éparses pour les raisons suivantes : 1. Ils stockent uniquement les valeurs de nœuds particuliers et ne stockent pas les valeurs indéfinies ; 2. L'interface d'accès à une valeur de nœud est extrêmement similaire à ce que de nombreux langages de programmation proposent pour accéder à un élément d'une liste multidimensionnelle.   Set ^a(1, 2, 3)=5 Write ^a(1, 2, 3) 3. Une structure globale est une structure de niveau assez bas pour le stockage des données, ce qui explique pourquoi les globales possèdent des caractéristiques de performance exceptionnelles (des centaines de milliers à des dizaines de millions de transactions par seconde selon le matériel, voir [1](http://www.intersystems.com/library/library-item/data-scalability-with-intersystems-cache-and-intel-processors/)) > Puisqu'une globale est une structure persistante, il n'est logique de créer des listes éparses sur leur base que dans les situations où vous savez à l'avance que vous disposerez de suffisamment de mémoire pour elles.   L'une des nuances de la mise en œuvre des listes éparses est le retour d'une certaine valeur par défaut si vous vous adressez à un élément indéfini. Ceci peut être mis en œuvre en utilisant la fonction [$GET](http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fget) dans COS. Prenons l'exemple d'une liste tridimensionnelle. SET a = $GET(^a(x,y,z), defValue) Quel type de tâches nécessite des listes éparses et comment les globales peuvent-elles vous aider ? ## Matrice d'adjacence [Ces matrices](https://en.wikipedia.org/wiki/Adjacency_matrix) sont utilisées pour la représentation des graphiques : ![](/sites/default/files/inline/images/graph_vs_matrix.png) Il est évident que plus un graphe est grand, plus il y aura de zéros dans la matrice. Si nous regardons le graphe d'un réseau social, par exemple, et que nous le représentons sous la forme d'une matrice de ce type, il sera principalement constitué de zéros, c'est-à-dire qu'il s'agira d'une liste éparse. Set ^m(id1, id2) = 1 Set ^m(id1, id3) = 1 Set ^m(id1, id4) = 1 Set ^m(id1) = 3 Set ^m(id2, id4) = 1 Set ^m(id2, id5) = 1 Set ^m(id2) = 2 .... Dans cet exemple, nous allons sauvegarder la matrice d'adjacence dans le **^m** globale, ainsi que le nombre d'arêtes de chaque nœud (qui est ami et avec qui et le nombre d'amis). Si le nombre d'éléments du graphique ne dépasse pas 29 millions (ce nombre est calculé comme 8 * [longueur maximale de la chaîne](http://docs.intersystems.com/cache201512/csp/docbook/DocBook.UI.Page.cls?KEY=GORIENT_appx_limits_long_string)), il existe même une méthode plus économique pour stocker de telles matrices - les chaines binaires, car elles optimisent les grands espaces d'une manière spéciale. Les manipulations de chaînes binaires sont effectuées à l'aide de la fonction [$BIT](http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fbit). ; setting a bit SET $BIT(rowID, positionID) = 1 ; getting a bit Write $BIT(rowID, positionID) ## Tableau des commutateurs FSM Le graphe des commutateurs FSM étant un graphe régulier, le tableau des commutateurs FSM est essentiellement la même matrice d'adjacence dont nous avons parlé précédemment. ## Automates cellulaires ![](https://habrastorage.org/files/d6c/33d/067/d6c33d0672f240efb97bbea48cdc9498.gif) L'automate cellulaire le plus célèbre est [le jeu "Life"](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life), dont les règles (lorsqu'une cellule a de nombreux voisins, elle meurt) en font essentiellement une liste éparse. Stephen Wolfram estime que les automates cellulaires représentent un [nouveau domaine de la science](https://en.wikipedia.org/wiki/A_New_Kind_of_Science). En 2002, il a publié un livre de 1280 pages intitulé "A New Kind of Science", dans lequel il affirme que les réalisations dans le domaine des automates cellulaires ne sont pas isolées, mais sont plutôt stables et importantes pour tous les domaines de la science. Il a été prouvé que tout algorithme qui peut être traité par un ordinateur peut également être mis en œuvre à l'aide d'un automate cellulaire. Les automates cellulaires sont utilisés pour simuler des environnements et des systèmes dynamiques, pour résoudre des problèmes algorithmiques et à d'autres fins. Si nous avons un champ considérable et que nous devons enregistrer tous les états intermédiaires d'un automate cellulaire, il est logique d'utiliser les globales. ## Cartographie La première chose qui me vient à l'esprit lorsqu'il s'agit d'utiliser des listes éparses est la cartographie. En règle générale, les cartes comportent beaucoup d'espace vide. Si nous imaginons que la carte du monde est composée de grands pixels, nous verrons que 71 % de tous les pixels de la Terre seront occupés par le réseau creux de l'océan. Et si nous ajoutons uniquement des structures artificielles à la carte, il y aura plus de 95 % d'espace vide. Bien sûr, personne ne stocke les cartes sous forme de tableaux bitmap, tout le monde utilise plutôt la représentation vectorielle. Mais en quoi consistent les cartes vectorielles ? C'est une sorte de cadre avec des polylignes et des polygones. En fait, il s'agit d'une base de données de points et de relations entre eux. L'une des tâches les plus difficiles en cartographie est la création d'une carte de notre galaxie réalisée par le télescope Gaia. Au sens figuré, notre galaxie est un gigantesque réseau creux : d'immenses espaces vides avec quelques points lumineux occasionnels - des étoiles. C'est 99,999999.......% d'espace absolument vide. Caché, une base de données basée sur des globales, a été choisie pour stocker la carte de notre galaxie. Je ne connais pas la structure exacte des globales dans ce projet, mais je peux supposer que c'est quelque chose comme ça : Set ^galaxy(b, l, d) = 1; le numéro de catalogue de l'étoile, s'il existe Set ^galaxy(b, l, d, "name") = "Sun" Set ^galaxy(b, l, d, "type") = "normal" ; les autres options peuvent inclure un trou noir, quazar, red_dwarf et autres. Set ^galaxy(b, l, d, "weight") = 14E50 Set ^galaxy(b, l, d, "planetes") = 7 Set ^galaxy(b, l, d, "planetes", 1) = "Mercure" Set ^galaxy(b, l, d, "planetes", 1, weight) = 1E20 ... _Où b, l, d représentent_ _[coordonnées galactiques](https://en.wikipedia.org/wiki/Galactic_coordinate_system): la latitude, la longitude et la distance par rapport au Soleil._ La structure flexible des globales vous permet de stocker toutes les caractéristiques des étoiles et des planètes, puisque les bases de données basées sur les globales sont exemptes de schéma. Caché a été choisi pour stocker la carte de notre univers non seulement en raison de sa flexibilité, mais aussi grâce à sa capacité à sauvegarder rapidement un fil de données tout en créant simultanément des globales d'index pour une recherche rapide. Si nous revenons à la Terre, les globales ont été utilisées dans des projets axés sur les cartes comme [OpenStreetMap XAPI](http://wiki.openstreetmap.org/wiki/Xapi) et FOSM, un branchement d'OpenStreetMap. Récemment, lors d'un hackathon Caché, un groupe de développeurs a mis en œuvre des [index géospatiaux](https://github.com/intersystems-ru/spatialindex) en utilisant cette technologie. Pour plus de détails, consultez [l'article](https://community.intersystems.com/post/creating-custom-index-type-cach%C3%A9). ### Mise en œuvre d'index géospatiaux à l'aide de globales dans OpenStreetMap XAPI [Les illustrations sont tirées de cette présentation](http://www.slideshare.net/george.james/fosdem-2010-gtm-and-openstreetmap). Le globe entier est divisé en carrés, puis en sous-carrés, puis en encore plus de sous-carrés, et ainsi de suite. Au final, nous obtenons une structure hiérarchique pour laquelle les globales ont été créées. ![](/sites/default/files/inline/images/global_world_map.gif) À tout moment, nous pouvons instantanément demander n'importe quelle case ou la vider, et toutes les sous-carrés seront également retournées ou vidées. Un schéma détaillé basé sur les globales peut être mis en œuvre de plusieurs façons. Variante 1: Set ^m(a, b, a, c, d, a, b,c, d, a, b, a, c, d, a, b,c, d, a, 1) = idPointOne Set ^m(a, b, a, c, d, a, b,c, d, a, b, a, c, d, a, b,c, d, a, 2) = idPointTwo ... Variante 2: Set ^m('abacdabcdabacdabcda', 1) = idPointOne Set ^m('abacdabcdabacdabcda', 2) = idPointTwo ... Dans les deux cas, il ne sera pas très difficile dans COS/M de demander des points situés dans un carré de n'importe quel niveau. Il sera un peu plus facile de dégager des segments d'espace carrés de n'importe quel niveau dans la première variante, mais cela est rarement nécessaire. Un exemple de carré de bas niveau : ![](/sites/default/files/inline/images/city_square.png) Et voici quelques globales du projet XAPI : représentation d'un index basé sur des globales : ![](/sites/default/files/inline/images/xapi_example.png) La globale **^voie** est utilisé pour stocker les sommets des [polylines](https://en.wikipedia.org/wiki/Polygonal_chain) (routes, petites rivières, etc.) et des polygones (zones fermées : bâtiments, bois, etc.). ## Une classification approximative de l'utilisation des listes éparses dans les globales. 1. Nous stockons les coordonnées de certains objets et leur état (cartographie, automates cellulaires). 2. Nous stockons des matrices creuses. Dans la variante 2), lorsqu'une certaine coordonnée est demandée et qu'aucune valeur n'est attribuée à un élément, nous devons obtenir la valeur par défaut de l'élément de la liste éparse. ## Les avantages que nous obtenons en stockant des matrices multidimensionnelles dans les globales **Suppression et/ou sélection rapide de segments d'espace qui sont des multiples de chaînes, de surfaces, de cubes, etc.** Pour les cas avec des index intégraux, il peut être pratique de pouvoir supprimer et/ou sélectionner rapidement des segments d'espace qui sont des multiples de chaînes, de surfaces, de cubes, etc. La commande [Kill](http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_ckill) permet de supprimer un élément autonome, une chaîne de caractères et même une surface entière. Grâce aux propriétés de la globale, elle se produit très rapidement, mille fois plus vite que la suppression élément par élément. L'illustration montre un tableau tridimensionnel dans la globale **^a** et différents types d'enlèvements. ![](/sites/default/files/inline/images/cubs.png) Pour sélectionner des segments d'espace par des indices connus, vous pouvez utiliser la commande [Merge](http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_cmerge). Sélection d'une colonne de la matrice dans la colonne Variable : ; Définissons un tableau tridimensionnel creux 3x3x3 Set ^a(0,0,0)=1,^a(2,2,0)=1,^a(2,0,1)=1,^a(0,2,1)=1,^a(2,2,2)=1,^a(2,1,2)=1 Colonne de fusion = ^a(2,2) ; Produisons la colonne Variable Zwrite colonne Produit : Column(0)=1 Colonne(2)=1 Ce qui est intéressant, c'est que nous avons obtenu un tableau épars dans la colonne Variable que vous pouvez adresser via [$GET](http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fget) puisque les valeurs par défaut ne sont pas stockées ici. La sélection de segments d'espace peut également se faire à l'aide d'un petit programme utilisant la fonction [$Order](http://docs.intersystems.com/cache20152/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_forder). Ceci est particulièrement utile pour les espaces dont les indices ne sont pas quantifiés (cartographie). ## Conclusion Les réalités d'aujourd'hui posent de nouveaux défis. Les graphes peuvent comporter des milliards de sommets, les cartes peuvent avoir des milliards de points, certains peuvent même vouloir lancer leur propre univers basé sur des automates cellulaires ([1](http://lenta.ru/news/2009/08/28/universe/), [2](http://www.worldscientific.com/worldscibooks/10.1142/4702)). Lorsque le volume de données dans les listes éparses ne peut pas être comprimé dans la RAM, mais que vous devez quand même travailler avec elles, vous devriez envisager de mettre en œuvre de tels projets en utilisant des globales et des COS. Clause de non-responsabilité :: cet article et les commentaires le concernant reflètent uniquement mon opinion et n'ont rien à voir avec la position officielle de la société d'InterSystems.
Article
Irène Mykhailova · Juin 7, 2022

Méthodes utiles générées automatiquement

Pour chaque propriété, requête ou index défini, plusieurs méthodes correspondantes seraient automatiquement générées lors de la compilation d'une classe. Ces méthodes peuvent être très utiles. Dans cet article, je décrirai certaines d'entre elles. Properties Disons que vous avez défini une propriété nommée "Property". Les méthodes suivantes seraient automatiquement disponibles (la propriété indiquée en gras est une partie variable, égale au nom de la propriété) : ClassMethod PropertyGetStored(id) Pour les propriétés de type de données, cette méthode renvoie leur valeur logique, pour les propriétés d'objet, elle renvoie l'id. C'est une référence globale wrappée à la globale de données de la classe et le moyen le plus rapide de récupérer la valeur de la propriété singulière. Cette méthode n'est disponible que pour les propriétés stockées. Method PropertyGet() C'est un getter de propriété. Peut être redéfini. Method PropertySet(val) As %Status C'est un définisseur de propriété. Peut être redéfini. Propriétés de l'objet S'il s'agit d'une propriété objet, certaines méthodes supplémentaires, liées à l'accès aux ID et OID, deviennent disponibles : Method PropertySetObjectId(id) Cette méthode définit la valeur de la propriété par ID, il n'est donc pas nécessaire d'ouvrir un objet pour le définir comme valeur de la propriété. Method PropertyGetObjectId() Cette méthode renvoie l'ID de la valeur de la propriété. Method PropertySetObject(oid) Cette méthode définit la valeur de la propriété par OID. Method PropertyGetObject() Cette méthode renvoie l'OID de la valeur de la propriété. Propriétés du type de données Pour une propriété de type de données, plusieurs autres méthodes de conversion entre différents formats sont disponibles : ClassMethod PropertyDisplayToLogical(val) ClassMethod PropertyLogicalToDisplay(val) ClassMethod PropertyOdbcToLogical(val) ClassMethod PropertyLogicalToOdbc(val) ClassMethod PropertyXSDToLogical(val) ClassMethod PropertyLogicalToXSD(val) ClassMethod PropertyIsValid(val) As %Status Vérification de la validité de la valeur de la propriété ClassMethod PropertyNormalize(val) Renvoi de la valeur logique normalisée **Remarques** * Les relations constituent des propriétés et peuvent être obtenues ou définies à l'aide des méthodes suivantes L'entrée val est toujours une valeur logique, sauf pour les méthodes de conversion de format. Index Pour un index nommé "Index", les méthodes suivantes seraient automatiquement disponibles ClassMethod IndexExists(val) As %Boolean Renvoi de 1 ou 0 selon l'existence d'un objet avec ce val, où val est une valeur logique de la propriété indexée. Index uniques Pour les index uniques, des méthodes supplémentaires sont disponibles : ClassMethod IndexExists(val, Output id) As %Boolean Renvoi de 1 ou 0 en fonction de l'existence d'un objet avec ce val, où val est une valeur logique de la propriété indexée. Renvoi également de l'identifiant de l'objet (s'il a été trouvé) comme second argument. ClassMethod IndexDelete(val, concurrency = -1) As %Status Suppression de l'entrée dont la valeur d'index est égale à val. ClassMethod IndexOpen(val, concurrency, sc As %Status) Renvoi de l'objet existant dont l'indice est égal à val. **Remarques:** a) Étant donné qu'un index peut être basé sur plusieurs propriétés, la signature de la méthode serait modifiée pour avoir plusieurs valeurs en entrée, par exemple, considérez cet index : Index MyIndex On (Prop1, Prop2); La méthode IndexExists aurait alors la signature suivante : ClassMethod IndexExists(val1, val2) As %Boolean Où val1 correspond à la valeur de Prop1 et val2 correspond à la valeur de Prop2. Les autres méthodes suivent la même logique. b) Caché génère un index IDKEY qui indexe le champ ID (RowID). Il peut être redéfini par l'utilisateur et peut également contenir plusieurs propriétés. Par exemple, pour vérifier si une classe a une propriété définie, exécutez : Write ##class(%Dictionary.PropertyDefinition).IDKEYExists(class, property) c) Toutes les méthodes d'indexation vérifient la présence d'une valeur logique d) Documentation Requêtes En ce qui concerne une requête (qui peut être une simple requête SQL ou une requête de classe personnalisée, voici mon [post](https://community.intersystems.com/post/class-queries-intersystems-cache%CC%81) à ce sujet) nommée "Query", la méthode Func est générée : ClassMethod QueryFunc(Arg1, Arg2) As %SQL.StatementResult qui renvoie un %SQL.StatementResult utilisé pour itérer sur la requête. Par exemple, la classe Sample.Person de l'espace de noms Samples possède une requête ByName acceptant un paramètre. Elle peut être appelée depuis le contexte objet au moyen de ce code : Set ResultSet=##class(Sample.Person).ByNameFunc("A") While ResultSet.%Next() { Write ResultSet.Name,! } En outre, une classe de démonstration sur [GitHub](https://github.com/eduard93/Utils/blob/master/Utils/GeneratedMethods.cls.xml) illustre ces méthodes.
Article
Kevin Koloska · Nov 30, 2022

Suite de tests d’E/S PerfTools

Suite de tests d’E/S PerfTools #Analytics #Caché #HealthShare #InterSystems IRIS #Open Exchange #TrakCare But Cette paire d’outils (RanRead et RanWrite) est utilisée pour générer des événements de lecture et d’écriture aléatoires dans une base de données (ou une paire de bases de données) afin de tester les opérations d’entrée/sortie par seconde (IOPS). Ils peuvent être utilisés conjointement ou séparément pour tester la capacité matérielle des E/S, valider les IOPS cibles et garantir des temps de réponse disque acceptables. Les résultats recueillis à partir des tests d’E/S varient d’une configuration à l’autre en fonction du sous-système d’E/S. Avant d’exécuter ces tests, assurez-vous que la surveillance correspondante du système d’exploitation et du niveau de stockage est configurée pour capturer les mesures de performances des E/S en vue d’une analyse ultérieure. La méthode suggérée consiste à exécuter l’outil Performance du système fourni avec IRIS. Veuillez noter qu’il s’agit d’une mise à jour d’une version précédente, qui peut être trouvée ici. Installation Téléchargez les outils PerfTools.RanRead.xml et PerfTools.RanWrite.xml depuis GitHub ici. Importez des outils dans l’espace de noms USER. UTILISATEUR> faire $system. OBJ. Load(« /tmp/PerfTools.RanRead.xml »,"ckf ») UTILISATEUR> faire $system. OBJ. Load(« /tmp/PerfTools.RanWrite.xml »,"ckf ») Exécutez la méthode Help pour afficher tous les points d’entrée. Toutes les commandes sont exécutées dans USER. USER> do ##class(PerfTools.RanRead). Aide() • do ##class(PerfTools.RanRead). Setup(Répertoire,DatabaseName,SizeGB,LogLevel) Crée une base de données et un espace de noms portant le même nom. Le niveau logarithmique doit être compris entre 0 et 3, où 0 correspond à « aucun » et 3 à « verbeux ». • do ##class(PerfTools.RanRead). Exécuter(Répertoire,Processus,Dénombrement,Mode) Exécutez le test d’E/S en lecture aléatoire. Le mode est 1 (par défaut) pour le temps en secondes, 2 pour les itérations, en référence au paramètre Count précédent • do ##class(PerfTools.RanRead). Stop() Termine toutes les tâches en arrière-plan. • do ##class(PerfTools.RanRead). Reset() Supprime les statistiques des exécutions précédentes. Ceci est important pour l’exécution entre les tests, sinon les statistiques des exécutions précédentes sont moyennées dans la version actuelle. • do ##class(PerfTools.RanRead). Purger(Répertoire) Supprime l’espace de noms et la base de données du même nom. • do ##class(PerfTools.RanRead). Export(Répertoire) Exporte un résumé de tous les tests de lecture aléatoire dans un fichier texte délimité par des virgules. USER> do ##class(PerfTools.RanWrite). Aide() • do ##class(PerfTools.RanWrite). Setup(Répertoire,NomBase de données) Crée une base de données et un espace de noms portant le même nom. • do ##class(PerfTools.RanWrite). Run(Répertoire,NumProcs,RunTime,HangTime,HangVariationPct,Longueur du nom global,Profondeur globale du nœud,Longueur globale du sous-nœud) Exécutez le test d’E/S en écriture aléatoire. Tous les paramètres autres que le répertoire ont des valeurs par défaut. • do ##class(PerfTools.RanWrite). Stop() Termine toutes les tâches en arrière-plan. • do ##class(PerfTools.RanWrite). Reset() Supprime les statistiques des exécutions précédentes. • do ##class(PerfTools.RanWrite). Purger(Répertoire) Supprime l’espace de noms et la base de données du même nom. • do ##class(PerfTools.RanWrite). Export(Directory) Exporte un résumé de tout l’historique des tests d’écriture aléatoire dans un fichier texte délimité par des virgules. Coup monté Créez une base de données vide (pré-développée) appelée RAN au moins deux fois la taille de la mémoire de l’hôte physique à tester. Assurez-vous que la taille du cache du contrôleur de stockage de la base de données vide est au moins quatre fois supérieure à celle du contrôleur de stockage. La base de données doit être plus grande que la mémoire pour garantir que les lectures ne sont pas mises en cache dans le cache du système de fichiers. Vous pouvez créer manuellement ou utiliser la méthode suivante pour créer automatiquement un espace de noms et une base de données. USER> do ##class(PerfTools.RanRead). Configuration(« /ISC/tests/TMP »,"RAN »,200,1) Répertoire créé /ISC/tests/TMP/ Création d’une base de données de 200 Go dans /ISC/tests/TMP/ Base de données créée dans /ISC/tests/TMP/ Remarque : On peut utiliser la même base de données pour RanRead et RanWrite, ou utiliser des bases séparées si l’intention de tester plusieurs disques à la fois ou à des fins spécifiques. Le code RanRead permet de spécifier la taille de la base de données, mais pas le code RanWrite, il est donc probablement préférable d’utiliser la commande RanRead Setup pour créer des bases de données prédimensionnées souhaitées, même si l’on utilisera la base de données avec RanWrite. Méthodologie Commencez avec un petit nombre de processus et des temps d’exécution de 30 à 60 secondes. Ensuite, augmentez le nombre de processus, par exemple commencez à 10 tâches et augmentez de 10, 20, 40, etc. Continuez à exécuter les tests individuels jusqu’à ce que le temps de réponse soit constamment supérieur à 10 ms ou que les IOPS calculées n’augmentent plus de manière linéaire. L’outil utilise la commande ObjectScript VIEW qui lit les blocs de base de données en mémoire , donc si vous n’obtenez pas les résultats attendus, tous les blocs de base de données sont peut-être déjà en mémoire. À titre indicatif, les temps de réponse suivants pour les lectures aléatoires de base de données de 8 Ko et 64 Ko (non mises en cache) sont généralement acceptables pour les baies entièrement flash : • Moyenne do ##class(PerfTools.RanWrite). Exécuter(« /ISC/tests/TMP »,1,60,.001) Processus RanWrite 11742 créant 1 processus de travail en arrière-plan. Préparé RanWriteJob 12100 pour parent 11742 Démarrer 1 processus pour le numéro de tâche RanWrite 11742 maintenant! Pour terminer l’exécution : faites ##class(PerfTools.RanWrite). Stop() En attente de finir.................. Travaux d’écriture aléatoire en arrière-plan terminés pour le parent 11742 Les processus 1 de la tâche RanWrite 11742 (60 secondes) avaient un temps de réponse moyen = 0,912 ms IOPS calculé pour la tâche RanWrite 11742 = 1096 Les autres paramètres de RanWrite peuvent généralement être laissés à leurs valeurs par défaut en dehors de circonstances inhabituelles. Ces paramètres sont les suivants : - HangVariationPct : variance sur le paramètre hangtime, utilisée pour imiter l’incertitude ; c’est un pourcentage du paramètre précédent - Longueur globale du nom : RanWrite choisit aléatoirement un nom global, et c’est la longueur de ce nom. Par exemple, s’il est défini sur 6, le Global peut ressembler à Xr6opg- Profondeur du nœud global et Longueur du sous-nœud global : le Global supérieur n’est pas celui qui est rempli. Ce qui est réellement rempli, ce sont des sous-nœuds, donc définir ces valeurs sur 2 et 4 donnerait une commande comme « set ^Xr6opg(« drb7 »,"xt8v ») = [value] ». Le but de ces deux paramètres et de la longueur du nom global est de s’assurer que le même global n’est pas défini encore et encore, ce qui entraînerait un minimum d’événements d’E/S Pour exécuter RanRead et RanWrite ensemble, au lieu de « do », utilisez la commande « job » pour les deux afin de les exécuter en arrière-plan. Résultats Pour obtenir les résultats simples pour chaque exécution enregistrés dans USER dans la table SQL PerfTools.RanRead et PerfTools.RanWrite, utilisez la commande Export pour chaque outil comme suit. Pour exporter le jeu de résultats dans un fichier texte délimité par des virgules (csv), exécutez la commande suivante : USER> do ##class(PerfTools.RanRead). Exporter(« /ISC/tests/TMP/ « ) Exportation du résumé de toutes les statistiques de lecture aléatoire vers /usr/iris/db/zranread/PerfToolsRanRead_20221023-1408.txt Terminé. Analyse Il est recommandé d’utiliser l’outil SystemPerformance intégré pour acquérir une véritable compréhension du système analysé. Les commandes de SystemPerformance doivent être exécutées dans l’espace de noms %SYS. Pour passer à cela, utilisez la commande ZN: UTILISATEUR> ZN « %SYS » Pour trouver des détails sur les goulots d’étranglement dans un système, ou si l’on a besoin de plus de détails sur la façon dont il fonctionne à ses IOPS cibles, il faut créer un profil SystemPerformance avec une acquisition de données à cadence élevée : %SYS> set rc=$$addprofile^SystemPerformance(« 5minhighdef »,"Un échantillonnage d’exécution de 5 minutes toutes les secondes »,1 300) Ensuite, exécutez ce profil (à partir de %SYS) et revenez immédiatement à USER et démarrez RanRead et/ou RanWrite en utilisant « job » plutôt que « do »: %SYS>set runid=$$run^SystemPerformance(« 5minhighdef ») %SYS> ZN « UTILISATEUR » USER> job ##class(PerfTools.RanRead). Exécuter(« /ISC/tests/TMP »,5,60) USER> job ##class(PerfTools.RanWrite). Exécuter(« /ISC/tests/TMP »,1,60,.001) On peut alors attendre la fin du travail SystemPerformance et analyser le fichier html résultant à l’aide d’outils tels que yaspe. Nettoyer Une fois l’exécution terminée des tests, supprimez l’historique en exécutant : %SYS> do ##class(PerfTools. RanRead). Reset() Vérifiez l’application associée sur InterSystems Open Exchange