Article Lorenzo Scalese · Juin 14, 2024 5m read

Nous concluons cette série d'articles SMART On FHIR avec Auth0 et le référentiel FHIR d'InterSystems IRIS en passant en revue notre application développée en Angular 16.

Rappelons à quoi ressemble l'architecture définie pour notre solution:

Notre application qui servira de front-end correspond à la deuxième colonne et comme vous pouvez le voir, elle sera en charge de deux choses:

  1. Redirigez la demande de connexion vers Auth0 et recevez la réponse.
  2. Envoyez et recevez la réponse des requêtes via REST envoyées au serveur FHIR.

Angular

Angular est un cadre d'application web développé en TypeScript, open source, maintenu par Google, utilisé pour créer et maintenir des applications web mono-page. Cette conception "applications web mono-page" permet de concevoir des applications beaucoup plus dynamiques pour l'utilisateur. Comme nous l'avons déjà expliqué dans le premier article, nous allons utiliser le serveur NGINX comme serveur d'application et reverse proxy qui évitera les problèmes dérivés de CORS en modifiant les en-têtes d'appel pour qu'ils correspondent à ceux du serveur.

Conception de l'application

Nous avons conçu notre application avec Angular Material pour simuler la conception d'une application mobile. Dans notre exemple, l'application est destinée à enregistrer une série de données de patients telles que la fréquence cardiaque, la pression artérielle et le poids et pour cela nous allons envoyer deux types de ressources FHIR à notre serveur, la première sera de type Patient avec laquelle l'utilisateur enregistrera ses données ; La seconde correspondra à la ressource Observation dans laquelle nous enverrons chacun des types de données que nous allons envoyer.

Cette application permettra à l'utilisateur de voir un graphique de l'évolution des données enregistrées.

Écran de connexion

Lorsque l'utilisateur accède à l'itinéraire https:\\localhost, l'écran initial s'affichera à partir duquel on peut demander à se connecter.

 

En cliquant sur le bouton de connexion, l'application redirigera automatiquement l'utilisateur vers la page Auth0 activée pour l'API configurée:

Après avoir introduit notre nom d'utilisateur et notre mot de passe, Auth0 nous demandera d'autoriser l'application à accéder à nos données. Une fois l'accès aux données confirmé, Auth0 nous redirigera vers l'adresse URL que nous avons indiqué lors du processus de configuration. Une fois le jeton d'accès généré, la bibliothèque Auth0 se chargera de l'inclure dans l'en-tête de tous les appels que nous lançons au serveur. On peut le voir dans l'image suivante :

Écran initial

Une fois connecté, la première communication avec notre serveur FHIR permettra de demander les informations disponibles pour l'utilisateur connecté. Pour cela, nous utiliserons une requête par paramètre en envoyant un appel GET du type suivant:

https://localhost:8443/smart/fhir/r5/Patient?email=lperezra%40intersystems.com

La réponse du serveur sera une ressource de type Bundle (paquet) avec les informations suivantes:

{
    "resourceType":"Bundle",
    "id":"8c5b1efd-cfdd-11ee-a06b-0242ac190002",
    "type":"searchset",
    "timestamp":"2024-02-20T10:48:14Z",
    "total":0,
    "link":[
        {
            "relation":"self",
            "url":"https://localhost:8443/smart/fhir/r5/Patient?email=lperezra%40intersystems.com"
        }
    ]
}

Comme nous pouvons le voir, nous avons un total de 0 patients avec cette adresse e-mail, donc notre application nous montrera un écran initial à partir duquel nous pouvons enregistrer nos données.

 

Comme vous pouvez le voir, le champ email est déjà rempli avec l'email de l'utilisateur connecté, car comme vous l'avez vu dans la requête initiale, il va être utilisé comme identifiant. Une fois le formulaire rempli, nous allons envoyer un appel du type suivant via POST:

https://localhost:8443/smart/fhir/r5/Patient

With the message body formed by a Patient resource:

{
    "resourceType":"Patient",
    "birthDate":"1982-03-08",
    "gender":"male",
    "identifier":[
        {
            "type":{
                "text":"ID"
            },
            "value":"12345678A"
        }
    ],
    "name":[
        {
            "family":"PÉREZ RAMOS",
            "given":[
                "LUIS ÁNGEL"
                ]
        }
    ],
    "telecom":[
        {
            "system":"phone",
            "value":"600102030"
        },
        {
            "system":"email",
            "value":"lperezra@intersystems.com"
        }
    ]
}

Les données du patient étant enregistrées sur notre serveur, la requête du patient renvoie maintenant un résultat, ce qui nous permet d'enregistrer les différentes données d'observation. Voyons à quoi ressemble l'écran initial:

Observations screen

De la même manière que nous avons envoyé les données du patient, nous enverrons les résultats d'observation à partir de leurs écrans spécifiques:

Pour chaque ressource envoyée au serveur, l'application ajoute un nouveau point dans le diagramme:

Pour ce faire, elle lancera une requête au serveur demandant les ressources type Observation (résultats d'observation) appartenant à l'utilisateur:

https://localhost/smart/fhir/r5/Observation?patient=Patient/1

Et le serveur retournera à nouveau une ressource type Bundle (paquet) avec tous les résultats d'observations enregistrés pour le patient:

Avec le résultat obtenu, l'application extrait toutes les valeurs numériques et construit les diagrammes pertinents.

Conclusion

Comme vous l'avez vu dans cet article et dans les deux précédents, la conception et la création d'applications SMART On FHIR n'est pas très complexe et nous permet de construire rapidement et de manière agile des applications qui profitent de toutes les fonctionnalités disponibles sur notre serveur FHIR.

Une application de ce type ne requiert pas la mise au point d'un back-end complexe pour gérer les opérations type CRUD sur les données et, grâce à l'utilisation d'OAuth2, il n'est pas nécessaire de gérer les utilisateurs à partir de l'application, cette fonctionnalité étant déléguée à Auth0 ou au serveur d'authentification et d'autorisation choisi par l'utilisateur.

Avec SMART On FHIR, nous pouvons, d'une manière simple et aisée, mettre les outils nécessaires à la gestion des données cliniques à la disposition des patients et des professionnels de la santé.

TMerci infiniment pour votre attention!

0
0 56
Article Lorenzo Scalese · Juin 12, 2024 7m read

Dans l'article précédent, nous avons présenté l'architecture de notre projet SMART On FHIR, il est donc temps de passer aux choses sérieuses et de commencer à configurer tous les éléments qui seront nécessaires.

Nous commençons avec Auth0.

Configuration de l'Auth0

Commençons par créer un compte Auth0 avec un email valide, une fois enregistré il nous faut créer notre première application, et nous le ferons à partir du menu de gauche:

Application menu

Dans notre exemple, l'application sera de type application web monopage car il s'agit d'une application développée dans Angular 16. Nous sélectionnons cette option et cliquons sur Create (Créer).

Single Page Web Application

Dans l'écran suivant, il faut définir les champs suivants:

ATTENTION! Les adresses URL doivent toutes être HTTPS, c'est l'une des exigences pour les connexions OAuth2.

Avec cela, nous avons configuré les adresses URL dont Auth0 a besoin pour rediriger l'utilisateur après le processus d'authentification et d'autorisation. Si vous voyez les URL, elles n'ont pas de port défini, c'est parce qu'avec le déploiement du projet Angular dans Docker via NGINX, nous avons indiqué qu'il sera accessible via le port HTTPS par défaut, 443. Vous pouvez mettre le nom qui vous convient le mieux.

Application configuration

Pour la configuration ultérieure de notre projet Angular, indiquez les valeurs que nous trouvons à la fois dans Domain et Client ID (identifiant de domaine et identifiant de client).

Notre application étant configurée, il est temps de définir l'API qui recevra les requêtes de notre application Angular et nous le ferons à nouveau à partir du menu de gauche:

Cette option nous montrera un nouvel écran pour saisir toutes les données nécessaires:

API configuration

Pour se connecter correctement, il vous faudra définir un identifiant pour l'API qui sera ensuite utilisé par l'application Angular comme "environnement". Comme vous pouvez le voir, il est recommandé de saisir une adresse URL, mais il n'est pas nécessaire que cette adresse soit fonctionnelle, puisqu'elle ne servira que d'identifiant. Dans notre cas, nous pouvons définir:

https://localhost/smart/fhir/r5

Enfin, nous configurons un algorithme de signature RS256 et passons à l'onglet Permissions, où nous définissons le périmètre de FHIR pour les utilisateurs connectés

API permission

Si vous souhaitez approfondir le sujet des contextes FHIR, vous pouvez consulter l'URL de la page officielle en cliquant ici. Pour notre exemple, nous avons défini le périmètre de l'utilisateur/*.* qui permet à l'utilisateur validé d'effectuer des opérations CRUD sur toutes les ressources du serveur FHIR.

Parfait! Nous venons de configurer notre compte Auth0 pour qu'il fonctionne comme un serveur OAuth2.

Configuration de l'application Angular

Je souhaiterais développer l'application en Angular 17, qui introduit pas mal de changements, mais malheureusement la documentation associée à Auth0 et ses bibliothèques ne sont que pour Angular 16, j'ai donc décidé de suivre un chemin facile et je l'ai développée dans la version 16.

Pour configurer notre projet Angular, il suffit d'ouvrir la page app.module.ts et de rechercher le fragment de code suivant:

Examinons la signification de chaque paramètre à configurer:

  • domain: correspond à la valeur Domain générée lors de la création de notre application dans Auth0.
  • clientId: identique à ce qui précède, mais avec la valeur Client ID générée.
  • audience: correspond à l'URL que nous avons configuré comme identifiant de notre API.
  • uri: avec cette valeur, nous indiquons à la bibliothèque TypeScript Auth0 d'intercepter tous les appels que nous faisons aux adresses URL qui contiennent cette URI et d'inclure l'Access_token qu'Auth0 renverra lorsque nous validerons (en ajoutant le paramètre Authorization à l'en-tête de l'appel: Bearer....).

Une fois ces valeurs modifiées, notre application Angular est configurée pour fonctionner avec Auth0. Dans le prochain article, nous verrons plus en détail comment nous invoquerons Auth0 depuis notre interface utilisateur.

Configuration d'InterSystems IRIS for Health

Ce projet est configuré pour installer automatiquement le serveur FHIR pendant le processus de déploiement, afin de nous épargner une étape. Dans notre cas, nous avons défini l'URI/smart/fhir/r5 comme point de terminaison de notre serveur FHIR. Pour ceux d'entre vous qui ne sont pas familiers avec la terminologie FHIR, r5 fait référence à la dernière version de FHIR, disponible depuis des mois sur IRIS.

Pour configurer notre instance IRIS, nous devons d'abord démarrer notre Docker et déployer les 3 conteneurs disponibles dans le projet. Il nous suffira d'exécuter la commande suivante dans le terminal depuis la racine du projet:

docker-compose up -d --build

Cela nous permettra de construire et d'exécuter les conteneurs contenus dans le projet. Pour les utilisateurs de Windows, si vous utilisez Docker Desktop, un écran comme celui-ci devrait s'afficher:

Docker Desktop

Voici nos 3 conteneurs:

  • iris: avec l'instance IRIS dans laquelle se trouve notre serveur FHIR.
  • smart-ui: avec le code de notre application web déployée depuis le serveur NGINX configuré à son tour pour que toutes les connexions se fassent via SSL avec les certificats associés.
  • webgateway: avec son serveur Apache associé (rappelons que depuis la version officielle 2023.1, le serveur Web privé (Private Web Server) a été supprimé, bien qu'il soit encore disponible dans les versions communautaires).

Passerelle Web

Je le répète encore une fois, pour utiliser OAuth2 avec notre serveur FHIR, il est obligatoire que toutes les connexions se fassent par HTTPS, le serveur Apache doit donc être configuré pour n'accepter que les appels de ce type, si vous jetez un œil au fichier /webgateway/shared/CSP.conf vous pouvez voir la section suivante responsable de la configuration du serveur Apache:

# SECTION SSL #
# Activez SSL/TLS (https://) sur le serveur Web Apache.
# L'utilisateur est responsable de fournir des certificats SSL valides.
LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so
LoadModule headers_module /usr/lib/apache2/modules/mod_headers.so
<VirtualHost *:443>
SSLEngine on
SSLCertificateFile "/webgateway-shared/apache_webgateway.cer"
SSLCertificateKeyFile "/webgateway-shared/apache_webgateway.key"
Header add ACCESS-CONTROL-ALLOW-ORIGIN "*"
</VirtualHost>

Vous pouvez voir comment la configuration est faite pour que les appels passent par le port 443, c'est-à-dire que l'adresse URL de notre webgateway serait https://webgateway et que tous les appels que nous lançons depuis notre application web vers notre serveur FHIR devraient être redirigés vers cette adresse URL (webgateway est le masque attribué au réseau du conteneur Docker créé par ce dernier).

Tous les appels au serveur depuis Angular viendront avec l'URL https://localhost/smart/fhir/r5 et le serveur NGINX sera en charge de rediriger ce localhost vers le webgateway. Si vous ouvrez le fichier /smart-ui/nginx.conf, vous pourrez voir la configuration suivante:

 

Dans cette configuration, nous voyons que notre application web écoutera sur le port 443 et que tous les appels qui ont la valeur / dans l'URL seront gérés par l'application Angular, tandis que ceux qui incluent /smart/ seront redirigés vers https://webgateway.

Faites attention avec proxy_set_header, qui sera la valeur qui permettra d'éviter les maux de tête avec CORS. Pour éviter que notre passerelle Web ne rejette les appels provenant d'autres serveurs, nous devons modifier la valeur de l'en-tête Host pour la configurer avec l'adresse de la passerelle Web.

InterSystems IRIS

Maintenant il faut configurer notre IRIS pour qu'il fonctionne avec Auth0, pour cela il faut le configurer en tant que client OAuth2. Pour ce faire, il suffit d'accéder au Portail de gestion avec les identifiants superuser/SYS et d'accéder à l'option System Administration > Security > OAuth 2.0 > Client, puis de cliquer sur Create Server Description et de remplir l'endpoint Issuer avec la valeur Domain que nous avons obtenue au moment de la création de l'application à Auth0 (https://[MY_DOMAIN]/). Soyez prudent! L'adresse URL doit se terminer par"/". Enfin, nous sélectionnons la configuration SSL/TLS et cliquons sur Discover and Save (Découvrir et Sauvegarder):

IRIS client

Notre instance IRIS récupère automatiquement les informations dont elle a besoin auprès d'Auth0.

Issuer endpoint

Il suffit d'ajouter un client au serveur précédemment configuré. En appuyant sur Client Configuration (Configuration client), nous accéderons à un nouvel écran où nous définirons le nom de l'application et du client. Ce nom de client sera celui que nous utiliserons plus tard pour configurer notre serveur FHIR.

Serveur FHIR

The last step to finish the configuration of our project is to tell our FHIR server which OAuth2 client will be used for the connection. Pour accéder à la configuration, nous allons ouvrir le Portail de Gestion et sélectionnez Health > FHIR > Configuration du FHIR > Configuration du serveur et nous allons ouvrir le point de terminaison qui s'affiche à l'écran, nous irons à la fin de celui-ci et cliquerons sur Edit (Modifier). Enfin, nous ajoutons dans le champ de nom du client OAuth OAuth Client Name le nom avec lequel nous avons créé la configuration du client.

FHIR OAuth Configuration

Conclusion

Nous avons déjà configuré notre projet, dans le prochain article nous étudierons comment notre application Angular interagit avec chacun des acteurs.

Je vous remercie de votre attention!

0
0 57
Article Lorenzo Scalese · Juin 10, 2024 4m read

Introduction

J'ai récemment participé à une séance pratique formidablement organisée par @Patrick Jamieson au cours de laquelle une application Angular a été configurée avec un serveur IRIS FHIR en suivant les protocoles définis par SMART On FHIR. J'ai trouvé cela très intéressant et j'ai donc décidé de développer ma propre application Angular et de profiter ainsi de ce que j'ai appris en la publiant au sein de la communauté.

SMART On FHIR

Voyons ce que Google nous dit sur SMART On FHIR:

SMART on FHIR est un standard de données qui permet aux applications d'accéder aux informations contenues dans les systèmes de dossiers médicaux électroniques (DME). Un développeur d'application peut écrire une application unique qui se connecte à n'importe quel système de DME adopté selon ce standard.

Les principaux concepts que nous allons traiter dans SMART On FHIR sont:

  • Authentification et autorisation déléguées par OAuth2 ou OpenID.
  • Gestion des ressources FHIR dans le contexte défini.
  • Communications HTTPS.

Architecture de notre projet

Pour cet exercice, nous avons configuré les éléments suivants à la fois dans Docker et dans le service Auth0:

  • Une application développée en Angular qui servira de front-end, et qui a été développée selon les principes de SMART On FHIR.
  • Serveur web NGINX et serveur proxy inverse qui publiera notre application développée en Angular.
  • Auth0 nous fournira le service d'authentification et d'autorisation via OAuth2.
  • InterSystems IRIS dans lequel nous déploierons notre serveur FHIR et auquel nous nous connecterons via la passerelle Web incluant un serveur Apache déjà disponible dans son image Docker.

Auth0

Bien que nous puissions déléguer l'authentification et l'autorisation des utilisateurs à un autre serveur IRIS déployé à cet effet, nous allons à cette occasion utiliser le service offert par Auth0.

Qu'est-ce que Auth0?

Auth0 est un service qui nous fournit l'ensemble du mécanisme pour gérer l'autorisation et l'authentification de nos plateformes.

Auth0 dispose également de bibliothèques spécifiques dans différentes langues pour pouvoir s'intégrer facilement à n'importe quel projet, c'est donc toujours une option à prendre en compte pour les développements basés sur SMART On FHIR.

Intégration d'Auth0 dans notre application

L'utilisation d'OAuth2 étant une condition requise pour l'utilisation de SMART On FHIR, cela implique l'inclusion d'un serveur OAuth2 dans le processus habituel d'authentification, d'autorisation et d'accès à l'application. Dans le diagramme suivant, nous pouvons voir la route suivie par les informations envoyées au système avec le service Auth0:

Analysons le processus:

  • Login request:
    1. Demande de connexion: L'utilisateur accède à l'application dans son navigateur Internet et demande à se connecter.
    2. Demande de connexion: L'application Angular transmet la demande au service Auth0.
    3. Page de connexion: Auth0 envoie une redirection vers votre propre page au navigateur Internet de l'utilisateur.
  • Authentification sur Auth0:
    1. Informations d'identification de l'utilisateur: L'utilisateur saisit son adresse électronique et le mot de passe avec lequel il est enregistré dans Auth0.
    2. Authentification & Autorisation: Auth0 valide les données et génère un Access_token comprenant le contexte attribué à l'utilisateur.
    3. Réponse & redirection de l'Access_token: Auth0 redirige la réponse vers l'URL indiquée dans la configuration du projet en incluant le jeton généré.
    4. Écran du patient: L'application Angular montre à l'utilisateur la page d'enregistrement de ses données personnelles.
  • Enregistrement des Ressources FHIR:
    1. Enregistrer le patient: l'utilisateur remplit le formulaire avec ses données, et l'application Angular transforme le formulaire en un objet JSON au format de la ressource FHIR Patient.
    2. Requête POST: l'application Angular envoie un appel HTTP POST au serveur FHIR déployé dans IRIS en incluant l'access_token comme jeton d'authentification dans l'en-tête de la requête
    3. Réponse POST: après avoir reçu la demande POST via Web Gateway, IRIS vérifie la validité du jeton et le contexte de la demande. Si tout est correct, il valide la ressource reçue et l'enregistre sur le serveur FHIR, en renvoyant un HTTP 201 indiquant la création de la nouvelle ressource et en joignant l'identifiant attribué à la nouvelle ressource dans un en-tête.
    4. Opération réussie: L'application Angular redirigera l'utilisateur vers l'écran présentant les principales fonctionnalités.

Une fois connecté, la bibliothèque Auth0 intégrée au projet sera chargée d'intercepter toutes les requêtes que nous faisons à notre serveur FHIR pour y inclure le jeton d'accès reçu d'Auth0.

A venir...

Dans les prochains articles, nous allons voir comment il faut configurer chacun des systèmes concernés et enfin comment les connecter à notre application Angular. Pour ceux qui ne peuvent pas attendre, vous pouvez consulter le README.md présent sur le GitHub associé au projet OpenExchange lié à cet article, qui explique en détail comment configurer à la fois Auth0 et InterSystems IRIS.

Awfully Good: Stay Tuned (1992) with John Ritter

0
0 80
Article Lorenzo Scalese · Avr 25, 2024 7m read

En tant que modèle linguistique d'IA, ChatGPT est capable d'effectuer une variété de tâches telles que traduire, écrire des chansons, répondre à des questions de recherche et même générer du code informatique. Avec ses capacités impressionnantes, ChatGPT est rapidement devenu un outil populaire pour diverses applications, des chatbots à la création de contenu.
Mais malgré ses capacités avancées, ChatGPT n'est pas en mesure d'accéder à vos données personnelles. Mais malgré ses capacités avancées, ChatGPT n'est pas en mesure d'accéder à vos données personnelles. Ainsi, dans cet article, je vais démontrer les étapes suivantes pour construire une IA ChatGPT personnalisée en utilisant le LangChain Framework:

  • Étape 1: Chargement du document 

  • Étape 2: Division du document en blocs

  • Étape 3: Utilisation de l'incorporation pour des blocs de données et leur conversion en vecteurs

  • Étape 4: Enregistrement des données dans la base de données de vecteurs

  • Étape 5: Obtention des données (question) de l'utilisateur et leur the intégration

  • Étape 6: Connexion à VectorDB et recherche sémantique

  • Étape 7: Récupération des réponses pertinentes basées sur les requêtes de l'utilisateur et leur envoi au LLM(ChatGPT)

  • Étape 8: Obtention d'une réponse de la part de LLM et renvoi de celle-ci à l'utilisateur

  REMARQUE : Veuillez lire mon article précédent LangChain – Unleashing the full potential of LLMs (LangChain - Libération du plein potentiel des LLM) pour obtenir plus de détails sur LangChain et sur la façon d'obtenir la clé API OpenAI

Alors, commençons
     

Étape 1: Chargement du document 

Tout d'abord, il faut charger le document. Nous allons donc importer PyPDFLoader pour le document PDF 

ClassMethod SavePDF(filePath) [ Language = python ]
{
#pour un fichier PDF, il faut importer PyPDFLoader à partir du framework langchainfrom langchain.document_loaders import PyPDFLoader
# pour un fichier CSV, il faut importer csv_loader# pour un fichier Doc, il faut importer UnstructuredWordDocumentLoader# Pour le document texte, il faut importer TextLoader#importation de l'os pour définir la variable d'environnementimport os
#attribution de la clé API OpenAI à une variable d'environnement 
os.environ['OPENAI_API_KEY'] = "apiKey"#Init du lanceur
loader = PyPDFLoader(filePath)   
#Chargement du document 
documents = loader.load()
return documents
}

Étape 2: Division du document en blocs

Les modèles linguistiques sont souvent limités par la quantité de texte qui peut leur être transmise. Il est donc nécessaire de les diviser en blocs moins volumineux. LangChain fournit plusieurs utilitaires pour ce faire.

L'utilisation d'un séparateur de texte (Text Splitter) peut également contribuer à améliorer les résultats des recherches dans les répertoires de vecteurs, car, par exemple, des blocs moins volumineux ont parfois plus de chances de correspondre à une requête. Tester différentes tailles de blocs (et leur chevauchement) est un exercice intéressant pour adapter les résultats à votre cas d'utilisation.

ClassMethod splitText(documents) [ Language = python ]
{
#Afin de diviser le document, il faut importer RecursiveCharacterTextSplitter du framework Langchain  from langchain.text_splitter import RecursiveCharacterTextSplitter
#Init du séparateur de texte, définition de la taille des blocs (1000) et du chevauchement. = 0
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=0)
#Division du document en plusieurs blocs
texts = text_splitter.split_documents(documents)
return texts
}

Étape 3: Utilisation d'incorporation pour des blocs de données et leur conversion en vecteurs

Les incorporations (embeddings) de texte sont le cœur et l'âme des outils de Large Language Operations.. Techniquement, nous pouvons travailler avec des modèles linguistiques en langage naturel, mais le stockage et l'extraction du langage naturel sont très inefficaces. 

Pour améliorer l'efficacité, il faut transformer les données textuelles en formes vectorielles. Il existe des modèles ML dédiés à la création d'intégrations à partir de textes. Les textes sont convertis en vecteurs multidimensionnels. Une fois ces données incorporées, nous pouvons les regrouper, les trier, les rechercher, etc. Nous pouvons calculer la distance entre deux phrases pour connaître leur degré de parenté. Et le plus intéressant, c'est que ces opérations ne se limitent pas à des mots-clés comme dans les recherches traditionnelles dans les bases de données, mais capturent plutôt la proximité sémantique de deux phrases. Cela rend le système beaucoup plus puissant, grâce à l'apprentissage automatique.
 

Les modèles d'incorporation de texte reçoivent la saisie de texte et renvoient une liste de flottants (embeddings), qui sont la représentation numérique du texte saisi. Les embeddings permettent d'extraire des informations d'un texte. Ces informations peuvent ensuite être utilisées, par exemple, pour calculer les similitudes entre des textes (par exemple, des résumés de films).

Les modèles d'incorporation de texte reçoivent la saisie de texte et renvoient une liste de flottants

    ClassMethod getEmbeddings(query) [ Language = python ]
    {
    #Obtention d'un modèle d'embeddings à partir du framework Langchainfrom langchain.embeddings import OpenAIEmbeddings
    #Definition d'embedding
    embedding = OpenAIEmbeddings()
    return embedding
    }
    

Étape 4: Enregistrement des données dans la base de données de vecteurs

    ClassMethod saveDB(texts,embedding) [ Language = python ]
    {
    #Obtention de la base de données Chroma à partir de langchainfrom langchain.vectorstores import Chroma      
    # Incorporation et stockage des textes# La fourniture d'un répertoire persistant (persist_directory) permet de stocker les embeddings sur le disque# par exemple dans le dossier myData du chemin d'accès à l'application en cours
    persist_directory = "myData"
    vectordb = Chroma.from_documents(documents=texts, embedding=embedding, persist_directory=persist_directory)
    #sauvegarde du document au niveau local
    vectordb.persist()
    vectordb = None
    }
    

Étape 5: Obtention des données (question) de l'utilisateur et leur the intégration

    ClassMethod getVectorData(query) [ Language = python ]
    {
    #REMARQUE: Il faudrait avoir le même embedding utilisée lorsque nous avons sauvegardé des donnéesfrom langchain.embeddings import OpenAIEmbeddings
    #obtention d'embeddings
    embedding = OpenAIEmbeddings()
    #saisie des données de l'utilisateur (paramètre)
    query = query
    #La suite du code...

Étape 6: Connexion à VectorDB et recherche sémantique

#code continue....     from langchain.vectorstores import Chroma
 persist_directory = "myData"## À présent, il est possible de charger la base de données persistante à partir du disque et de l'utiliser comme d'habitude. 
 vectordb = Chroma(persist_directory=persist_directory, embedding_function=embedding)
 return vectordb
 }

Étape 7: Récupération des réponses pertinentes basées sur les requêtes de l'utilisateur et leur envoi au LLM(ChatGPT)

La mémoire de conversation est la façon dont un chatbot peut répondre à de multiples requêtes à la manière d'un chat. Elle assure une conversation cohérente et, sans elle, chaque requête serait traitée comme une entrée totalement indépendante, sans tenir compte des interactions passées.

LLM avec et sans une mémoire de conversation. Les cases bleues représentent les assistances de l'utilisateur et les cases grises représentent les réponses des LLM. Sans mémoire de conversation (à droite), les LLM ne peuvent pas répondre en utilisant la connaissance des interactions précédentes.

La mémoire permet le  Large Language Model (LLM) de se souvenir des interactions précédentes avec l'utilisateur. Par défaut, les LLMs sont  sans état — ce qui signifie que chaque requête entrante est traitée indépendamment des autres interactions. La seule chose qui existe pour un agent sans état est la saisie actuelle, rien d'autre.


Le modèle ConversationalRetrievalChain est un modèle d'IA conversationnelle conçu pour extraire des réponses pertinentes sur la base des requêtes de l'utilisateur. Il fait partie de la technologie de l'équipe de Langchain. Le modèle utilise une approche basée sur la récupération, où il recherche des réponses préexistantes dans une base de données pour trouver la réponse la plus appropriée à une requête donnée. Le modèle est entraîné sur un grand ensemble de conversations pour apprendre les schémas et le contexte afin de fournir des réponses précises et utiles.

ClassMethod retriveResponse(vectordb) [ Language = python ]
{
from langchain.llms import OpenAI
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain
#La mémoire de conversation est la façon dont un chatbot peut répondre à de multiples requêtes.
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
#Le ConversationalRetrievalChain est un modèle d'IA conversationnelle conçu pour extraire des réponses pertinentes sur la base des requêtes de l'utilisateur.
qa = ConversationalRetrievalChain.from_llm(OpenAI(temperature=0), vectordb.as_retriever(), memory=memory)
return qa
}


Étape 8: Obtention d'une réponse de la part de LLM et renvoi de celle-ci à l'utilisateur 

ClassMethod getAnswer(qa) [ Language = python ]
{
#Obtention d'une réponse de la part de LLM et renvoi de celle-ci à l'utilisateur
getAnswer = qa.run(query)
return getAnswer
}

Pour plus de détails et de fonctionnalités, veuillez consulter mon application irisChatGPT 

Vidéo connexe

<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube.com/embed/h9JoqbUKFBk" title="YouTube video player" width="560"></iframe>

Je vous remercie

1
0 313
Article Lorenzo Scalese · Nov 9, 2023 14m read

Salut les devs,

Actuellement, je travaille sur un projet qui requiert une gestion hautement dynamique des événements. Dans le contexte du langage de programmation Java, mon premier choix aurait été d'opter instinctivement pour l'utilisation du "Observer Pattern". Le "Observer Pattern" représente une approche pour gérer les interactions entre les objets en établissant un mécanisme de notification. Il permet à plusieurs observateurs de réagir de manière autonome aux changements d'état d'un sujet, favorisant ainsi la flexibilité et la modularité du code. Si vous n'êtes pas familier avec ce modèle de conception, vous pouvez trouver de plus amples informations à ce sujet sur ceWikipedia

Bien que ce soit naturel et couramment utilisé dans certains langages de programmations comme le Java ou le C++, en ObjectScript pas du tout. 

2
1 147
Article Lorenzo Scalese · Oct 24, 2023 22m read

Notre objectif

Aujourd'hui, nous poursuivons le développement de notre dernier article et présentons des informations sur certaines fonctionnalités que nous avons ajoutées à notre portail. Nous inclurons une petite partie de CSS pour mieux visualiser les données disponibles et les exporter. Enfin, nous examinerons comment ajouter des options de filtrage et de classement. Lorsque vous aurez terminé cet article, vous pourrez afficher une requête simple et complète de manière élégante.

Précédemment, dans "Un portail pour gérer la mémoire réalisé avec Django"...

Il nous faut reprendre là où nous nous sommes arrêtés avant de passer au développement du portail. Nous avons précédemment créé la base du projet sur la ligne de commande avec certaines commandes intégrées de Django telles que startproject. Ensuite, nous avons ajouté les exigences de connexion à la base de données au fichier requirements.txt et ses paramètres au fichier settings.py pour simplifier l'installation. Plus tard, nous avons créé une application de globales "Globals" avec les URL et les chemins appropriés, dirigeant les utilisateurs du projet principal vers les vues qui communiquent avec les données au moyen de modèles. Nous avons également créé un dossier API avec quelques méthodes qui permettent d'obtenir des informations à partir d'une base de données distante. Avec tout ce qui précède, nous avons laissé à la disposition des utilisateurs une option permettant de créer une communication entre différentes instances d'IRIS. Enfin, nous avons créé le fichier index.html pour l'afficher sur le lien index.html to display it on the link http://127.0.0.1:8000/globals/.

À ce stade, l'interface devraient ressembler à l'image ci-dessous.

Cependant, moins précise que l'illustration précédente, l'interface ressemble à la capture d'écran suivante.

Ajout de CSS

Les feuilles de style en cascade (CSS) permettent de transformer ce projet en quelque chose de plus présentable, de plus beau et de plus agréable à travailler. Dans cette section, vous apprendrez à utiliser les feuilles de style en cascade dans votre projet. Nous vous présenterons également quelques concepts de base de ce langage. Cependant, notre objectif principal sera de relier le fichier de styles et le projet.

Tout d'abord, il faut créer un dossier appelé "static" dans le répertoire /globals. Il sera nécessaire pour y stocker tous nos fichiers statiques par la suite. Vous pouvez ajouter le fichier styles.css à ce dossier.

À présent, nous pouvons enfin mettre de l'ordre et de la couleur. Cependant, si vous ne connaissez rien au CSS, ne vous inquiétez pas ! Voici quelques bonnes nouvelles pour vous ! Pour commencer, c'est relativement intuitif. Il suffit d'écrire l'élément et d'ajouter le design entre les crochets. Vous pouvez accéder aux classes après un point, et aux ID après un hashtag. Si vous voulez un conseil de pro, vous pouvez utiliser un astérisque pour ajouter le style à chaque élément du fichier HTML.

Par exemple, si nous ajoutons un identifiant et une classe aux trois derniers paragraphes du fichier index.html, nous pourrons y accéder séparément et y ajouter des couleurs, des arrière-plans et des polices différents.

<p>showing results for {{globals.count}} globals</p><pclass="totalSize">total size: {{sumSize.size__sum}}</p><pid="allocatedSize">total allocated size: {{sumAllocated.allocatedsize__sum}}</p>
* {
    font-family: 'Lucida Sans';
}

p {
    background-color: yellow;
}

.totalSize {
    color: brown;
}

#allocatedSize {
    font-weight: bold;
}
 

 

Selon cet actif, après avoir configuré les connexions appropriées, chaque police affichée sur la page devrait faire partie de la famille Lucida Sans, les trois paragraphes devraient avoir un arrière-plan jaune, le paragraphe avec la classe _totalSize_ devrait être en caractères bruns, et le paragraphe avec l'ID _allocatedSize_ devrait être en caractères gras.

Vous devriez remarquer quelques particularités si vous jouez avec les éléments, en y ajoutant des ID, des classes et des styles. Premièrement, il y a une hiérarchie : les définitions des identifiants remplacent les classes qui, à leur tour, remplacent les styles généraux. Deuxièmement, plusieurs éléments peuvent partager une même classe. De plus, les éléments enfants hériteront généralement du style de leurs parents (certaines choses ne seront cependant pas héritées, mais il s'agit d'un sujet plus avancé que nous n'aborderons pas ici). Si vous le souhaitez, vous pouvez nous faire part, dans la section des commentaires, des comportements que vous avez remarqués et dont vous n'aviez pas connaissance auparavant.

Ensuite, nous devons faire en sorte que le projet puisse comprendre ce qu'est un fichier de style et où le trouver. Ensuite, nous pouvons obtenir le fichier HTML faisant référence au fichier _styles.css_.

Tout d'abord, accédez à _settings.py_, recherchez la variable STATIC_URL, et modifiez sa valeur de la manière suivante.

STATIC_URL = '/static/'
Cette configuration doit indiquer au projet que tous les fichiers statiques se trouvent dans un dossier appelé "static" à l'intérieur du dossier de l'application. Dans ce cas, il s'agit du répertoire _/globals/static_. Si nous avions une autre application utilisée par ce projet, elle ne reconnaîtrait que son dossier "static" interne (à moins qu'il ne soit spécifié qu'il s'agit d'un dossier général).

Les fichiers statiques sont ceux qui ne changent pas lorsque l'application est en cours d'exécution. Vous pouvez les utiliser pour stocker des fonctions, des définitions, des constantes et des images qui facilitent l'exécution du code (dans ce cas, des fichiers .py). En pratique, c'est dans ces fichiers que vous stockez le JavaScript et le CSS.

L'étape suivante consiste à y faire référence à partir du fichier index.html. Ajoutez une balise

avant le corps. À l'intérieur du corps, utilisez une balise

pour faire référence à une feuille de style de type text/CSS, avec la référence "{% static 'styles.css' %}" et le média "screen". De plus, vous devez indiquer à Django de télécharger les éléments statiques. Rappelez-vous que nous pouvons "dire à Django" quelque chose via le fichier HTML en ajoutant du code entre les crochets et les signes de pourcentage, comme indiqué dans l'exemple : “{% votre code ici %}”.

<head>
    {% load static %}
    <linkrel="stylesheet"type="text/CSS"href="{% static 'styles.css' %}"media="screen"/></head>

J'ai remplacé notre liste de globales par un tableau de globales avec une syntaxe similaire. Ensuite, j'ai ajouté au projet un espacement sympa entre les éléments ainsi que les couleurs de l'Innovatium. Vous pouvez consulter le fichier CSS que j'ai créé sur My GitHub et voir comment ce tableau a pris l'aspect illustré ci-dessous. Je ne suis pas un expert en CSS ou en conception de sites web. Cependant, si vous avez des questions, je serai heureux de les résoudre et même d'apprendre de nouvelles astuces ensemble. Laissez un commentaire ou envoyez-moi un message !

<table><thead><tr><th>Database</th><th>Global</th><th>Size</th><th>Allocated</th></tr></thead><tbody>
        {% for global in globals %}
        <tr><td>{{ global.database }}</td><td>{{ global.name }}</td><td>{{ global.size }}</td><td>{{ global.allocatedsize }}</td></tr>
        {% endfor %}
    </tbody></table>



PS. Si les fichiers statiques sont mis en cache, il est possible que les changements ne soient pas visibles lors du rechargement. Vous pouvez appuyer sur Ctrl+Shift+Delete et supprimer le cache stocké lorsque vous utilisez Google Chrome comme navigateur. Vous devez ensuite recharger votre page encore une fois. Si vous avez tout fait correctement, vous verrez ce qui suit dans votre terminal :

CONSEIL : pour éviter la nécessité de vider le cache à chaque fois que vous utilisez le programme, ajoutez la ligne suivante à l'en-tête de votre fichier HTML : &lt;meta http-equiv="cache-control" content="no-cache" />. Cette balise devrait empêcher la page de stocker le cache.

Exportation de données

Dans cette section, nous revenons au back-end. Il vous sera utile de revoir la partie 1 de cette série d'articles si vous ne connaissez pas le chemin des requêtes (modèle -> URL -> vue -> base de données et retour).
Nous mettrons en place un formulaire permettant à l'utilisateur de choisir entre l'exportation au format CSV, XML ou JSON, car cela couvre la plupart des utilisations pour le transfert de données. Et vous pouvez ajouter tous les langages que vous souhaitez. Pour cela, nous aurons besoin d'un formulaire avec une méthode HTTP, un jeton de sécurité, trois entrées radio et un bouton "Soumettre".

<formmethod="GET">
    {% csrf_token %}
    <inputtype="radio"name="exportLanguage"value="CSV"/><labelfor="CSV">CSV</label><inputtype="radio"name="exportLanguage"value="XML"/><labelfor="XML">XML</label><inputtype="radio"name="exportLanguage"value="JSON"/><labelfor="JSON">JSON</label><buttontype="submit"formaction="{% url 'export' %}"> Export </button></form>

Si vous désirez que les étiquettes affichent un texte "cliquable" pour la valeur correspondante, ajoutez la propriété onclick à l'étiquette avec la valeur getElementById(‘ID correspondant’).checked = true ainsi que les identifiants correspondants pour chaques option. La propriété formaction spécifiée dans le bouton renvoie à l'URL. Ainsi, vous pouvez avoir autant de boutons que vous le souhaitez pour indiquer différentes URL et soumettre le formulaire en conséquence.

Après avoir terminé l'étape précédente, nous pouvons ajouter le chemin qui nous dirige vers la vue sur urls.py et finalement créer la vue sur views.py. Cette vue peut sembler un peu plus complexe que celles que nous avons réalisées précédemment, mais, étape par étape, nous y parviendrons ensemble.

from .views import home, update, export

urlpatterns = [
    path('', home),
    path('update', update, name="update"),
    path('export', export, name="export")
]

Il faut d'abord assigner une variable pour référencer les globales. Ensuite, nous devons créer un chemin où le fichier apparaîtra lorsque l'utilisateur cliquera sur le bouton d'exportation ( il pourra être modifié ultérieurement du côté client). Enfin, nous devons savoir quelle langage a été sélectionnée et procéder à l'exportation adéquate pour chacun d'entre eux.

import os

defexport(request):
    globals = irisGlobal.objects.all()           # gets the globals
    cd = os.getcwd()                             # gets current directory
    language = request.GET.get("exportLanguage") # gets language selected in the formif language == "CSV":
        passelif language == "XML":
        passelif language == "JSON":
        passreturn redirect(home)

Pour les CSV, il suffit de mettre chaque enregistrement sur une ligne et de séparer chaque colonne par une virgule. La manière la plus logique de procéder serait de concaténer toutes les informations relatives à chaque groupe dans une chaîne de caractères entre virgules, suivie d'un terminateur de ligne, et d'écrire chaque ligne dans le fichier.

if language == "CSV":
    with open(cd+"\\test.csv", "w") as file:
        for eachGlobal in globals:
            row = eachGlobal.database+", "+eachGlobal.name+", "+str(eachGlobal.size)+", "+str(eachGlobal.allocatedsize)+"\n"
            file.write(row)
        

 

Pour JSON et XML, les convertisseurs série de Django sont nécessaires. Ils peuvent sembler complexes, mais en réalité, ils sont assez simples. Le module convertisseur série possède deux méthodes: sérialisation and désérialisation, qui permettent de convertir des informations depuis et vers votre langage préféré. Heureusement, XML et JSON sont des options intégrées.

from django.core import serializers

[...]

elif language =="XML": with open(cd+"\test.xml", "w") as file: globals = serializers.serialize("xml", globals) file.write(globals)

elif language =="JSON": with open(cd+"\test.json", "w") as file: globals = serializers.serialize("json", globals) file.write(globals)


Bien joué ! L'application est enfin prête à être rechargée et testée. Après l'exportation, votre espace de travail devrait ressembler à l'image ci-dessous.
 

Filtres

Commençons par appliquer un filtre à une base de données. Nous devons créer une balise de formulaire avec une entrée de texte pour saisir le nom de la base de données et une référence à l'URL. Nous pouvons utiliser l'affichage d'accueil avec quelques adaptations.

<formmethod="GET"action="{% url 'home' %}">
    {% csrf_token %}
    <inputtype="text"name="database"placeholder="Database"/></form>

Puisque nous nous référons maintenant au chemin d'accès à partir de l'index, il faut le nommer sur les modèles, sur urls.py.

path('', home, name="home"),

Rappelez-vous que dans l'affichage d'accueil, nous obtenons toutes les globales du modèle avec irisGlobal.objects.all() et nous les renvoyons à l'index. À ce moment-là, nous ne devons renvoyer qu'un ensemble filtré de ces valeurs globales au lieu de toutes les valeurs. La bonne nouvelle est que nous allons résoudre ce problème avec quatre lignes de code seulement.
    Tout d'abord, comme nous l'avons fait avec l'exportation, nous devons obtenir des informations de l'entrée avec request.GET.get() et réduire notre ensemble de globales en fonction de ce que notre utilisateur souhaite. Grâce à l'objet de requête de Django.db.models, nous pourrons utiliser la fonction de filtrage pour atteindre notre objectif.

from django.db.models import Sum, Q
from .models import irisGlobal

def home(request):
    globals = irisGlobal.objects.all()
    databaseFilter = request.GET.get("database")
    
    if databaseFilter:
        query = Q(database__contains=databaseFilter)
        globals = globals.filter(query)
        

Le Q() est l'objet de requête. Au sein de cet objet, vous pouvez ajouter le nom d'une colonne, deux soulignements et une instruction SQL pour affiner la recherche. Ensuite, vous pouvez passer tous les objets Q nécessaires comme arguments de la fonction de filtrage, et ils seront unis par les opérateurs "AND" (à moins qu'il ne soit spécifié le contraire). Il y a beaucoup d'autres choses que vous pouvez faire avec la classe Q. Pour en savoir plus sur cette classe et sur d'autres façons d'effectuer des requêtes avec Django, consultez la documentation officielle).

Vous pouvez maintenant recharger et tester vos filtres. N'oubliez pas de prêter attention à la façon dont les agrégateurs à la fin de la page s'adaptent aux filtres puisque nous les avons construits à partir de la variable de globales globals .

Plus de filtres !

Si vous êtes à l'aise avec la dernière section, le sujet peut être abordé avec plus d'assurance. Il est temps d'ajouter d'autres filtres et de les combiner. Pour commencer, ajoutez quelques entrées supplémentaires à notre formulaire.

<formmethod="GET">
    {% csrf_token %}
    <inputtype="text"name="database"placeholder="Database"/><inputtype="text"name="global"placeholder="Global name"/><inputtype="number"name="size"placeholder="Size"step="0.001"/><inputtype="number"name="allocated"placeholder="Allocated size"step="0.001"/><buttontype="submit"formaction="{% url 'home' %}"> Filter </button></form>

à l'affichage, ajoutez les nouveaux filtres à la variable globals. Ils doivent s'enchaîner et être équivalents à l'instruction SQL ci-dessous.

    On the view, add the new filters to the variable globals. They should chain and be equivalent to the SQL statement below.

SELECT * FROM globals

WHERE database LIKE ‘%databaseFilter%’ AND

      globals LIKE ‘%globalsFilter%’ AND

      size >=sizeFilter AND

      allocatedsize>=allocFilter

globals = irisGlobal.objects.all()
databaseFilter = request.GET.get("database")
globalFilter = request.GET.get("global")
sizeFilter = request.GET.get("size")
allocFilter = request.GET.get("allocated")

if databaseFilter: globals = globals.filter(Q(database__contains=databaseFilter)) else: databaseFilter=""if globalFilter: globals = globals.filter(Q(name__contains=globalFilter)) else: globalFilter=""if sizeFilter: globals = globals.filter(Q(size__gte=sizeFilter)) else: sizeFilter=""if allocFilter: globals = globals.filter(Q(allocatedsize__gte=allocFilter)) else: allocFilter=""

Le filtre fonctionne déjà, mais il peut encore être quelque peu amélioré. Si nous transmettons les variables reçues au retour, nous pourrons les utiliser comme valeurs dans les entrées. Elles ne disparaîtront pas non plus lorsque nous rechargerons la page ou que nous appuierons sur le bouton filtre.

return render(request, "index.html", {"globals": globals,
    "sumSize": sumSize,
    "sumAllocated":sumAllocated,
    "database":databaseFilter
    "global":globalFilter
    "size":sizeFilter,
    "allocated":allocFilter
})
<formmethod="GET">
    {% csrf_token %}
    <inputtype="text"name="{{database}}"placeholder="Database"/><inputtype="text"name="{{global}}"placeholder="Global name"/><inputtype="number"name="{{size}}"placeholder="Size"step="0.001"/><inputtype="number"name="{{allocated}}"placeholder="Allocated size"step="0.001"/><buttontype="submit"formaction="{% url 'home' %}"> Filter </button></form>

Nous pouvons fournir aux filtres des options pour les mises à jour inférieures ou égales à une valeur spécifique. Nous pouvons même programmer des mises à jour en direct (mise à jour à chaque touche sur laquelle l'utilisateur appuie pendant la saisie des données). Pour l'instant, nous nous contenterons de garder ces idées à l'esprit et nous passerons à l'ajout d'un ordre.

Exportation d'un ensemble filtré

Après tout ce que nous avons fait, il est logique que vous souhaitiez adapter l'affichage de l'exportation pour utiliser tous les filtres que nous avons ajoutés.

Nous pouvons placer toute la logique que nous avons ajoutée à partir des filtres dans une fonction que nous appellerons handle_filters, et au lieu d'utiliser le tableau créé à partir de irisGlobals.objects.all() pour obtenir les informations, nous utiliserons le tableau créé par cette fonction. Pour que tout cela fonctionne, nous devons assembler les deux formulaires.

defhandle_filters(request):
    globals = irisGlobal.objects.all()
    databaseFilter = request.GET.get("database")
    globalFilter = request.GET.get("global")
    sizeFilter = request.GET.get("size")
    allocFilter = request.GET.get("allocated")
<span class="hljs-keyword">if</span> databaseFilter:
    globals = globals.filter(Q(database__contains=databaseFilter))
<span class="hljs-keyword">else</span>:
    databaseFilter=<span class="hljs-string">""</span>
<span class="hljs-keyword">if</span> globalFilter:
    globals = globals.filter(Q(name__contains=globalFilter))
<span class="hljs-keyword">else</span>:
    globalFilter=<span class="hljs-string">""</span>
<span class="hljs-keyword">if</span> sizeFilter:
    globals = globals.filter(Q(size__gte=sizeFilter))
<span class="hljs-keyword">else</span>:
    sizeFilter=<span class="hljs-string">""</span>
<span class="hljs-keyword">if</span> allocFilter:
    globals = globals.filter(Q(allocatedsize__gte=allocFilter))
<span class="hljs-keyword">else</span>:
    allocFilter=<span class="hljs-string">""</span>
<span class="hljs-keyword">return</span> globals, databaseFilter, globalFilter, sizeFilter, allocFilter</code></pre>
defexport(request):
    globals = handle_filters(request)[0]
<formmethod="GET">
    {% csrf_token %}
    <inputtype="radio"name="exportLanguage"value="CSV"/><labelfor="CSV">CSV</label><inputtype="radio"name="exportLanguage"value="XML"/><labelfor="XML">XML</label><inputtype="radio"name="exportLanguage"value="JSON"/><labelfor="JSON">JSON</label><buttontype="submit"formaction="{% url 'export' %}"> Export </button><br/><inputtype="text"name="{{database}}"placeholder="Database"/><inputtype="text"name="{{global}}"placeholder="Global name"/><inputtype="number"name="{{size}}"placeholder="Size"step="0.001"/><inputtype="number"name="{{allocated}}"placeholder="Allocated size"step="0.001"/><buttontype="submit"formaction="{% url 'home' %}"> Filter </button></form>

 

Order table (with JavaScript)

This section will show you how I added order options to the table. There are many ways to do it, but today we will use JavaScript to learn something different from what we have done previously. Of course, it would be better if you had some knowledge of the language’s basics, but you will be able to follow up anyway. Nevertheless, the main focus is to understand the connections. They will be very similar to the CSS’s and will open even more doors for you to manage data from the portal.

Let’s begin! Open the index.html and add two <script> tags at the very bottom of the body. The first one should reference the dependencies. The second one will point to a .js file in our project (we will create it in the next step). Since we already added ‘{% load static %}’, this will be enough for the JavaScript file to start working.
 

<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><scriptsrc="{% static 'index.js'%}"></script>

 

Tableau de commandes (avec JavaScript)

Cette section vous montrera la manière dont j'ai ajouté des options de commande au tableau. Il existe de nombreuses façons de le faire, mais aujourd'hui nous allons utiliser JavaScript pour apprendre quelque chose de différent de ce que nous avons fait précédemment. Bien sûr, il serait préférable que vous ayez une certaine connaissance des bases du langage, mais vous pourrez suivre de toute façon. Néanmoins, l'objectif principal est de comprendre les connexions. Elles seront très similaires à celles du CSS et vous offriront encore plus de possibilités pour gérer les données du portail.

Commençons ! Ouvrez le fichier index.html et ajoutez deux balises tout en bas du corps. La première doit faire référence aux dépendances. La deuxième pointera vers un fichier .js dans notre projet (nous le créerons à l'étape suivante). Comme nous avons déjà ajouté "{% load static %}", cela suffira pour que le fichier JavaScript démarre.
 

<scriptsrc="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><scriptsrc="{% static 'index.js'%}"></script>

 

Une fois la connexion établie, créez un fichier dans le dossier /static, et nommez-le index.js (ou quel que soit le nom que vous lui avez donné dans la référence). Si, à cette étape, vous ajoutez un console.log(“Hello world!”); au fichier et rechargez la page, vous verrez qu'il fonctionne déjà. Cela signifie que nous pouvons enfin commencer à créer la logique de commande.
     Nous voulons que le tableau soit dans l'ordre lorsque l'utilisateur clique sur un en-tête. Pour ce faire, il nous faut que l'affichage obéisse à l'utilisateur chaque fois qu'il clique sur l'un de ces en-têtes. Nous disposons du document à partir duquel nous sélectionnons chaque

élément du tableau et, pour chacun d'entre eux, nous voulons savoir quand il est cliqué.
 
  <pre class="codeblock-container" idlang="5" lang="JavaScript" tabsize="4"><code class="language-javascript hljs"><span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">"table th"</span>).forEach(<span class="hljs-function"><span class="hljs-params">header</span> =&gt;</span> {
header.addEventListener(<span class="hljs-string">"click"</span>, () =&gt; {
    
});

})

Nous pouvons maintenant lui communiquer ce qu'il doit faire une fois qu'il a reçu l'événement "click". Nous aurons besoin du tableau (le parent de , qui est le parent de , qui est le parent du "cliqué" ) et d'un nombre indiquant quel en-tête a été sélectionné. Nous devons également savoir si le tableau est déjà dans l'ordre croissant pour cette colonne. Nous pouvons alors appeler une fonction pour trier le tableau.

      <pre class="codeblock-container" idlang="5" lang="JavaScript" tabsize="4"><code class="language-javascript hljs"><span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">"table th"</span>).forEach(<span class="hljs-function"><span class="hljs-params">header</span> =&gt;</span> {
header.addEventListener(<span class="hljs-string">"click"</span>, () =&gt; {
    <span class="hljs-keyword">const</span> table = header.parentElement.parentElement.parentElement;
    <span class="hljs-keyword">const</span> columnIndex = <span class="hljs-built_in">Array</span>.prototype.indexOf.call(header.parentElement.children, header);
    <span class="hljs-keyword">const</span> isAscending = header.classList.contains(<span class="hljs-string">"ascending"</span>);
    sortTable(table, columnIndex+<span class="hljs-number">1</span>, !isAscending);
});

})

Il existe de nombreux algorithmes prédéfinis pour le tri des tableaux. Vous pouvez même télécharger et utiliser certains d'entre eux. Je veux éviter d'ajouter d'autres dépendances à ce projet. C'est pourquoi je m'en tiendrai à l'algorithme suivant.

document.querySelectorAll("table th").forEach(header => {
    header.addEventListener("click", () => {
        const table = header.parentElement.parentElement.parentElement;
        const columnIndex = Array.prototype.indexOf.call(header.parentElement.children, header);
        const isAscending = header.classList.contains("ascending");
        sortTable(table, columnIndex+1, !isAscending);
    });
})

There are many pre-built sorting table algorithms. You can even download and use some of them. I want to avoid adding more dependencies to this project. For that reason, I will stick to using the following algorithm.

functionsortTable(table, columnIndex, ascending = true) {
    const direction = ascending ? 1 : -1;                 // if its ascending, direction is 1, else is -1const header = $('th:nth-child('+columnIndex+')');    // gets the header with index columnIndexconst body = table.children[1]                        // gets the body (second children of table)const rows = Array.from(body.querySelectorAll("tr")); // creates an array for the rows in the body // removes previous order from every header
    table.querySelectorAll("th").forEach(th => th.classList.remove("ascending", "descending"));
<span class="hljs-comment">// adds the order to the clicked header</span>
header[<span class="hljs-number">0</span>].classList.toggle(<span class="hljs-string">"ascending"</span>, ascending);
header[<span class="hljs-number">0</span>].classList.toggle(<span class="hljs-string">"descending"</span>, !ascending);

<span class="hljs-comment">// algorithm for sorting the rows</span>
<span class="hljs-keyword">const</span> sorted = rows.sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> aColumn = a.querySelector(<span class="hljs-string">'td:nth-child('</span>+columnIndex+<span class="hljs-string">')'</span>).textContent;
    <span class="hljs-keyword">const</span> bColumn = b.querySelector(<span class="hljs-string">'td:nth-child('</span>+columnIndex+<span class="hljs-string">')'</span>).textContent;

    <span class="hljs-keyword">return</span> aColumn &gt; bColumn ? (<span class="hljs-number">1</span>*direction) : (<span class="hljs-number">-1</span>*direction);
});

<span class="hljs-comment">// removes the rows from the body and add the sorted rows</span>
<span class="hljs-keyword">while</span> (body.firstChild) {
	body.removeChild(body.firstChild);
}
body.append(...sorted);

}

La référence suivante explique l'algorithme susmentionné. https://dcode.domenade.com/tutorials/how-to-easily-sort-html-tables-with-css-and-javascript

Maintenant, avec un peu de CSS, vous avez un tableau parfaitement ordonné.


.ascending::after {
    content: "\25be";
}

.descending::after { content: "\25b4"; }

.ascending, .descending { background-color: #ffffff30; box-shadow: .1px .1px10.px #aaa; }

Une approche différente de l'ajout d'ordre

Une autre façon d'ajouter de l'ordre serait de créer un formulaire avec une option pour choisir chaque colonne (avec un peu de JavaScript, vous pouvez également utiliser les en-têtes pour la sélection), de le diriger vers un affichage qui utilise la fonction .order_by(“nom de la colonne”) sur l'objet globals, et de le renvoyer ordonné sur la fonction de rendu. Les deux options sont implémentées sur mon GitHub si vous voulez le vérifier. Faites attention au fait que dans la première méthode, nous ne changeons pas les données ou l'ensemble des résultats. Puisque nous ajustons seulement la façon dont elles sont affichées, cela n'affectera pas l'exportation.

Un autre conseil sur JavaScript

J'ai préparé pour vous une dernière chose qui pourrait être utile à connaître lorsque vous utilisez Django et JavaScript. Si vous voulez rediriger vers une URL (par exemple, la mise à jour), vous pouvez utiliser document.location.href = “update”; dans le fichier .js.

Si je suis un IRIS Dev, pourquoi voudrais-je savoir cela ?

Vous avez peut-être observé que l'ensemble de cet article n'utilise pas directement IRIS. Les tutoriels de la partie 2 seraient très similaires si nous utilisions n'importe quelle autre base de données. Cependant, si nous construisons tout ce qui est décrit ci-dessus en utilisant la technologie d'InterSystems comme base, nous aurons la possibilité d'adapter ce portail pour exécuter ou montrer beaucoup plus de choses en toute simplicité. Si vous êtes déjà un développeur IRIS, vous pouvez facilement imaginer comment ce simple portail peut combiner des informations provenant d'origines multiples (pas nécessairement globales), en plus de la capacité à fournir toutes sortes de traitements et d'analyses de données, avec les outils Cloud, IntegratedML (Machine Learning), Business Intelligence, Text Analytics et Natural Language Processing (NLP), etc. Lorsque tous les outils fournis par InterSystems offrent une interactivité complète, facile et magnifique avec les utilisateurs, à condition que toute la sécurité nécessaire soit en place, les possibilités dépassent l'imagination.

J'ai rassemblé ici quelques exemples pour illustrer mon point de vue. Vous pouvez avoir un capteur qui envoie des informations à l'API Callin d'InterSystems, intégré à un programme en C++. Ces données peuvent être interprétées, traitées et stockées dans IRIS, et leur analyse peut être affichée avec Django, ce qui permet à l'utilisateur de l'utiliser. Combiné avec InterSystems IRIS for Health et HealthShare, ce portail devient un outil de suivi des patients en thérapie. Il peut utiliser l'apprentissage automatique et la BI pour détecter et prédire les changements dangereux et envoyer des alertes avec SAM (System Alerting and Monitoring) au médecin responsable, qui peut être affiché sur le portail avec toutes les informations relatives au cas requises pour prendre une décision rapide.

Dans un autre contexte, mais avec une logique similaire, InterSystems IRIS for Supply Chain peut être utilisé avec ce portail pour permettre à l'utilisateur de voir ce qui pourrait compromettre le fonctionnement du système. Cette collaboration nous permet de prendre et de transmettre des décisions d'une manière qui rend l'ensemble de l'opération facilement compréhensible et contrôlable.
 

0
0 79
Article Lorenzo Scalese · Oct 4, 2023 8m read

Description du cas

Imaginons que vous soyez un développeur en Python ou que vous disposiez d'une équipe bien formée et spécialisée en Python, mais que le délai qui vous est imparti pour analyser certaines données dans IRIS soit serré. Bien sûr, InterSystems offre de nombreux outils pour toutes sortes d'analyses et de traitements. Cependant, dans le scénario donné, il est préférable de faire le travail en utilisant le bon vieux Pandas et de laisser IRIS pour une autre fois.
    Dans la situation décrite ci-dessus et dans bien d'autres cas, il se peut que vous souhaitiez extraire des tables d'IRIS pour gérer des données en dehors des produits d'InterSystems. Cependant, vous pouvez également avoir besoin de faire les choses dans l'autre sens lorsque vous avez un tableau externe dans n'importe quel format, à savoir CSV, TXT ou Pickle, que vous avez besoin d'importer et d'utiliser les outils d'IRIS sur celui-ci.
Indépendamment du fait que vous ayez ou non à faire face à un problème décrit ci-dessus, Innovatium m'a appris qu'il est toujours utile de connaître plusieurs façons de résoudre un problème de codage. La bonne nouvelle, c'est qu'il n'est pas nécessaire de passer par le processus laborieux de création d'un nouveau tableau, de transfert de toutes les lignes et d'ajustement de chaque type lorsque l'on importe un tableau d'IRIS.
    Cet article vous montrera comment transformer rapidement un tableau IRIS en une trame de données Pandas et inversement avec seulement quelques lignes de code. Vous pouvez consulter le code à partir de mon GitHub, où vous trouverez Jupiter Notebook avec toutes les étapes de ce tutoriel.

Importation du tableau d'IRIS

Bien entendu, vous devez commencer par importer les bibliothèques nécessaires à ce projet.

import pandas as pd
import sqlalchemy as db

L'étape suivante consiste à créer la connexion entre le fichier Python et l'instance IRIS. Pour ce faire, nous utiliserons la fonction create_engine() de SQLAlchemy, avec une chaîne en tant que paramètre. Cette chaîne doit contenir des informations sur le dialecte de l'opération, le nom d'utilisateur et le mot de passe, l'hôte et le port de l'instance, ainsi que l'espace de noms de destination. Pour plus d'informations sur les concepts de base de l'utilisation de sqlalchemy-iris, consultez l'un de mes articles précédents, SQLAlchemy - la façon la plus simple d'utiliser Python et SQL avec les bases de données d'IRIS.

engine = db.create_engine("iris://_system:SYS@localhost:1972/SAMPLE")
connection = engine.connect()

Ensuite, nous pouvons déclarer une variable qui contiendra la trame de données et appeler la fonction read_sql_table() de Pandas sur cette connexion, en spécifiant le nom du tableau sous la forme d'une chaîne avec le schéma. Vous pouvez également indiquer le schéma dans un autre argument, ce qui est, en fait, préférable car le fait d'avoir un point sur la chaîne de nom peut provoquer des erreurs dans certains cas.

df = pd.read_sql_table("NameAge", connection, schema="PD")

C'est une bonne pratique de vérifier si le tableau avec lequel nous travaillons existe dans le schéma que nous voulons utiliser et, bien sûr, s'il y a un schéma dont nous avons besoin au départ. Dans la dernière section de cet article, vous apprendrez comment le faire ainsi que quelques autres conseils. À partir de maintenant, si vous avez l'habitude d'utiliser Pandas, vous pouvez effectuer toutes les modifications et analyses que vous souhaitez puisque vous savez quoi faire. Explorez l'exemple suivant pour voir comment cela fonctionne.

Envoi d'un tableau à IRIS

Avant de commencer, modifions un peu notre trame de données, à titre d'exemple. Nous pouvons adapter les valeurs d'une colonne à nos besoins (par exemple, ajouter des lignes et des colonnes, etc.). Après avoir joué un peu, j'ai mis les noms en minuscules et j'ai ajouté une nouvelle personne et une colonne sur la base des données existantes. Vous pouvez consulter l'illustration suivante pour voir le résultat.

Nous pouvons maintenant le renvoyer à IRIS par une seule ligne de code. Il suffit de spécifier le moteur et le nom du tableau.

df.to_sql("NameAge", con=engine, schema="PD", if_exists="replace")

Une fois de plus, nous devons placer le schéma dans un argument séparément du nom du tableau afin d'éviter certaines erreurs et un comportement indésirable. En outre, l'argument if_exists spécifie ce qu'il faut faire s'il existe déjà un tableau portant le même nom dans le schéma concerné. Les valeurs possibles sont : "replace" (remplacer), "fail" (échouer, la valeur par défaut) et "append" (ajouter). Bien entendu, l'option "replace" supprime le tableau et en crée un nouveau à l'aide d'une commande SQL, tandis que l'option "append" ajoute les données au tableau existant. N'oubliez pas que cette méthode ne vérifie pas les valeurs répétées ; soyez donc prudent lorsque vous utilisez cet attribut. Enfin, la valeur "fail" provoque l'erreur suivante :

Gardez à l'esprit que si vous spécifiez un nom de tableau qui n'existe pas, la fonction le créera.

Vous pouvez maintenant lancer une requête dans IRIS pour voir ce qu'il y a de nouveau ou aller dans le Portail de gestion dans la partie consacrée à SQL. N'oubliez pas que si vous avez utilisé la valeur "replace", vous devez consulter le code source de la classe, car la méthode la réécrit complètement. Cela signifie que si vous avez implémenté des méthodes, vous devez les laisser dans une superclasse.

Plus d'astuces sur sqlalchemy-iris

Si vous rencontrez quelques problèmes que vous n'avez pas pu résoudre avec les informations partagées dans d'autres communautés ou forums liés au code de votre application, vous trouverez peut-être ici l'aide dont vous avez besoin. Vous y découvrirez une liste d'astuces pour trouver des détails sur le moteur et le dialecte.

Caractéristiques spécifiques au dialecte

SQL Alchemy travaille avec des dialectes qui sont automatiquement choisis en fonction de votre moteur. Lorsque vous utilisez la fonction create_engine() pour vous connecter à une base de données IRIS, le dialecte choisi est sqlalchemy-iris by Dmitry Maslennikov.
Vous pouvez accéder à ses caractéristiques et les modifier à l'aide de la propriété de dialecte de votre moteur.

engine = db.create_engine("iris://_system:SYS@localhost:1972/SAMPLE")
engine.dialect

Avec l'extension IntelliCode de VSCode, vous pouvez rechercher toutes les options de cette propriété ou consulter le code source sur CaretDev's GitHub.

Vérification des schémas disponibles dans un moteur

Une fonction spéciale du dialecte digne d'être soulignée est la fonction get_schema_names(). Faites attention ! Les informations suivantes peuvent être cruciales pour vous si vous voulez éviter les erreurs dans votre code et pour l'itération.

connection = engine.connect()
engine.dialect.get_schema_names(connection)

 

Vérification des tableaux disponibles dans un schéma

Examinons une situation semblable. Il se peut que vous ayez besoin de connaître les tableaux disponibles à partir d'un schéma. Dans ce cas, vous pouvez utiliser l'inspection. Exécutez la fonction inspect() sur le moteur et enregistrez-la dans une variable. Cette même variable sera utilisée pour accéder à une autre fonction, get_table_names(). Elle renverra une liste des noms de tableaux dans le schéma spécifié ou dans la valeur par défaut "SQLUser".

inspection = db.inspect(engine)
inspection.get_table_names(schema="Sample")

De plus, si vous souhaitez utiliser plus de fonctionnalités de SQL Alchemy sur vos données, vous pouvez déclarer une base et faire en sorte que ses métadonnées reflètent un schéma du moteur.

b = db.orm.declarative_base()
b.metadata.reflect(engine, schema="Sample")

Si vous avez besoin de plus d'informations pour résoudre ce problème, consultez la Documentation SQL Alchemy et le dépôt sqlalchemy-iris GitHub Repository. Vous pouvez également m'envoyer un message ou laisser un commentaire, et nous essaierons de découvrir le secret ensemble.

Considérations finales

L'approche de mise en œuvre présentée dans cet article met l'accent sur l'utilisation des instances IRIS en tant que fournisseurs de services en nuage et permet d'effectuer une analyse sur différentes bases. Elle facilite la surveillance simultanée de toutes les instances dans toutes leurs qualités et la comparaison de leurs performances et de leur utilisation. Si vous combinez ces connaissances avec le développement décrit dans un autre article à propos d'un portail réalisé avec Django, vous pouvez rapidement construire un gestionnaire puissant pour autant de fonctionnalités et d'instances que vous le souhaitez.
    Cette implémentation est également un moyen efficace de transférer des données de l'extérieur d'IRIS vers une classe bien construite. Comme vous êtes peut-être familier avec d'autres fonctions trouvées dans Pandas pour traiter de nombreux langages différents, à savoir CSV, JSON, HTML, Excel et Pickle, il vous sera facile de changer read_sql\table en read_csv, read_json, ou toute autre option. Certes, je dois vous avertir que l'intégration avec InterSystems à partir de certains types de données n'est pas une fonctionnalité intégrée et qu'elle n'est donc pas toujours facile à mettre en œuvre. Cependant, l'union de SQL Alchemy et de Pandas sera toujours utile pour exporter des données depuis IRIS.
    Ainsi, dans cet article, nous avons appris qu'IRIS possède tous les outils dont vous avez besoin pour vous aider à développer et à intégrer facilement les dispositifs existants de votre système ou les dispositifs de votre domaine d'expertise.
 

0
0 146
Article Lorenzo Scalese · Sept 27, 2023 16m read

Cette publication soutient la démonstration au Global Summit 2023 "Demos and Drinks" avec des détails très probablement perdus dans le bruit de l'événement. Il s'agit d'une démonstration sur la façon dont on peut utiliser les capacités FHIR SQL d'InterSystems du Serveur FHIR avec la solution Super Awesome Identity and Resolution, Zingg.ai pour détecter les enregistrements en double dans votre référentiel FHIR, et l'idée de base derrière la remédiation de ces ressources avec le PID^TOO|| en cours de construction actuellement inscrit dans le programme Incubateur d'InterSystems. Si vous êtes dans

0
0 68
Article Lorenzo Scalese · Sept 21, 2023 12m read

Salut les devs,

Aujourd’hui j’aimerais aborder un sujet qui m’a fait passer des moments difficiles (j’en suis convaincu, celà a déjà dû être le cas d’un bon nombre d’entre-vous) “le bottleneck”.  C’est un sujet très vaste, cet article se concentrera sur l’identification des requêtes HTTP entrantes qui pourraient être à l’origine de problèmes de lenteur.  Je vous mettrai aussi à disposition un petit outil que j’ai développé pouvant aider à leur identification.

Nos logiciels deviennent de plus en plus complexes, traitent un grand nombre de requêtes provenant de différentes sources, il peut s’agir d'applications front-end ou de tiers applications back-end. Pour garantir des performances optimales, il est essentiel de disposer d'un système de log capable de prendre quelques mesures clés telles que le temps de réponse, le nombre de global référence et le nombre de lignes de code exécutées pour chaque réponse HTTP.  Dans le cadre de mon travail, je suis impliqué dans le développement d’un logiciel dossier patient informatisé ainsi que sur l’analyse des incidents. La charge utilisateur provient essentiellement de requêtes HTTP (API REST ou application CSP), la nécessité de disposer de ce type de mesure lorsque des problèmes de lenteur généralisée se produisent est devenu une évidence.

0
0 512
Article Lorenzo Scalese · Sept 15, 2023 6m read

Salut les développeurs,

Dans cet article, je vais vous montrer comment exécuter du code au moment de la compilation avec les macros ObjectScript.

Voici un cas d'utilisation qui m'a récemment amené à utiliser cette fonctionnalité :

Dans le cadre d'une application médicale développée depuis plus de 20 ans, nous avons un grand nombre de paramètres. Bien que nous disposions de procédures pour documenter ces paramètres, il peut être utile d'avoir une vue rapide sur les paramètres réellement utilisés par le code de l'application.

0
0 99
Article Lorenzo Scalese · Sept 5, 2023 9m read

Hibernate est le framework le plus populaire pour réaliser des projets ORM (Mapping Objet-Relationnel). Avec Hibernate, un logiciel peut utiliser les principaux SGBD du marché, et même changer de fournisseur de base de données à tout moment, sans impact sur le code source. Cela est possible car Hibernate prend en charge les dialectes. Chaque produit de base de données a un dialecte différent qui peut être assigné dans un fichier de configuration. Ainsi, si un logiciel utilise Oracle et souhaite évoluer vers InterSystems IRIS, il suffit de modifier le fichier de configuration avec les informations relatives à la connexion et au dialecte. Si votre logiciel nécessite une préparation à l'utilisation de la base de données indiquée par votre client, Hibernate est la solution qu'il vous faut.

Est-ce qu'il existe un dialecte d'InterSystems IRIS pour le nouveau Hibernate 6 ?

Actuellement, il n'existe pas de dialecte officiel pour utiliser IRIS avec le nouveau Hibernate 6. Pour résoudre ce problème, Dmitry Maslennikov a proposé une idée dans l'excellent portail d'idées (https://ideas.intersystems.com/ideas/DPI-I-372) et je l'ai mise en œuvre.
Si vous suivez ce tutoriel, vous verrez l'idée et ce nouveau dialecte d'IRIS en action et en fonctionnement.

Ce qu'il vous faut pour réaliser ce tutoriel

Pour réaliser ce tutoriel, vous avez besoin de :

  1. Une instance IRIS en cours d'exécution (si vous n'en avez pas, vous pouvez l'obtenir sur https://openexchange.intersystems.com/package/ObjectScript).
  2. Les outils Spring Tools installés (téléchargez-les sur https://spring.io/tools). Choisissez la version Eclipse pour ce tutoriel.
  3. Java JDK version 17 (téléchargez-le sur https://jdk.java.net/archive/). Choisissez Java 17 pour ce tutoriel.
  4. Tout le code source de ce tutoriel : https://github.com/yurimarx/iris-java-tools/tree/main/springboot-sample.

Les étapes du tutoriel

1.    Ouvrez Spring Tool Suite (STS) et choisissez un chemin d'accès valide à l'espace de travail (n'importe quel dossier) et cliquez sur Launch ("lancement") :

2.    Cliquez sur le lien Créer un nouveau Projet Spring Starter :

3.    Cet assistant créera un nouveau projet Spring. Remplissez les champs avec les valeurs suivantes :
•    Service URL: https://start.spring.io
•    Type : Maven (c'est un gestionnaire de paquets comme NPM, ZPM ou IPM)
•    Emballage : Jar (type d'exécutable pour le projet compilé)
•    Version de Java : 17 ou 20 (pour ce tutoriel, j'ai choisi la version 17)
•    Langage : Java
•    Groupe : com.tutorial (domaine du projet pour Maven)
•    Artifact : iris-tutorial (nom du projet pour Maven)
•    Version : 0.0.1-SNAPSHOT (version du projet pour Maven)
•    Description : Tutoriel IRIS
•    Paquet : com.tutorial.iris (paquet roott pour le projet)

4.    Cliquez sur Next (Suivant). 5.    Choisissez les dépendances suivantes pour votre projet :

6.    Cliquez sur Finish (Terminer) pour créer votre projet.
7.    Ouvrez votre fichier pom.xml et incluez 2 nouvelles dépendances (pour le dialecte IRIS et pour le pilote IRIS JDBC), ainsi qu'un dépôt (ce qui est nécessaire car le pilote IRIS JDBC d'InterSystems n'est pas publié dans un dépôt maven public).

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.1</version><relativePath /><!-- lookup parent from repository --></parent><groupId>com.tutorial</groupId><artifactId>tutorial-dialect</artifactId><version>0.0.1-SNAPSHOT</version><name>tutorial-dialect</name><description>Tutorial for IRIS Hibernate 6 Dialect</description><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-rest</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-hateoas</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-rest-hal-explorer</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.github.yurimarx</groupId><artifactId>hibernateirisdialect</artifactId><version>1.0.0</version></dependency><dependency><groupId>com.intersystems</groupId><artifactId>intersystems-jdbc</artifactId><version>3.7.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build><repositories><repository><id>InterSystems IRIS DC Git Repository</id><url>
				https://github.com/intersystems-community/iris-driver-distribution/blob/main/JDBC/JDK18</url><snapshots><enabled>true</enabled><updatePolicy>always</updatePolicy></snapshots></repository></repositories></project>

8.    Allez dans le fichier application.properties (src > main > java > dossier resources) et définissez les propriétés connection et dialect avec ces valeurs :

spring.datasource.username=_SYSTEMspring.datasource.url=jdbc:IRIS://localhost:1972/USERspring.datasource.password=SYSspring.jpa.properties.hibernate.default_schema=Examplespring.jpa.hibernate.ddl-auto=updatespring.datasource.driver-class-name=com.intersystems.jdbc.IRISDriverspring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=falsespring.jpa.database-platform=io.github.yurimarx.hibernateirisdialect.InterSystemsIRISDialectspring.jpa.show-sql=truespring.jpa.properties.hibernate.format_sql=true

9.    Créez une nouvelle classe persistante (cliquez sur le bouton droit de la souris sur le projet > Nouveau > Classe) :

10.    Remplissez les champs suivants pour créer la classe :
•    Paquet: com.tutorial.iris.model
•   Nom: Product (Produit)

11.    Cliquez sur Finish (Terminer) pour créer la classe. 12.    Développez la classe persistante Produit (classe dont les valeurs sont conservées dans un tableau SQL) à l'aide de ce code source :

package com.tutorial.dialect.model;

import java.util.Date;

import com.fasterxml.jackson.annotation.JsonFormat;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;

@Entity@Table(name = "Product")
publicclassProduct{

	
	@Id@GeneratedValue (strategy = GenerationType.IDENTITY)
    private Long id;
	
	private String name;
	
	private String description;
	
	private Double height;
	
	private Double width;
	
	private Double weight;
	
	@Column(name="releasedate")
	@Temporal(TemporalType.DATE)
	@JsonFormat(pattern = "yyyy-MM-dd")
	private Date releaseDate;

	public Long getId(){
		return id;
	}

	publicvoidsetId(Long id){
		this.id = id;
	}

	public String getName(){
		return name;
	}

	publicvoidsetName(String name){
		this.name = name;
	}

	public String getDescription(){
		return description;
	}

	publicvoidsetDescription(String description){
		this.description = description;
	}

	public Double getHeight(){
		return height;
	}

	publicvoidsetHeight(Double height){
		this.height = height;
	}

	public Double getWidth(){
		return width;
	}

	publicvoidsetWidth(Double width){
		this.width = width;
	}

	public Double getWeight(){
		return weight;
	}

	publicvoidsetWeight(Double weight){
		this.weight = weight;
	}

	public Date getReleaseDate(){
		return releaseDate;
	}

	publicvoidsetReleaseDate(Date releaseDate){
		this.releaseDate = releaseDate;
	}

}

13.    Créez un référentiel d'interface pour les opérations CRUD sur la classe Produit (cliquez sur le bouton droit de la souris dans le projet > Nouveau > Interface) :

14.    Saisissez les valeurs de l'interface et cliquez sur Finish (Terminer) :
•   Paquet: com.tutorial.iris.repository
•   Nom: ProductRepository

15.    Cliquez sur Finish (Terminer) pour créer l'interface.
16.    Développez l'interface ProductRepository (un référentiel CRUD implémentant les fonctions de sauvegarde, de suppression, de recherche, de recherche unique et de mise à jour) (src > main > java > com > tutoriel > dialecte > dossier référentiel) à l'aide de ce code source :

package com.tutorial.dialect.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.tutorial.dialect.model.Product;

@RepositorypublicinterfaceProductRepositoryextendsCrudRepository<Product, Long> {

}

17.    Maintenant, en utilisant le navigateur HAL de Springboot, vous pouvez tester les fonctions CRUD dans un écran web.
18.    Assurez-vous d'exécuter une instance IRIS sur localhost, port 1972 avec l'utilisateur _SYSTEM et le mot de passe SYS (ou modifiez l'application.properties pour d'autres valeurs de connexion).
19.    Exécutez l'application (cliquez avec le bouton droit de la souris sur le projet > Exécuter en tant que > Spring boot app).
20.    Sur la console, vous verrez le journal indiquant le démarrage de l'application :

21.    Allez dans votre navigateur et tapez http://localhost:8080. Découvrez le navigateur HAL :

22.    Cliquez sur le bouton "plus" de produits endpoint pour créer un nouveau produit :

23.    Remplissez les valeurs suivantes pour créer un nouveau produit et cliquez sur le bouton "Go" (aller) pour confirmer :

24.    Un nouveau produit a été lancé :

Vérifier la nouvelle ligne sur IRIS (tableau du produit sur l'espace de noms USER)

Testez d'autres opérations, vérifiez la base de données et amusez-vous !

0
0 62
Article Lorenzo Scalese · Août 14, 2023 2m read

InterSystems IRIS propose plusieurs façons de profiler votre code. Dans la plupart des cas, il produit suffisamment d'informations pour trouver les endroits où l'on passe le plus de temps ou les ensembles les plus globaux. Mais il est parfois difficile de comprendre le flux d'exécution et comment il s'est terminé à ce point-là. Pour résoudre ce problème, j'ai décidé d'implémenter un moyen de construire un rapport d'une certaine manière, de sorte qu'il est possible de naviguer par pile vers le bas.

 

0
0 45
Article Lorenzo Scalese · Juil 31, 2023 11m read

Notre objectif

Dans le dernier article, nous avons parlé de quelques éléments de démarrage pour Django. Nous avons appris à commencer le projet, à nous assurer que nous disposons de tous les éléments requis et à créer une matrice CRUD. Cependant, aujourd'hui, nous allons un peu plus loin.
Aujourd'hui, nous allons donc connecter IRIS à un environnement Python, construire quelques fonctions et les afficher sur une page web. Ce sera similaire à la dernière discussion, mais nous irons assez loin pour que vous puissiez faire quelque chose de nouveau, mais pas assez pour que vous vous sentiez perdus.
Dans ce projet, nous obtiendrons des informations sur les globales dans IRIS afin de suivre leur taille et de comprendre le coût de chaque espace de noms et de chaque table en Mo.

Le projet

Chaque étape est disponible sur mon Référentiel GitHub, dans l'historique des commits.

Les éléments de démarrage

Nous suivons quelques étapes similaires à celles de l'article précédent, alors cette partie vous sera familière.

  • Allez dans le répertoire souhaité sur le terminal et tapez ce qui suit.

django-admin startproject globalSize

  • Ajoutez requirements.txt avec le texte suivant, et tapez

pip install -r requirements.txt

pour être sûr de remplir les conditions requises.

# Django en lui-même
django>=4.2.1# Pilote InterSystems IRIS pour Django
django-iris==0.2.2
  • Dans globalSize/settings.py, ajoutez IRIS dans les configurations DATABASES :
DATABASES = {
	‘default’: {
		‘ENGINE’: ‘django_iris’,
		‘NAME’: ‘USER’,
		‘USER’: ‘_system’,
		‘PASSWORD’: ‘SYS’,
		‘HOST’: ‘localhost’,
		‘PORT’: 1972,
	}
}
  • N'oubliez pas d'ajouter un .gitignore et un .editorconfig. Il est également pratique d'avoir un linter de votre choix, mais cela dépasse le cadre de cet article.

 

Création de l'application et du modèle

Nous avons créé une application et un modèle dans le dernier article, cette section devrait donc vous être familière, même s'il s'agit d'une application et d'un modèle différents.

  • Pour créer l'application, saisissez

python manage.py startapp globals

  • Dans _globals/models.py, _créez un modèle avec les informations que vous souhaitez afficher sur vos globales :
classirisGlobal(models.Model):
	database = models.CharField(max_length=40)
	name = models.CharField(max_length=40)
	allocatedsize = models.FloatField()
	size = models.FloatField()


	def__str__(self):return self.name
  • Dans _settings.py, _ajoutez la nouvelle application à INSTALLED_APPS :
INSTALLED_APPS = [
	…,
	‘globals’,
]

 

Configuration des URL et de la page d'accueil

Une fois de plus, nous suivons quelques étapes similaires à celles de l'article précédent.

  • La fonction incluse dans django.urls est importée dans _globalSize/urls.py et un nouveau chemin est ajouté à globals.urls dans urlpatterns.
from django.urls import path, include
urlpatterns = [
    …,
    path(‘globals/’, include(‘globals.urls’)),
]
  • Créez les URLs pour l'application, en ajoutant le fichier globals/urls.py avec le texte suivant.
from django.urls import path
from .views import home
urlpatterns = [
	path(‘’, home),
]
  • Créez la vue importée dans la dernière étape. Dans view.py, ajoutez la fonction ci-dessous.
defhome(request):return render(request, “index.html”)
  • Pour finir, ajoutez le fichier globals/templates/index.html et générez la page d'accueil comme vous le souhaitez. Consultez l'exemple ci-dessous :


  
    hello world!
  

Si vous entrez les commandes ci-dessous et suivez le lien http://127.0.0.1:8000/globals/, vous aurez une page affichant "hello world !".

python manage.py makemigrations<br>python manage.py migrate<br>python manage.py runserver

Affichage des globales dans les pages d'accueil et d'administration

  • Importez le modèle et enregistrez-le dans le fichier admin.py.
from .models import irisGlobal
admin.site.register(irisGlobal)
  • Importez le modèle dans views.py et renvoyez-le dans la fonction.
from .models import irisGlobal
defhome(request):
	globals = irisGlobal.objects.all()
	return render(request, “index.html”, {“globals”: globals})
  • Nous pouvons maintenant accéder aux valeurs globales à partir de _index.html _comme nous le souhaitons. Consultez l'exemple ci-dessous.
ID  -  DATABASE          /       GLOBAL       - Size / Allocated
  {% for global in globals %}
  >
    {{ global.id }} - {{ global.database }}    {{ global.name  }}  -  {{ global.size  }}  /  {{ global.allocatedsize }}
  
  {% endfor %}

 

Récupération de données

Récupération de données
À ce stade, le projet est prêt à recevoir des informations. Il existe de nombreuses façons de modeler ce projet, mais pour ma part, j'utiliserai l'approche de Python afin que nous puissions apprendre une nouvelle solution qu'il est possible d'intégrer à Django et à IRIS.
Nous avons besoin de quelques méthodes pour récupérer toutes les données. Nous pouvons utiliser InterSystems IRIS Cloud SQL avec le pilote DB-API pour nous connecter à l'instance que nous voulons analyser - il n'est pas nécessaire que ce soit la même que celle où nous avons connecté Django.
L'organiser dans un nouveau dossier que nous pouvons traiter comme un module est une bonne pratique. Pour cela, créez le dossier api dans les globaux, ajoutez un fichier vide _init_.py_ pour que Python le reconnaisse comme un module, et commencez à écrire le fichier qui contiendra les méthodes. Nous pouvons l'appeler methods.py.


Création de la connexion

Pour connecter notre environnement Python à l'IRIS d'InterSystems, nous devons suivre quelques étapes décrites dans la section "ObjectScript dans l'environnement Python" de l'article précédent [Python et IRIS dans la pratique] (https://community.intersystems.com/post/python-and-iris-practice-examples).
A partir de maintenant, c'est simple ; nous importons iris, nous passons l'adresse de la connexion (l'instance d'IRIS que nous voulons analyser dans le format suivant : host:port/namespace), un nom d'utilisateur et un mot de passe à la méthode iris.connect et nous créons IRIS de Python. Jetez un coup d'œil au code ci-dessous.

import intersystems_iris as iris
from django.db import connection as djangoconnection

# connection par iris
conn_params = djangoconnection.get_connection_params()
conn_params[“namespace”] = “%SYS”
connection = iris.connect(**conn_params)
irisPy = iris.createIRIS(connection)

 

Obtention des répertoires de bases de données

Puisque nous voulons récupérer les tailles des globales, nous avons besoin (bien sûr) de leurs tailles, de leurs noms et de leurs adresses - ce que nous appelons des bases de données.
Je vais vous montrer une version simplifiée de la fonction, mais rappelez-vous qu'il est préférable de vérifier chaque étape et chaque connexion, et de lancer une exception si quelque chose ne va pas.
Tout comme nous le ferions en ObjectScript, nous avons besoin d'une requête SQL pour pouvoir la préparer, l'exécuter et récupérer une liste contenant tous les répertoires de la base de données dans son jeu de résultats. Nous pouvons faire tout cela facilement avec les fonctions "irisPy.classMethodSomething()", où Something (quelque chose) représente le type que la méthode doit retourner, et irisObject.invoke(), où nous pouvons accéder à n'importe quoi à partir de l'irisObject référencé. Voici un exemple.

defgetAllDatabaseDirectories():try:
	   # vérification de la connexion faite dans irisPy, et si elle est fixée à l'espace de noms %SYS
	   databaseDirectoriesList = []
	   with connection.cursor() as cursor:
		cursor.execute(“SELECT DISTINCT %EXACT(Directory) FROM Config.Databases WHERE SectionHeader = ?”, [“Databases”,],)
		databaseDirectoriesList = [row[0] for row in cursor]

    except Exception as error:
        return str(error)

    return databaseDirectoriesList

La variable statement est définie comme un objet généré par la méthode %New de la classe IRIS %SQL.Statement. Il est alors possible d'invoquer la méthode %Prepare à partir de l'objet instancié, avec une chaîne de requête comme argument. Ensuite, nous pouvons invoquer les méthodes %Execute et %Next pour exécuter la requête et boucler sur l'ensemble des résultats, en ajoutant les informations souhaitées à une liste Python pour un accès facile.
Il est facile de trouver chaque répertoire de base de données dans le tableau Config.Databases, situé uniquement dans l'espace de noms %SYS de chaque instance d'IRIS. Consultez-la dans le portail de gestion si vous le souhaitez, vous y trouverez d'autres informations intéressantes.

Récupération de toutes les globales d'une base de données

Cette fonction est très similaire à la précédente. Cependant, nous disposons maintenant d'une requête de classe prête à l'emploi. Une fois de plus, nous avons besoin d'une requête SQL, nous pouvons donc préparer la requête DirectoryList à partir de la classe %SYS.GlobalQuery. Ensuite, nous l'exécutons avec un répertoire de base de données comme argument et nous récupérons une liste contenant tous les globales de cette base de données.

defgetGlobalsList(databaseDirectory: str):try:
        statement = irisPy.classMethodObject("%SQL.Statement", "%New")
        status = statement.invoke("%PrepareClassQuery", "%SYS.GlobalQuery","DirectoryList")

        result = statement.invoke("%Execute", databaseDirectory)

        globalList = []
        while (result.invoke("%Next")!=0):
            globalList.append(result.invoke("%Get", "Name"))

    except Exception as error:
        return str(error)

    return globalList

Récupération des tailles des globales et des tailles allouées

Enfin, nous pouvons accéder aux informations cibles. Heureusement, IRIS dispose d'une méthode intégrée pour récupérer la taille et la taille allouée à condition de fournir une base de données et une paire de globales.

defgetGlobalSize(databaseDirectory: str, globalName: str):try:
        globalUsed = iris.IRISReference(0)
        globalAllocated = iris.IRISReference(0)
        status = irisPy.classMethodObject("%GlobalEdit", "GetGlobalSize", databaseDirectory, globalName, globalAllocated, globalUsed, 0)

    except Exception as error:
        return str(error)

    return (globalUsed.getValue(), globalAllocated.getValue())

Cette fois, nous avons besoin de la fonction IRISReference(0) du module iris pour recevoir les tailles de la fonction "GetGlobalSize" par référence. Ensuite, nous pouvons accéder à la valeur avec la méthode getValue().

Affichage complet sur la page d'accueil

Enfin, nous pouvons utiliser ces fonctions pour afficher les données sur la page d'accueil. Nous disposons déjà d'un moyen pour accéder aux informations et d'un tableau, il ne nous reste plus qu'à le remplir. Pour cela, je veux créer un bouton de mise à jour.
Tout d'abord, nous ajoutons un lien vers le fichier index.html.

href = "{% url 'update' %}">updatea>body>

Ajoutez le lien vers la liste urlpatterns, dans urls.py.

Add the link to the urlpatterns list, in urls.py.
from .views import home, update
urlpatterns = [
    path('', home),
    path('update', update, name="update"),
]

Ensuite, créez la vue, dans views.py.

from django.shortcuts import render, redirect
from .api.methods import *
defupdate(request):
    irisGlobal.objects.all().delete()
    databaseList = getAllDatabaseDirectories()

    for database in databaseList:
        globalList = getGlobalsList(database)

        for glob in globalList:
            used, allocated = getGlobalSize(database, glob)
            irisGlobal.objects.create(database=database, name=glob, size=used, allocatedsize=allocated)

    return redirect(home)

 

Pour cette vue, nous devons d'abord importer la fonction de redirection de django.shortcuts, ainsi que les méthodes que nous venons de construire.  
C'est une bonne idée de supprimer toutes les données précédentes du tableau afin que les globales éventuellement supprimées disparaissent. Comme le nombre de globales n'est probablement pas gigantesque, il est préférable de procéder ainsi plutôt que de vérifier chaque enregistrement pour voir s'il a été supprimé ou s'il a besoin d'une mise à jour.  
Ensuite, nous obtenons tous les répertoires des bases de données afin de pouvoir, pour chaque base de données, vérifier tous les globales qui s'y trouvent, et pour chaque globale, nous pouvons avoir sa taille utilisée et sa taille allouée.  
À ce stade, le modèle Django est rempli et prêt à récupérer des données, nous redirigeons donc vers la vue d'accueil.  
Si vous accédez à <http://127.0.0.1:8000/globals/> et cliquez sur le lien de mise à jour que nous avons ajouté, la page devrait se recharger et dans quelques secondes, elle affichera la liste des globales, avec ses bases de données, ses tailles et ses tailles allouées, comme dans l'image ci-dessous.


Ajout d'une agrégation

Vous seriez surpris de savoir à quel point il est simple d'ajouter quelques options d'analyse rapide, telles qu'une somme ou un décompte. Il n'est pas nécessaire de maîtriser Django pour créer quelques tableaux de bord sur cette page et, après cette section, vous devriez être en bonne position pour commencer.
Nous savons déjà que la vue accueil est responsable du rendu de l'index. Jusqu'à présent, nous avons généré la variable "globals", contenant toutes les données, et l'avons passée à l'index.html. Nous allons faire quelque chose de similaire, mais avec des fonctions d'agrégation. Nous allons créer une variable pour chaque somme, utiliser les méthodes aggregate() et Sum(), et les ajouter à l'argument "context list" de la fonction "render". Et bien sûr, n'oubliez pas d'importer "Sum" de django.db.models. Consultez la fonction ci-dessous.

defhome(request):
	globals = irisGlobal.objects.all()
	sumSize = globals.aggregate(Sum("size"))
	sumAllocated = globals.aggregate(Sum("allocatedsize"))
	return render(request, "index.html", {"globals": globals, "sumSize": sumSize, "sumAllocated":sumAllocated})

Nous pouvons maintenant l'ajouter au fichier index.html et ajouter quelques paragraphes sous la liste (

élément). Dans ces paragraphes, nous pouvons accéder au décompte de toutes les globales et aux sommes, comme indiqué ci-dessous.

…
  showing results for {{globals.count}} globalsp>total size: {{sumSize.size__sum}}p><total allocated size: {{sumAllocated.allocatedsize__sum}}p>body>html>

Actualisez le lien et vous devriez obtenir le résultat suivant.


La fin... ou presque

Dans cet article, nous avons appris comment InterSystems IRIS stocke les données en mémoire, comment y accéder à partir de Python, comment construire une API, et comment utiliser IRIS comme un système en nuage, de sorte que nous puissions le suivre et l'analyser facilement. Nous pouvons voir à l'horizon des requêtes plus complexes, la création de tableaux de bord, l'automatisation des mises à jour et l'ajout d'un système de notification.
Dans le prochain article, je me rapprocherai de cet horizon, en montrant comment filtrer et ordonner les données avant de les afficher, en ajoutant quelques options modifiables côté client, et enfin, en ajoutant une pincée de CSS pour donner du charme à l'ensemble.
Souhaitez-vous voir quelque chose que je n'ai pas encore dit ? N'hésitez pas à me contacter si vous avez des idées ou des besoins sur lesquels vous aimeriez que j'écrive.

0
0 171
Article Lorenzo Scalese · Juin 30, 2023 5m read

Si vous déployez dans plus d'un environnement/région/cloud/client, vous rencontrerez inévitablement le problème de la gestion de la configuration.

Alors que tous vos déploiements (ou juste certains) peuvent partager le même code source, certaines parties, telles que la configuration (paramètres, mots de passe) diffèrent d'un déploiement à l'autre et doivent être gérées différemment.

Dans cet article, j'essaierai d'offrir quelques conseils à ce sujet. Cet article traite principalement des déploiements de conteneurs.

Unification de la base de code

Avant d'aborder la gestion de la configuration, parlons de l'unification des bases de code. Le problème est le suivant : la base de code doit généralement viser à coalescer en une seule version. Bien sûr, à tout moment, vous aurez plusieurs versions de votre base de code - la version DEV avec de toutes nouvelles fonctionnalités, la version TEST avec du code de test supplémentaire, la version PROD, et ainsi de suite. Et c'est fine parce que, dans cet exemple, les changements se regroupent en une seule version au fil du temps. Plusieurs branches DEV deviennent une branche TEST et ainsi de suite.

Le problème commence lorsque nous avons plusieurs versions à l'étape finale de notre pipeline de déploiement. Par exemple, le client ABC a demandé une fonctionnalité particulière XYZ, et il existe maintenant deux versions de la base de code PROD - avec la fonctionnalité XYZ et sans elle. Cette situation est problématique car elle double immédiatement nos délais de construction et notre consommation de ressources. Bien sûr, il n'y a parfois aucun moyen d'y remédier. Cependant, supposons que vous ayez de telles divergences persistantes entre les bases de code. Dans ce cas, il peut être intéressant de rechercher si vous pouvez les regrouper en une seule version - dans ce scénario, la fonctionnalité XYZ est activée ou non au démarrage.

Cela dit, passons à la gestion de la configuration.

 

Gestion de la configuration

Qu'est-ce que nous voulons ?

 * Ne pas stocker toutes les configurations possibles dans un seul conteneur ( pour une part, ce n'est pas sûr, et pour une autre part, nous avons toujours besoin de choisir la configuration à appliquer).
 * Ne pas construire un conteneur pour chaque configuration (simplifie le pipeline CD)

Nous devons transmettre la configuration (paramètres, secrets, ...) lors du démarrage d'InterSystems IRIS et l'appliquer à notre application.

Transmission de la configuration

Il existe plusieurs façons de transmettre la configuration au démarrage.

Les variables d'environnement

Simple et rapide. Dans le cas où vous souhaitez tout stocker dans une seule variable, utilisez JSON pour la sérialisation. N'oubliez pas par ailleurs que Windows a une limite de 32K caractères pour les variables d'environnement (mégaoctets sous Linux). Pour récupérer une variable d'environnement, utilisez $SYSTEM.Util.GetEnviron("ENV_VAR\NAME"). Le principal avantage est la facilité de mise en œuvre. Vérifiez également votre outil CD - il a généralement un minimum de support des variables d'environnement pendant l'exécution du pipeline, parfois avec un support des secrets pour plus de sécurité.

Les fichiers de montage

La configuration peut être un fichier de montage au démarrage. L'avantage est qu'il n'y a pas de limite de longueur. L'inconvénient est que toutes les offres en nuage ne prennent pas en charge les fichiers de montage, et même si c'est le cas, la gestion des fichiers peut s'avérer plus délicate que celle des variables d'environnement. Sachez que les fichiers peuvent toujours être récupérés à partir des anciennes couches d'un conteneur, même s'ils ont été supprimés à une couche ultérieure.

Fusion du CPF

Les paramètres du système peuvent être transmis via le Fichier de fusion de configuration. Ainsi que ses fonctionnalités intégrées. Inconvénient : Le fichier de fusion CPF ne traite pas les paramètres au niveau de l'application.

Les secrets de Docker/CSP

Docker peut monter un secret sous une forme de fichier à l'intérieur d'un conteneur. Les offres en nuage proposent souvent une fonctionnalité similaire. Utilisez-la à la place des fichiers ordinaires pour améliorer la sécurité.

Traitement de la configuration

Bien, vous avez passé la configuration à l'intérieur d'un conteneur. Que faire maintenant ?

%ZSTART

%ZSTART est votre code personnalisé, exécuté au démarrage du système. Il ressemble à ceci :

SYSTEM
    try {
        new $namespace
        set $namespace = "USER"
        // apply configuration
        set $namespace = "%SYS"
    } catch ex {
        zn "%SYS"
        do ex.Log()
    }
    quit 1

Il est essentiel de noter que %ZSTART doit être testé complètement - car InterSystems IRIS ne démarrera pas (ou se comportera de manière erratique) si la routine %ZSTART renvoie une erreur.

Vous êtes maintenant prêt à appliquer vos paramètres.

La manière dont vous stockez/appliquez les paramètres au niveau de l'application dépend naturellement de l'application, mais les paramètres par défaut du systèmeSystem Default Settings{.editor-rtfLink} sont disponibles si vous utilisez l'interopérabilité. Même si vous n'utilisez pas de productions interopérables, vous pouvez toujours utiliser les paramètres par défaut du système car ils sont disponibles pour tout le monde (tant que l'interopérabilité est activée dans un espace de noms).

Pour créer un nouveau paramètre, exécutez :

Set setting = ##class(Ens.Config.DefaultSettings).%New()
Set setting.ProductionName = production
Set setting.ItemName = itemname
Set setting.HostClassName = hostclassname
Set setting.SettingName = settingname
Set setting.SettingValue = settingvalue
Set setting.Description = description
Set sc = setting.%Save()

Et pour récupérer les paramètres, appelez :

set sc = ##class(Ens.Config.DefaultSettings).%GetSetting(production, itemname, hostclassname, "", settingname, .settingvalue)
set settingvalue = ##class(Ens.Director).GetProductionSettingValue(production, settingname, .sc)

Les hôtes d'interopérabilité reçoivent leurs paramètres automatiquement au démarrage du travail de l'hôte commercial.

Summary

Making your application configurable when deploying to more than one server is essential. It can be pretty easy at the early part of the development cycle, but the costs rise with each hardcoded URL. InterSystems IRIS offers several tools you can use to configure the state of your application at startup.

How do you manage multiple deployments?

0
0 63
Article Lorenzo Scalese · Juin 23, 2023 8m read

Si vous utilisez IRIS dans une configuration miroir pour HA dans AWS, la question de la fourniture d'un Miroir VIP (IP virtuelle) devient pertinente. L'IP virtuelle permet aux systèmes en aval d'interagir avec IRIS en utilisant une seule adresse IP. Même en cas de basculement, les systèmes en aval peuvent se reconnecter à la même adresse IP et continuer à travailler.

Le principal problème, lors du déploiement sur AWS, est qu'un VIP IRIS exige que les deux membres du miroir soient dans le même sous-réseau, d'après les docs :

Pour utiliser un miroir VIP, les deux membres du basculement doivent être configurés dans le même sous-réseau et le VIP doit appartenir au même sous-réseau que l'interface réseau sélectionnée sur chaque système.

Cependant, pour obtenir l'HA, les membres du miroir IRIS doivent être déployés dans des zones de disponibilité différentes, ce qui signifie des sous-réseaux différents (car les sous-réseaux ne peuvent être que dans un seul az). L'une des solutions pourrait être les équilibreurs de charge, mais ils (A) coûtent cher, et (B) si vous devez acheminer du trafic non-HTTP (comme TCP pour HL7), vous devrez utiliser des équilibreurs de charge de réseau avec une limite de 50 ports au total.

Dans cet article, je voudrais fournir un moyen de configurer un Mirror VIP sans utiliser l'équilibrage de la charge du réseau suggéré dans la plupart des autres architectures de référence AWS. En production, nous avons trouvé des limitations qui entravaient les solutions avec le coût, les limites de 50 auditeurs, les dépendances DNS et la nature dynamique des deux adresses IP qu'AWS fournit à travers les zones de disponibilité.

Architecture

Architecture(4)

Nous avons un VPC avec trois sous-réseaux privés (je simplifie ici - bien sûr, vous aurez probablement des sous-réseaux publics, un arbitre dans une autre AZ, et ainsi de suite, mais c'est un minimum absolu suffisant pour démontrer cette approche). Le VPC se voit allouer des IPs : 10.109.10.1 à 10.109.10.254 ; les sous-réseaux (dans différents AZs) sont : 10.109.10.1 à 10.109.10.62, 10.109.10.65 à 10.109.10.126, et 10.109.10.224 à 10.109.10.254.

Implementation d'un VIP

  1. Sur chaque instance EC2 (SourceDestCheck doit être définie sur false), nous allons allouer la même adresse IP sur l'interface réseau eth0:1. Cette adresse IP se trouve dans la plage CIDR du VPC - dans une zone VIP spéciale. Par exemple, nous pouvons utiliser la dernière IP d'une plage - 10.109.10.254 :
cat << EOFVIP >> /etc/sysconfig/network-scripts/ifcfg-eth0:1
          DEVICE=eth0:1
          ONPARENT=on
          IPADDR=10.109.10.254
          PREFIX=27
          EOFVIP
sudo chmod -x /etc/sysconfig/network-scripts/ifcfg-eth0:1
sudo ifconfig eth0:1 up

En fonction du système d'exploitation, vous pouvez avoir besoin d'exécuter :

ifconfig eth0:1
systemctl restart network
  1. En cas de basculement du miroir, mettre à jour la table de routage pour qu'elle pointe vers l'eni sur le nouveau primaire. Nous utiliserons un rappel ZMIRROR pour mettre à jour le tableau de routage après que le membre du miroir actuel soit devenu le primaire. Ce code utilise Python intégré pour :
  • Obtenir l'IP sur eth0:1
  • Obtenir l'InstanceId et la Région à partir des métadonnées de l'instance
  • Trouver le tableau des routes principales pour le VPC EC2
  • Supprimer l'ancienne route, s'il y en a une
  • Ajouter une nouvelle route pointant vers elle-même

Le code:

import os
import urllib.request
import boto3
from botocore.exceptions import ClientError

PRIMARY_INTERFACE = 'eth0'
VIP_INTERFACE = 'eth0:1'

eth0_addresses = [
    line
    for line in os.popen(f'ip -4 addr show dev {PRIMARY_INTERFACE}').read().split('\n')
    if line.strip().startswith('inet')
]

VIP = None
for address in eth0_addresses:
    if address.split(' ')[-1] == VIP_INTERFACE:
        VIP = address.split(' ')[5]

if VIP is None:
    raise ValueError('Échec de la récupération d'un VIP valide !')

# Recherche de l'ID de l'instance du membre du miroir actuel
instanceid = (
    urllib.request.urlopen('http://169.254.169.254/latest/meta-data/instance-id')
    .read()
    .decode()
)

region = (
    urllib.request.urlopen('http://169.254.169.254/latest/meta-data/placement/region')
    .read()
    .decode()
)

session = boto3.Session(region_name=region)
ec2Resource = session.resource('ec2')
ec2Client = session.client('ec2')
instance = ec2Resource.Instance(instanceid)

# Recherche de l'ID du tableau de routage principal pour ce VPC
vpc = ec2Resource.Vpc(instance.vpc.id)

for route_table in vpc.route_tables.all():
    # Mise à jour du tableau de routage principal pour pointer vers cette instance
    try:
        ec2Client.delete_route(
            DestinationCidrBlock=VIP, RouteTableId=str(route_table.id)
        )
    except ClientError as exc:
        if exc.response['Error']['Code'] == 'InvalidRoute.NotFound':
            print('Rien à supprimer, continuer')
        else:
            raise exc
    # Ajout de la nouvelle route
    ec2Client.create_route(
        DestinationCidrBlock=VIP,
        NetworkInterfaceId=instance.network_interfaces[0].id,
        RouteTableId=str(route_table.id),
    )

et le même code comme routine ZMIRROR :

NotifyBecomePrimary() PUBLIC {
  try {
    set dir = $system.Util.ManagerDirectory()_ "python"
    do ##class(%File).CreateDirectoryChain(dir)

    try {
      set boto3 = $system.Python.Import("boto3")
    } catch {
      set cmd = "pip3"
      set args($i(args)) = "install"
      set args($i(args)) = "--target"
      set args($i(args)) = dir
      set args($i(args)) = "boto3"
      set sc = $ZF(-100,"", cmd, .args)
      // pour python précédant la version 3.7, installer également dataclasses
      set boto3 = $system.Python.Import("boto3")
    }
    kill boto3

    set code =  "import os" _ $c(10) _
				"import urllib.request" _ $c(10) _
				"import boto3" _ $c(10) _
				"from botocore.exceptions import ClientError" _ $c(10) _
				"PRIMARY_INTERFACE = 'eth0'" _ $c(10) _
				"VIP_INTERFACE = 'eth0:1'" _ $c(10) _
				"eth0_addresses = [" _ $c(10) _
				"    line" _ $c(10) _
				"    for line in os.popen(f'ip -4 addr show dev {PRIMARY_INTERFACE}').read().split('\n')" _ $c(10) _
				"    if line.strip().startswith('inet')" _ $c(10) _
				"]" _ $c(10) _
				"VIP = None" _ $c(10) _
				"for address in eth0_addresses:" _ $c(10) _
				"    if address.split(' ')[-1] == VIP_INTERFACE:" _ $c(10) _
				"        VIP = address.split(' ')[5]" _ $c(10) _
				"if VIP is None:" _ $c(10) _
				"    raise ValueError('Échec de la récupération d'un VIP valide !')" _ $c(10) _
				"# Recherche de l'ID de l'instance du membre du miroir actuel" _ $c(10) _
				"instanceid = (" _ $c(10) _
				"    urllib.request.urlopen('http://169.254.169.254/latest/meta-data/instance-id')" _ $c(10) _
				"    .read()" _ $c(10) _
				"    .decode()" _ $c(10) _
				")" _ $c(10) _
				"region = (" _ $c(10) _
				"    urllib.request.urlopen('http://169.254.169.254/latest/meta-data/placement/region')" _ $c(10) _
				"    .read()" _ $c(10) _
				"    .decode()" _ $c(10) _
				")" _ $c(10) _
				"session = boto3.Session(region_name=region)" _ $c(10) _
				"ec2Resource = session.resource('ec2')" _ $c(10) _
				"ec2Client = session.client('ec2')" _ $c(10) _
				"instance = ec2Resource.Instance(instanceid)" _ $c(10) _
				"# Recherche de l'ID du tableau de routage principal pour ce VPC" _ $c(10) _
				"vpc = ec2Resource.Vpc(instance.vpc.id)" _ $c(10) _
				"for route_table in vpc.route_tables.all():" _ $c(10) _
				"    # Mise à jour du tableau de routage principal pour pointer vers cette instance" _ $c(10) _
				"    try:" _ $c(10) _
				"        ec2Client.delete_route(" _ $c(10) _
				"            DestinationCidrBlock=VIP, RouteTableId=str(route_table.id)" _ $c(10) _
				"        )" _ $c(10) _
				"    except ClientError as exc:" _ $c(10) _
				"        if exc.response['Error']['Code'] == 'InvalidRoute.NotFound':" _ $c(10) _
				"            print('Rien à supprimer, continuer')" _ $c(10) _
				"        else:" _ $c(10) _
				"            raise exc" _ $c(10) _
				"    # Ajout de la nouvelle route" _ $c(10) _
				"    ec2Client.create_route(" _ $c(10) _
				"        DestinationCidrBlock=VIP," _ $c(10) _
				"        NetworkInterfaceId=instance.network_interfaces[0].id," _ $c(10) _
				"        RouteTableId=str(route_table.id)," _ $c(10) _
				"    )"


    set rc = $system.Python.Run(code)
    set sc = ##class(%SYS.System).WriteToConsoleLog("Attribution VIP " _ $case(rc, 0:"successful", :"error"), , $case(rc, 0:0, :1), "NotifyBecomePrimary:ZMIRROR")

  } catch ex {
    #dim ex As %Exception.General
    do ex.Log()
    set sc = ##class(%SYS.System).WriteToConsoleLog("Une exception a été détectée lors de I'attribution d'un VIP : " _ ex.DisplayString(), , 1, "NotifyBecomePrimary:ZMIRROR")
  }
  quit 1
}

Démarrage initial

NotifyBecomePrimary est aussi appelé automatiquement au démarrage du système (après la reconnexion des miroirs), mais si vous voulez que vos environnements non-miroirs acquièrent VIP de la même manière, utilisez la routine ZSTART routine:

SYSTEM() PUBLIC {
  if '$SYSTEM.Mirror.IsMember() {
    do NotifyBecomePrimary^ZMIRROR()
  }
  quit 1
}

Suppression

Si vous utilisez des outils de provisionnement automatique, comme CloudFormation, cette route doit être supprimée avant de pouvoir supprimer le sous-réseau. Vous pouvez ajouter le code de suppression à ^%ZSTOP, mais n'oubliez pas de vérifier $SYSTEM.Mirror.IsPrimary() parce que lorsque le miroir primaire s'arrête, pendant ^%ZSTOP il est toujours primaire. De manière générale, je recommanderais la suppression des routes externes dans le cadre d'un script d'outils de provisionnement.

Conclusion

Et c'est tout ! Dans le tableau de routage, nous obtenons une nouvelle route pointant vers un miroir primaire Primary actuel lorsque l'événement NotifyBecomePrimary se produit.

image

L'auteur tient à remercier Jared Trog et @sween pour la création de cette approche.

L'auteur tient à remercier @Tomohiro Iwamoto pour avoir testé cette approche et déterminé toutes les conditions requises pour qu'elle fonctionne.

0
0 131
Article Lorenzo Scalese · Mai 31, 2023 5m read

Les systèmes de bases de données ont des exigences de sauvegarde très spécifiques qui, dans les déploiements d'entreprise, nécessitent une réflexion et une planification préalables. Pour les systèmes de bases de données, l'objectif opérationnel d'une solution de sauvegarde est de créer une copie des données dans un état équivalent à celui de l'arrêt de l'application en douceur.  Les sauvegardes cohérentes avec les applications répondent à ces exigences et Caché fournit un ensemble d'API qui facilitent l'intégration avec des solutions externes pour atteindre ce niveau de cohérence des sauvegardes.

Ces API sont ExternalFreeze et ExternalThaw. ExternalFreeze met temporairement en pause les écritures sur le disque et pendant cette période, Caché commet les changements en mémoire. Pendant cette période, l'opération de sauvegarde doit se terminer et être suivie d'un appel à ExternalThaw. Cet appel engage les démons d'écriture à écrire sur le disque la mise à jour du pool tampon global (cache de la base de données) et reprend les opérations normales des démons d'écriture de la base de données Caché. Ce processus est transparent pour les processus utilisateur de Caché.  Les méthodes spécifiques de la classe API sont les suivantes :

##Class(Backup.General).ExternalFreeze()

##Class(Backup.General).ExternalThaw()

Ces API, associées à la nouvelle capacité d'Azure Backup à exécuter un script avant et après l'exécution d'une opération d'instantané, fournissent une solution de sauvegarde complète pour les déploiements de Caché sur Azure. La [capacité de script pré/post d'Azure Backup] (https://azure.microsoft.com/en-us/blog/announcing-application-consistent-backup-for-linux-vms-using-azure-backup) est actuellement disponible uniquement sur les VM Linux.

Conditions préalables

Au niveau le plus élevé, vous devez effectuer trois étapes avant de pouvoir sauvegarder une VM à l'aide d'Azure Backup:

  1. Create a Recovery Services vault
  2. Install has the latest version of the VM Agent.
  3. Check network access to the Azure services from your VM. 

L'espace de stockage Recovery Services gère les objectifs de sauvegarde, les politiques et les éléments à protéger. Vous pouvez créer une voûte de Recovery Services via le portail Azure ou via un script utilisant PowerShell.  Azure Backup nécessite une extension qui s'exécute dans votre VM, est contrôlée par l'agent Linux VM et la dernière version de l'agent est également nécessaire.  L'extension interagit avec les points d'extrémité HTTPS externes d'Azure Storage et de la voûte Recovery Services.  L'accès sécurisé à ces services depuis la VM peut être configuré à l'aide d'un proxy et de règles réseau dans un groupe de sécurité Azure Network Security Group. 

Vous trouverez plus d'informations sur ces étapes dans la section Préparez votre environnement pour sauvegarder les machines virtuelles déployées par Resource Manager.

La Configuration du pré-scripting et post-scripting

La possibilité d'appeler un script avant et après l'opération de sauvegarde est incluse dans la dernière version de l'extension Azure Backup (Microsoft.Azure.RecoveryServices.VMSnapshotLinux). Pour plus d'informations sur l'installation de l'extension, veuillez consulter [la documentation détaillée des fonctionnalités] (https://docs.microsoft.com/en-us/azure/backup/backup-azure-linux-app-consistent).

Par défaut, l'extension inclut des exemples de pré-scripts et post-scripts situés dans votre VM Linux à l'adresse suivante :

/var/lib/waagent/Microsoft.Azure.RecoveryServices.VMSnapshotLinux-1.0.9110.0/main/tempPlugin

Et doit être copié aux emplacements suivants respectivement.

/etc/azure/prescript.sh
/etc/azure/postScript.sh

Vous pouvez également télécharger le modèle de script à partir de GitHub.

Pour Caché, le script prescript.sh où un appel à l'API ExternalFreeze peut être implémenté et le postScript.sh doivent contenir l'implémentation qui exécute ExternalThaw.

Voici un exemple d'implémentation de prescript.sh pour Caché.

#!/bin/bash
# les variables utilisées pour retourner l'état du script
success=0
error=1
warning=2
status=$success
log_path="/etc/preScript.log"#path of log file
printf  "Logs:\n" > $log_path# TODO: Replace &lt;CACHE INSTANCE> with the name of the running instance
csession &lt;CACHE INSTANCE> -U%SYS "##Class(Backup.General).ExternalFreeze()" >> $log_path
status=$?if [ $status -eq 5 ]; then
echo "SYSTEM IS FROZEN"
printf  "SYSTEM IS FROZEN\n" >> $log_pathelif [ $status -eq 3 ]; then
echo "SYSTEM FREEZE FAILED"
printf  "SYSTEM FREEZE FAILED\n" >> $log_path
status=$error
csession &lt;CACHE INSTANCE> -U%SYS "##Class(Backup.General).ExternalThaw()"
fi

exit $status

Voici un exemple d'implémentation de postScript.sh pour Caché.

#!/bin/bash
# les variables utilisées pour retourner l'état du script
success=0
error=1
warning=2
status=$success
log_path="/etc/postScript.log"#path of log file
printf  "Logs:\n" > $log_path# TODO: Replace &lt;CACHE INSTANCE> with the name of the running instance
csession &lt;CACHE INSTANCE> -U%SYS "##class(Backup.General).ExternalThaw()"
status=$?
if [ $status req 5]; then
echo "SYSTEM IS UNFROZEN"
printf  "SYSTEM IS UNFROZEN\n" >> $log_pathelif [ $status -eq 3 ]; then
echo "SYSTEM UNFREEZE FAILED"
printf  "SYSTEM UNFREEZE FAILED\n" >> $log_path
status=$error
fi
exit $status

Exécution d'une sauvegarde

Dans le portail Azure, vous pouvez déclencher la première sauvegarde en naviguant vers le service de restauration. Veuillez considérer que le temps d'instantané de la VM devrait être de quelques secondes indépendamment de la première sauvegarde ou des sauvegardes suivantes. Le transfert de données de la première sauvegarde prendra plus de temps mais le transfert de données commencera après l'exécution du post-script pour dégeler la base de données et ne devrait pas avoir d'impact sur le temps entre le pré-script et le post-script.

Il est fortement recommandé de restaurer régulièrement votre sauvegarde dans un environnement de non-production et [d'effectuer des contrôles d'intégrité de la base de données] (http://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCDI_integrity#GCDI_integrity_verify_utility) pour garantir l'efficacité de vos opérations de protection des données.

Pour plus d'informations sur le déclenchement de la sauvegarde et d'autres sujets tels que la planification de la sauvegarde, veuillez consulter Sauvegarde des machines virtuelles Azure dans une voûte Recovery Services.  

0
0 60
Article Lorenzo Scalese · Mai 17, 2023 16m read

Nos clients ont souvent besoin de configurer HealthShare HealthConnect et IRIS en mode haute disponibilité.

D'autres moteurs d'intégration sur le marché sont souvent présentés comme ayant des configurations de "haute disponibilité", mais ce n'est pas vraiment le cas. En général, ces solutions fonctionnent avec des bases de données externes et donc, si celles-ci ne sont pas configurées en haute disponibilité, lorsqu'un crash de la base de données se produit ou que la connexion à celle-ci est perdue, l'ensemble de l'outil d'intégration devient inutilisable.

Dans le cas des solutions InterSystems, ce problème n'existe pas, car la base de données fait partie intégrante des outils eux-mêmes. Et comment InterSystems a-t-il résolu le problème de la haute disponibilité ? Par des configurations absconses qui pourraient nous entraîner dans une spirale d'aliénation et de folie ? NON ! Chez InterSystems, nous avons écouté et pris en compte vos plaintes (comme nous essayons toujours de le faire ;) ) et nous avons mis la fonction mirroring (mise en miroir) à la disposition de tous nos utilisateurs et développeurs.

Mirroring

Comment fonctionne le Miroir ? Le concept lui-même est très simple. Comme vous le savez déjà, IRIS et HealthShare fonctionnent avec un système de journalisation qui enregistre toutes les opérations de mise à jour des bases de données de chaque instance. Ce système de journalisation est celui qui nous aide à récupérer les instances sans perte de données après un crash. Ces fichiers journaux sont envoyés entre les instances configurées en miroir, ce qui permet aux instances configurées en miroir d'être mises à jour en permanence.

Architecture

Décrivons brièvement l'architecture d'un système configuré en miroir :

  • Deux instances configurées en mode basculement :
    • Nœud actif : reçoit toutes les opérations régulières de lecture/écriture.
    • Nœud passif : en mode lecture, il reçoit de manière synchrone toutes les modifications produites dans le nœud actif.
  • 0-14 instances asynchrones : autant d'instances asynchrones que vous souhaitez utiliser; elles peuvent être de deux types :
    • DR async (reprise après sinistre) : nœuds en mode lecture qui ne font pas partie du basculement, bien qu'ils puissent être transférés manuellement. Si tel est le cas, ils pourraient être automatiquement transférés vers le nœud principal en cas de défaillance des deux autres nœuds de basculement. La mise à jour de vos données se fait en mode asynchrone, leur fraîcheur n'est donc pas garantie.
    • Reporting Asyncs (rapports asynchrones) : Nœuds mis à jour de manière asynchrone pour une utilisation dans des tâches de BI ou d'exploration de données. Ils ne peuvent pas être transférés vers le basculement car des écritures peuvent être effectuées sur les données.
  • ISCAgent : Installé sur chaque serveur où se trouve une instance. Il sera chargé de surveiller l'état des instances de ce serveur. C'est un autre moyen de communication entre les Serveurs Miroirs en plus de la communication directe.
  • Arbiter : il s'agit d'un ISCAgent installé indépendamment par rapport aux serveurs qui composent le Miroir et qui permet d'augmenter la sécurité et le contrôle des bascules au sein de celui-ci en surveillant les ISCAgents installés et les instances d'IRIS/HealthShare. Son installation n'est pas obligatoire.

Il s'agirait du fonctionnement d'un Miroir formé par un basculement avec seulement deux nœuds :

Dans un miroir InterSystems IRIS, lorsque le primaire devient indisponible, le miroir bascule sur le backup.

Avertissement préalable

Le projet associé à cet article n'a pas de licence active permettant la configuration du miroir. Si vous voulez l'essayer, envoyez-moi un email directement ou ajoutez un commentaire en bas de l'article et je vous contacterai.

Déploiement dans Docker

Pour cet article, nous allons mettre en place un petit projet dans Docker qui nous permet de mettre en place 2 instances de basculement avec un Arbiter. Par défaut, les images IRIS disponibles pour Docker ont l'ISCAgent déjà installé et configuré, nous pouvons donc sauter cette étape. Il sera nécessaire de configurer le projet associé à l'article à partir d'un code Visual Studio, car cela nous permettra de travailler plus confortablement avec les fichiers du serveur par la suite.

Voyons quelle forme aurait notre docker-compose.yml :

version:'3.3'services:  arbiter:      container_name:arbiter      hostname:arbiter      image:containers.intersystems.com/intersystems/arbiter:2022.1.0.209.0      init:true      command:        -/usr/local/etc/irissys/startISCAgent.sh2188  mirrorA:    image:containers.intersystems.com/intersystems/iris:2022.1.0.209.0    container_name:mirrorA    depends_on:      -arbiter    ports:    -"52775:52773"    volumes:    -./sharedA:/shared    -./install:/install    -./management:/management    command:      --check-capsfalse      --key/install/iris.key      -a/install/installer.sh    environment:    -ISC_DATA_DIRECTORY=/shared/durable    hostname:mirrorA  mirrorB:    image:containers.intersystems.com/intersystems/iris:2022.1.0.209.0    container_name:mirrorB    depends_on:      -arbiter      -mirrorA    ports:    -"52776:52773"    volumes:    -./sharedB:/shared    -./install:/install    -./management:/management    command:      --check-capsfalse      --key/install/iris.key      -a/install/installer.sh    environment:    -ISC_DATA_DIRECTORY=/shared/durable    hostname:mirrorB

Nous pouvons remarquer que nous avons défini 3 conteneurs :

  • Arbiter : il correspond à l'ISCAgent (même si l'image s'appelle Arbiter) qui sera déployé pour contrôler les instances IRIS qui formeront le Mirror Failover (basculement miroir). Au démarrage du conteneur, il exécutera un fichier shell qui démarrera l'ISCAgent écoutant sur le port 2188 du conteneur.
  • mirrorA : conteneur dans lequel l'image IRIS v.2022.1.0.209 sera déployée et que nous configurerons ultérieurement en tant que nœud de basculement primaire.
  • mirrorB : conteneur dans lequel l'image IRIS v.2022.1.0.209 sera déployée et que nous configurerons ultérieurement en tant que nœud de basculement secondaire.

Lorsque nous exécutons la commande docker-compose up -d, les conteneurs définis seront déployés dans notre Docker, et cela devrait ressembler à ceci dans notre Docker Desktop (si nous le faisons à partir de Windows).

Configuration du miroir.

Avec nos conteneurs déployés, nous allons procéder à l'accès aux instances que nous allons configurer en miroir, la première sera à l'écoute sur le port 52775 (mirrorA) et la seconde sur 52776 (mirrorB). L'utilisateur et le mot de passe d'accès seront superuser / SYS

Du fait que les instances sont déployées dans Docker, nous aurons deux options pour configurer les IP de nos serveurs. La première est d'utiliser directement le nom de nos conteneurs dans la configuration (ce qui est le plus simple) ou de vérifier les IP que Docker a attribuées pour chaque conteneur (en ouvrant la console et en exécutant un ifconfig qui renvoie l'IP attribuée). Pour des raisons de clarté, nous utiliserons pour l'exemple les noms que nous avons donnés à chaque conteneur comme adresse de chacun au sein de Docker.

Tout d'abord, nous allons configurer l'instance que nous utiliserons comme nœud actif du basculement (FailOver). Dans notre cas, ce sera ce que nous avons appelé mirrorA.

La première étape consistera à activer le service de mise en miroir, ce qui nous permettra d'accéder au menu de mise en miroir à partir du portail de gestion : Administration du système --> Configuration --> Paramètres du miroir --> Activer le miroirService et cochez la case Service activé :

Une fois le service activé, nous pouvons commencer à configurer notre nœud actif. Après avoir activé le service, vous pourrez voir que de nouvelles options ont été activées dans le menu Miroir :

Dans ce cas, comme nous n'avons pas de configuration de miroir déjà créée, nous devons en créer une nouvelle avec l'option Créer un miroir. Lorsque nous accédons à cette option, le portail de gestion ouvrira une nouvelle fenêtre à partir de laquelle nous pourrons configurer notre miroir :

Examinons de plus près chacune des options :

  • Nom du miroir : le nom avec lequel nous identifierons notre miroir. Pour notre exemple, nous l'appellerons MIRRORSET. ** Nécessite le SSL/TLS** : pour notre exemple, nous ne configurerons pas de connexion utilisant le SSL/TLS, bien que dans les environnements de production, il serait plus que pratique d'éviter que le fichier journal soit partagé sans aucun type d'encryptage entre les instances. Si vous souhaitez configurer cette connexion, vous avez tous les renseignements nécessaires à l'adresse URL de la documentation.
  • Use Arbiter : cette option n'est pas obligatoire, mais elle est fortement recommandée, car elle ajoute une couche de sécurité à notre configuration de miroir. Pour notre exemple, nous la laisserons cochée et nous indiquerons l'IP dans laquelle nous avons notre Arbiter en cours d'exécution. Pour notre exemple, l'IP sera dans le nom du conteneur arbiter.
  • User Virtual IP : dans les environnements Linux/Unix, cette option est très intéressante car elle nous permet de configurer une IP virtuelle pour notre nœud actif qui sera géré par notre miroir. Cette IP virtuelle doit appartenir au même sous-réseau que les nœuds de basculement. Le fonctionnement de l'IP virtuelle est très simple, en cas de défaillance du nœud actif le miroir configurera automatiquement l'IP virtuelle sur le serveur où se trouve le nœud passif à promouvoir. De cette manière, la promotion du nœud passif en nœud actif sera complètement transparente pour les utilisateurs, puisqu'ils continueront à être connectés à la même IP, même si elle sera configurée sur un serveur différent. Si vous souhaitez en savoir plus sur l'IP virtuelle, vous pouvez consulter cette URL de la documentation.

Le reste de la configuration peut être laissé tel quel. Sur le côté droit de l'écran, nous verrons les renseignements relatifs à ce nœud dans le miroir :

  • Nom du membre du miroir : nom de ce membre du miroir, par défaut il prendra le nom du serveur avec le nom de l'instance.
  • Superserver Address : Adresse IP du super-serveur de ce nœud, dans notre cas, mirrorA.
  • Port de l'agent : port dans lequel l'agent ISCAgent correspondant à ce nœud a été configuré. Par défaut 2188.

Une fois les champs nécessaires configurés, nous pouvons procéder à la sauvegarde du miroir. Nous pouvons vérifier comment la configuration s'est déroulée à partir du moniteur du miroir (Opération du système --> Moniteur du miroir).

Parfait, nous avons notre miroir nouvellement configuré. Comme vous pouvez le constater, seul le nœud actif que nous venons de créer apparaît. Très bien, allons donc ajouter notre nœud passif dans le Failover. Nous accédons au portail de gestion mirrorB et au menu Mirror Settings. Comme nous l'avons déjà fait pour l'instance mirrorA, nous devons activer le service Mirror. Nous répétons l'opération et dès que les options du menu seront mises à jour, nous choisirons Join as Failover (Rejoindre en tant que basculement).

Nous avons ici l'écran de connexion au miroir. Expliquons brièvement la signification de chacun des champs :

  • Nom du miroir : nom que nous avons donné au miroir au moment de sa création, dans notre exemple MIRRORSET.
  • Adresse de l'agent sur l'autre système : IP du serveur où l'ISCAgent du nœud actif est déployé, pour nous ce sera mirrorA.
  • Port de l'agent : port d'écoute de l'ISCAgent du serveur dans lequel nous avons créé le miroir. Par défaut 2188.
  • Nom de l'instance IRIS d'InterSystems : le nom de l'instance IRIS sur le nœud actif. Dans ce cas, il coïncide avec celui du nœud passif, IRIS.

Après avoir enregistré les données du miroir, nous aurons la possibilité de définir les renseignements relatifs au nœud passif que nous sommes en train de configurer. Examinons à nouveau les champs que nous pouvons configurer pour le nœud passif :

  • Nom du membre du miroir : nom que le nœud passif prendra dans le miroir. Par défaut, il est formé par le nom du serveur et de l'instance. Superserver Address : Adresse IP du super-serveur dans notre nœud passif. Dans ce cas mirrorB.
  • Port de l'agent** : port d'écoute de l'agent ISCAgent installé sur le serveur du nœud passif que nous sommes en train de configurer. Par défaut 2188. SSL/TLS Requirement : non configurable dans cet exemple, nous n'utilisons pas SSL/TLS. Adresse privée du miroir : Adresse IP du nœud passif. Comme nous l'avons vu, lors de l'utilisation de Docker, nous pouvons utiliser le nom du conteneur mirrorB. Adresse de l'agent : Adresse IP du serveur où l'ISCAgent est installé. Même chose que précédemment, mirrorB.

Nous enregistrons la configuration comme nous l'avons indiqué et nous retournons au moniteur du miroir pour vérifier que nous avons tout configuré correctement. Nous pouvons visualiser le moniteur du nœud actif dans miroirA et du nœud passif dans miroirB. Examinons les différences entre les deux instances.

Moniteur miroir sur le nœud actif mirrorA :

Moniteur du miroir sur le nœud passif mirrorB:

Comme vous pouvez le constater, les renseignements affichés sont similaires, il s'agit essentiellement de changer l'ordre des membres du basculement. Les options sont également différentes, examinons-en quelques-unes :

  • Nœud actif mirrorA :
    • Set No Failover (Configurer pas de basculement) : empêche l'exécution du basculement dans le cas d'un arrêt de l'une des instances qui en font partie.
    • Demote other member (Démonter l'autre membre) : Supprime l'autre membre du basculement (dans ce cas mirrorB) de la configuration du miroir.
  • Nœud passif mirrorB :
    • Stop Mirror On This Member (Supprimer le miroir sur ce membre) : arrête la synchronisation du miroir sur le membre de basculement (ici mirrorB) : Arrête la synchronisation du miroir sur le nœud passif de basculement.
    • Demote To DR Member (Rétrograder vers le membre DR) : rétrograde ce nœud de la partie du basculement avec sa synchronisation en temps réel vers le mode de reprise après sinistre en mode asynchrone.

Parfait, nous avons déjà nos nœuds configurés, voyons maintenant la dernière étape de notre configuration. Nous avons à décider quelles tableaux feront partie du miroir et à le configurer sur les deux nœuds. Si vous regardez le README.md du projet Open Exchange associé à cet article, vous verrez que nous configurons et déployons deux applications que nous utilisons habituellement pour la formation. Ces applications sont déployées automatiquement lorsque nous démarrons les conteneurs Docker et que les NAMESPACES et les bases de données sont créés par défaut.

La première application est celle de l'entreprises COMPANY qui nous permet de sauvegarder les dossiers des entreprises et la seconde est PHONEBOOK qui nous permet d'ajouter des contacts personnels liés aux entreprises enregistrées, ainsi que des clients.

Ajoutons une entreprise :

Nous allons maintenant créer un contact personnel pour l'entreprise précédente :

Les données relatives à l'entreprise seront enregistrées dans la base de données COMPANY et les données relatives au contact dans PERSONAL, les deux bases de données étant mappées de manière à être accessibles à partir de l'espace de noms PHONEBOOK. Si nous vérifions les tableaux dans les deux nœuds, nous verrons que dans mirrorA nous avons les données de l'entreprise et du contact, mais que dans mirrorB il n'y a toujours rien, ce qui est logique.

Les entreprises enregistrées dans mirrorA:

Très bien, procédons à la configuration des bases de données sur notre miroir. Pour ce faire, depuis notre nœud actif (miroirA), nous accédons à l'écran d'administration des bases de données locales (Administrateur système --> Configuration --> Configuration du système --> Bases de données locales) et nous cliquons sur l'option Ajouter au miroir, nous devons sélectionner dans la liste toutes les bases de données que nous voulons ajouter et lire le message qui s'affiche à l'écran :

Une fois les bases de données ajoutées au miroir à partir du nœud actif, nous avons à faire une sauvegarde de celles-ci ou à copier les fichiers de base de données (IRIS.dat) et à les restaurer sur le nœud passif. Si vous décidez de faire une copie directe des fichiers IRIS.dat, gardez à l'esprit que vous devez figer les écritures dans la base de données à copier, vous pouvez voir les commandes nécessaires dans l'URL de la documentation. Dans notre exemple, il ne sera pas nécessaire de faire une pause, puisque personne d'autre que nous n'écrit dans la base de données.

Avant d'effectuer cette copie des fichiers de la base de données, vérifions l'état du miroir à partir du moniteur du nœud actif :

Examinons le nœud passif :

Comme nous pouvons le voir, depuis le nœud passif nous sommes informés que bien que nous ayons 3 bases de données configurées dans le miroir, la configuration n'a pas encore été faite. N'oublions pas que nous devons démonter les bases de données du nœud passif pour pouvoir effectuer la copie et pour cela nous accéderons depuis le portail de gestion à Configuration du système --> Bases de données et en accédant à chacune d'entre elles nous procéderons à leur démontage.

Parfait ! Bases de données démontées. Accédons au code du projet associé à l'article depuis Visual Studio Code et constatons que nous avons les dossiers où se trouvent les installations IRIS, sharedA pour mirrorA et sharedB pour mirrorB. Accédons aux dossiers où se trouvent les bases de données COMPANY, CUSTOMER et PERSONAL (/sharedA/durable/mgr) et procédons à la copie du fichier IRIS.dat de chaque base de données dans le miroir vers les répertoires appropriés du miroirB (/sharedB/durable/mgr).

Une fois la copie terminée, nous montons à nouveau les bases de données mirrorB et vérifions l'état des bases de données configurées à partir du moniteur du miroir mirrorB :

Bingo ! Notre miroir a reconnu les bases de données et il ne nous reste plus qu'à les activer et les mettre à jour. Pour ce faire, nous allons cliquer sur l'action Activer puis sur Catchup (Rattraper), qui apparaîtra après l'activation. Voyons ce qu'il en est :

PParfait, nos bases de données sont déjà correctement configurées en miroir, si nous consultons la base de données COMPANY nous devrions voir l'enregistrement que nous avons enregistré depuis mirrorA auparavant :

Il est évident que notre base de données COMPANY a l'enregistrement que nous avons saisi précédemment dans mirrorA, nous avons copié l'ensemble de la base de données après tout. Ajoutons une nouvelle société à partir de miroirA que nous appellerons "Une autre société" et interrogeons à nouveau le tableau de la base de données COMPANY :

Le voici. Nous aurons juste à nous assurer que nos bases de données configurées en miroir sont en mode lecture seule pour le nœud passif mirrorB :

Et les voici ! en mode R pour la lecture. Bon, nous avons déjà notre miroir configuré et nos bases de données synchronisées. Dans le cas où nous aurions des productions en cours, ce ne serait pas un problème puisque le miroir se charge automatiquement de les gérer, en les démarrant dans le nœud passif en cas de chute dans le nœud actif.

Merci beaucoup à tous ceux qui ont atteint ce stade ! C'était long, mais j'espère que cela vous sera utile.

0
0 176
Article Lorenzo Scalese · Mai 12, 2023 5m read

Selon le dictionnaire de Cambridge, tokéniser des données signifie "remplacer un élément de données privé par un jeton (= un élément de données différent qui représente le premier), afin d'empêcher que des renseignements privés soient vus par quelqu'un qui n'est pas autorisé à le faire" (https://dictionary.cambridge.org/pt/dicionario/ingles/tokenize). Aujourd'hui, plusieurs entreprises, en particulier dans les secteurs de la finance et de la santé, tokenisent leurs données, ce qui constitue une stratégie importante pour répondre aux exigences en matière de cybersécurité et de confidentialité des données ( RGPD, CCPA, HIPAA et LGPD). Mais pourquoi ne pas utiliser le chiffrement ? Le processus de tokenisation pour protéger les données sensibles est plus couramment utilisé que le chiffrement des données pour les raisons suivantes :

  1. Amélioration des performances : le cryptage et le décryptage des données à la volée dans le cadre d'un traitement opérationnel intensif entraînent des performances médiocres et nécessitent une plus grande puissance du processeur.
  2. Tests : il est possible de tokeniser une base de données de production et de la copier dans une base de données de test et de conserver les données de test adaptées à des tests unitaires et fonctionnels plus réels.
  3. Meilleure sécurité : si un pirate informatique craque ou obtient la clé secrète, toutes les données cryptées seront disponibles, car le cryptage est un processus réversible. Le processus de tokenisation n'est pas réversible. Si vous devez récupérer les données d'origine à partir des données tokenisées, vous devez maintenir une base de données sécurisée et séparée pour relier les données d'origine et les données tokenisées.

Architecture de tokenisation

L'architecture de tokenisation nécessite deux bases de données : l'App DB pour stocker les données tokenisées et d'autres données de l'entreprise et une base de données de jetons pour stocker les valeurs d'origine et tokenisées, de sorte que lorsque vous en avez besoin, votre application peut obtenir les valeurs d'origine à montrer à l'utilisateur. Il existe également une API REST Tonenizator qui permet de tokeniser les données sensibles, de les stocker dans la base de données de tokens et de renvoyer un ticket. L'application métier stocke le ticket, les données symbolisées et les autres données dans la base de données de l'application. Consultez le diagramme d'architecture :

 

Application Tokenizator

Découvrez comment cela fonctionne dans l'application Tokenization : https://openexchange.intersystems.com/package/Tokenizator.

Cette application est une API REST qui permet de créer des jetons :

  • Toute valeur est remplacée par les étoiles. Exemple : carte de crédit 4450 3456 1212 0050 par 4450 *** *** 0050.
  • N'importe quelle adresse IP réelle est remplacée par une fausse valeur. Exemple : 192.168.0.1 par 168.1.1.1.
  • Toute donnée personne est remplacée par une fausse personne. Exemple : Yuri Gomes avec l'adresse Brasilia, Brésil par Robert Plant avec l'adresse Londres, Royaume-Uni.
  • Toute valeur numéro est remplacée par une fausse valeur numérique. Exemple : 300,00 par 320,00.
  • Toute donnée de carte de crédit est remplacée par un faux numéro de carte de crédit. Exemple : 4450 3456 1212 0050 par 4250 2256 4512 5050.
  • N'importe quelle valeur est remplacée par une valeur hash. Exemple : Architecte système par dfgdgasdrrrdd123.
  • Toute valeur est remplacée par une expression regex. Exemple : EI-54105-tjfdk par AI-44102-ghdfg en utilisant la règle regex [A-Z]{2}-\d{5}-[a-z]{5}.

Si vous souhaitez une autre option, ouvrez une demande sur le projet github.

Pour tokeniser les valeurs et obtenir les valeurs d'origine par la suite, suivez les étapes suivantes :

  1. Ouvrez votre Postman ou consommez cette API à partir de votre application.

  2. Créez une demande de tokenisation en utilisant les méthodes STARS, PERSON, NUMBER, CREDITCARD, HASH, IPADDRESS et REGEX pour cet échantillon de données sensibles :

    • Méthode : POST

    • URL: http://localhost:8080/token/tokenize

    • Corps (JSON): [ { "tokenType":"STARS", "originalValueString":"545049405679", "settings": { "starsPosition":"1", "starsQuantity":"4" } }, { "tokenType":"IPADDRESS", "originalValueString":"192.168.0.1", "settings": { "classType":"CLASS_B", "ipSize":"4" } }, { "tokenType":"PERSON", "originalValueString":"Yuri Marx Pereira Gomes", "settings": { "localeLanguage":"en", "localeCountry":"US", "withAddress":"true", "withEmail":"true" } }, { "tokenType":"NUMBER", "originalValueNumber":300.0, "settings": { "minRange":"100.0", "maxRange":"400.0" } }, { "tokenType":"CREDITCARD", "originalValueString":"4892879268763190", "settings": { "type":"VISA" } }, { "tokenType":"HASH", "originalValueString":"Architecte système" }, { "tokenType":"REGEX", "originalValueString":"EI-54105-tjfdk", "settings": { "regex":"[A-Z]{2}-\d{5}-[a-z]{5}" } } ]

    • Découvrez les résultats. Vous obtenez une valeur tokenizée (tokenizedValueString) à stocker dans votre base de données locale.

  3. Copiez le ticket de la réponse (stockez-le dans votre base de données locale avec la valeur tokenisée).

  4. Avec le ticket, vous pouvez maintenant obtenir la Valeur d'origine. Créez une demande pour obtenir la valeur d'origine en utilisant le ticket :

    Tous les tokens générés sont stockés dans SQL d'InterSystems IRIS Cloud pour vous permettre d'obtenir vos valeurs d'origine en toute performance et en toute confiance.

    Profitez-en !

0
0 81
Article Lorenzo Scalese · Avr 21, 2023 5m read

picture

Créer un Chatbot avec IRIS et Python

Dans cet article, je vais montrer comment intégrer la base de données IRIS d'InterSystems avec Python pour servir un Modèle d'apprentissage automatique du traitement du langage naturel (NLP).

Pourquoi Python ?

Avec une large adoption et utilisation dans le monde, Python a une grande communauté et un grand nombre d'accélérateurs et de bibliothèques pour déployer n'importe quel type d'application. Vous pouvez également consulter : https://www.python.org/about/apps/ pour en savoir plus.

Globales Iris

Lorsque j'ai commencé à me familiariser avec les globales (^globals), leur utilisation m'est devenue familière en tant que moyen rapide d'ingérer des données dans un modèle de données prêt à l'emploi. Dans un premier temps, je vais donc utiliser les globales (^globals) pour stocker les données d'entraînement et les conversations pour enregistrer le comportement du chatbot.

Traitement du langage naturel

Le traitement du langage naturel ou NLP (Natural Language Processing) est un domaine de l'IA qui crée la capacité de lire et de comprendre le sens de nos langues pour les machines. Comme vous pouvez l'imaginer, ce n'est pas très simple, mais je vais vous montrer comment faire vos premiers pas dans ce vaste et beau domaine.

Démo - Essayez-le vous-même

J'ai déployé l'application Chatbot en tant que démonstration ici : http://iris-python-suite.eastus.cloudapp.azure.com:8080

Comment ça marche?

Apprentissage automatique

Il est tout d'abord bon de savoir que l'apprentissage automatique a un paradigme différent de celui du développement de logiciels courants. Le point principal qui est difficile à comprendre est le cycle de développement des modèles d'apprentissage automatique.

Alerte aux explications superficielles

Le cycle de développement d'une application standard est le suivant :

Développer le code->Tester (avec des données de développement)->Déployer (en utilisant des données réelles)

L'apprentissage automatique du code en lui-même n'a pas la même valeur. La responsabilité est partagée avec les données ! Et pas n'importe quelles données, mais de vraies données ! Car le code final à exécuter est généré par une fusion entre les concepts de développement et les données utilisées. Un cycle d'application de l'apprentissage automatique devrait donc se dérouler de la manière suivante :

Développer (Former) Modèle+Données réelles->Valider->Déployer le résultat de ceci (un Modèle)

Comment former le modèle?

Il existe de nombreuses techniques pour entraîner les modèles et chaque cas et objectif nécessite une courbe d'apprentissage importante. Dans ce cas, j'utilise la bibliothèque ChatterBot qui encapsule certaines techniques et fournit des méthodes d'entraînement et des données d'entraînement prétraitées pour nous aider à nous concentrer sur les résultats.

Langages de modèles pré-entraînés et modèles personnalisés

Vous pouvez commencer par cela pour avoir un chatbot conversationnel de base. Vous pouvez également créer toutes les données nécessaires à l'entraînement de votre chatbot, et cela peut être parfait pour vos besoins, mais terriblement difficile à réaliser en un temps limité. Dans ce projet, j'utilise en_core_web_sm comme base de conversation et je la fusionne avec des données d'entraînement personnalisées que vous pouvez créer à l'aide d'un formulaire

Architecture de base

image

Qu'ai-je utilisé en Python ?

Dans cet environnement d'application, j'utilise Python 3.7 avec les modules suivants :

  • PyYAML<=5.0.0
  • dash==1.12.0
  • dash-bootstrap-components==0.10.1
  • dash-core-components==1.10.0
  • dash-html-components==1.0.3
  • dash-renderer==1.4.1
  • dash-table==4.7.0
  • plotly==4.7.1
  • numpy==1.18.4
  • networkx==2.4
  • Flask>=1.0.0
  • chatterbot>=1.0.0
  • chatterbot-corpus>=1.2.0
  • SQLAlchemy>=1.2
  • ./nativeAPI_wheel/irisnative-1.0.0-cp34-abi3-linux_x86_64.whl

Structure de projet

Ce projet a une structure simple et facile à comprendre. Dans le dossier principal, nous avons trois sous-dossiers importants :

  • ./app: avec tout le code d'application et la configuration d'installation.
  • ./iris: avec InterSystems IRIS dockerfile préparant à servir l'application.
  • ./data: Pour relier l'hôte à l'environnement du conteneur par un volume

Structure de l'application

À l'intérieur du répertoire ./app, nous trouvons quelques fichiers :

  • chatbot.py : avec la mise en œuvre de l'application web
  • iris_python_suite.py : une classe avec quelques accélérateurs à utiliser avec IRIS Database et Python par l'API native d'IRIS.

Structure de la base de données

Cette application utilise Intersystems IRIS comme référentiel, les globales utilisées sont :

  • ^chatbot.training.data : stocke toutes les données d'entraînement personnalisées sous forme de questions et de réponses.
  • ^chatbot.conversation : stocke toutes les données de conversation.
  • ^chatbot.training.isupdated : contrôle le pipeline de formation.

Produits de mon autre solution

Je n'ai pas créé de rapport pour toutes les conversations mais ce n'est pas un problème, grâce à mon visualiseur de graphes globaux je peux suivre les conversations. image

Exécution de l'application par vous-même

Conditions préalables

  • git
  • docker et docker-compose (et plus de paramètres de mémoire dans docker au moins 4 Go)
  • accès à un terminal dans votre environnement

Étapes

Avec docker-compose, vous pouvez facilement mettre en place un environnement avec toutes les pièces et configurations. Allez dans le dossier iris-python-covid19 et tapez ce texte :

$ docker compose build
$ docker compose up

Durée estimée de mise en place des conteneurs

La première exécution dépendra de votre lien internet pour télécharger les images et les dépendances. Si la durée est supérieure à 15 minutes, il est probable que quelque chose ne fonctionne pas, n'hésitez pas à nous en faire part ici. Après la première exécution, les exécutions suivantes seront plus performantes et prendront moins de 2 minutes.

Si tout est correct

Après un certain temps, vous pouvez ouvrir votre navigateur et aller à l'adresse :

La forme des données d'apprentissage

http://localhost:8050/chatbot-training-data

Le chatbot

http://localhost:8080

Vous devriez consulter le portail d'administration d'IRIS

Pour le moment, j'utilise l'espace de noms USER.

http://localhost:9092
user: _SYSTEM
pass: theansweris42

Si cet article vous est utile ou si vous en aimez le contenu, votez :

Cette application est au concours actuel sur open exchange, vous pouvez voter pour mon application iris-python-suite ici (https://openexchange.intersystems.com/contest/current)

0
0 330
Article Lorenzo Scalese · Avr 17, 2023 7m read

Il y a quelque temps, GitHub a annoncé une nouvelle fonctionnalité, les Codespaces GitHub Codespaces. Elle permet d'exécuter VSCode dans le navigateur, avec presque la même puissance que s'il était exécuté localement sur votre machine, mais aussi avec la puissance des nuages, de sorte que vous pouvez choisir le type de machine avec jusqu'à 32 cœurs de CPU et 64 Go de RAM.

C'est impressionnant, n'est-ce pas ? Mais comment cela peut-il nous aider à travailler sur des projets pilotés par InterSystems IRIS ? Voyons comment le configurer pour nous.

Démarrage simple pour tout référentiel

Grâce à cette fonctionnalité, vous pourrez modifier n'importe quel dépôt dans le nuage, par le bouton Code. Veuillez noter que cette fonctionnalité est encore en version bêta, et peut ne pas être disponible pour tout le monde. Après une période bêta, elle ne sera disponible que pour les comptes payants.

Ainsi, ce dépôt n'a pas été spécialement préparé pour les codespaces, mais seulement pour VSCode. Appuyez sur le bouton de nouveau codespace "New codespace", pour créer un environnement juste pour vous.

Dans mon cas, lors de l'utilisation précédente de codespaces, j'ai déjà installé l'extension ObjectScript et l'ai activée globalement par une invite de Codespaces. Ainsi, elle s'installe pour moi à chaque fois, et le code ObjectScript est déjà mis en évidence. Mais l'IRIS n'est pas encore disponible. Démarrons-le avec docker-compose.

Après cela, nous pouvons maintenant nous connecter au terminal IRIS et compiler le code.

Les ports de docker-compose sont automatiquement reconnus et peuvent être ouverts dans le navigateur. Il est donc également possible d'ouvrir le Portail de gestion du système.

Utilisation avec un référentiel préparé

Nous avons réussi à faire fonctionner VSCode et IRIS dans le nuage, mais nous avons dû faire certaines choses manuellement, pour le rendre prêt. Cependant, il est également possible de rendre votre référentiel prêt pour le développement dès le début.

C'est possible, avec devcontainer.json. Je vais faire un exemple basé sur l'un de mes projets récents Realworld. Ce projet est assez complexe, il a un backend et un frontend et utilise docker-compose pour démarrer tout cela ensemble.

devcontainer peut utiliser docker-compose également, donc, ma configuration est apparue comme ceci.

{
  "name": "IRIS RealWorld example",
  "dockerComposeFile": "docker-compose.yml",
  "service": "server",
  "extensions": [
    "intersystems-community.vscode-objectscript"
  ],
  "forwardPorts": [
    80,
    52773
  ],
  "workspaceFolder": "/home/irisowner/conduit",
  "remoteUser": "irisowner",
  "postCreateCommand": "iris start iris",
  "settings": {
    "terminal.integrated.defaultProfile.linux": "bash",
    "terminal.integrated.profiles.linux": {
      "bash": {
        "path": "bash",
        "icon": "terminal-bash"
      },
      "iris": {
        "path": "iris",
        "args": ["session", "iris"]
      }
    },
    "intersystems.servers": {
      "/ignore": true
    },
    "objectscript.ignoreInstallServerManager": true,
    "objectscript.ignoreInstallLanguageServer": true,
    "objectscript.conn": {
      "active": true,
      "host": "localhost",
      "port": 52773,
      "ns": "CONDUIT",
      "username": "demo",
      "password": "demo",
      "links": {
        "Conduit APP": "http://localhost:80/",
        "Conduit API": "http://${host}:${port}/conduit/"
      }
    }
  }
}

Il y a beaucoup de choses configurées

  • chemin vers le fichier docker-compose.yml personnalisé, en particulier pour Codespaces
  • nom du service principal, où le développement est effectué 
  • liste des extensions installées par défaut dans VSCode
  • Les ports qui doivent être publiés, dans ce cas, le port du serveur web pour IRIS et pour le frontend
  • chemin du répertoire de travail
  • utilisateur à l'intérieur du conteneur;; comme nous entrons dans le conteneur IRIS, nous avons besoin de l'utilisateur irisowner 
  • dans docker-compose, notre conteneur IRIS est configuré pour ne pas utiliser le point d'entrée par défaut iris-main, mais juste mettre en veille avec infinity, et après avoir démarré l'environnement, nous devons démarrer notre IRIS
  • et enfin, les paramètres pour VSCode, peuvent également être configurés ici, c'est un niveau de paramètres de la machine. Qui, bien sûr, peuvent être remplacés ou ajoutés avec .vscode/settings.json

Le démarrage de Codespaces pour un tel référentiel prendra un peu plus de temps, car il faudra construire tous les conteneurs nécessaires et les démarrer. GitHub indique qu'il sera possible de pré-créer de telles images après chaque poussée au référentiel, de sorte que le démarrage sera plus rapide.

Et quand il a démarré, aucune autre action n'est nécessaire, il est prêt pour le développement.

Ce projet a une option pour tester l'API REST avec des tests Postman préparés, donc, j'ai installé npm et newman à l'intérieur du conteneur backend avec IRIS. Et il est possible d'y exécuter ces tests. Tout est passé, bien joué.

Et le frontend est également disponible

GitHub permet de se connecter à Codespaces, y compris depuis le VSCode local. Lorsque vous appuyez sur le bouton vert Codespaces dans le coin, vous pouvez choisir d'ouvrir dans VC Code (l'extension GitHub Codespaces doit être installée).

Et voici, c'est le même projet, ouvert avec votre VSCode local, mais fonctionnant dans le nuage, comme vous pouvez voir le résultat de ifconfig, je ne suis certainement pas à Singapour, en ce moment.

Dans un navigateur mais sans Codespaces GitHub

Et si vous n'avez pas accès à la fonction Codespaces, ou si vous ne voulez pas l'utiliser de cette manière, mais que vous voulez quand même essayer VSCode dans le navigateur.

Eh bien, c'est possible avec un autre projet code-server

Vous pouvez simplement exécuter ce VSCode avec la commande suivante

docker run -it -p 8080:8080 codercom/code-server --auth=none

Il s'exécutera, la version par défaut de VSCode, sans dossiers mappés à l'intérieur, montez simplement n'importe quel dossier, et définissez-le comme répertoire de travail, et vous le verrez à l'intérieur.

docker run -it -p 8080:8080 -v `pwd`:/opt/realworld -w /opt/realworld codercom/code-server --auth=none

C'est le VSCode par défaut, sans extension ObjectScript installée. Il a une limitation avec les extensions, il n'a pas accès au marché original de VSCode, au lieu de cela il utilise un autre endroit, open-vsx.org, et l'extension ObjectScript principale est disponible là aussi.

Avec un tel fichier Docker, nous pouvons créer notre propre serveur de code, avec tout ce qui y est installé, ainsi que certaines extensions déjà installées.

FROM codercom/code-server

USER root

RUN curl -fsSL https://deb.nodesource.com/setup_15.x | bash - && \
    apt-get install -y jq nodejs python3-pip python3-dev unixodbc-dev && \
    rm -rf /var/lib/apt/lists/* && \
    pip3 install pyodbc && \
    npm install -g yarn && \
  sudo chown -R 1000:1000 /home/coder

COPY extensions extensions

COPY settings.json /root/.local/share/code-server/User/settings.json

ENV SERVICE_URL=https://open-vsx.org/vscode/gallery
ENV ITEM_URL=https://open-vsx.org/vscode/item

RUN \
  code-server --install-extension ms-python.python && \
  code-server --install-extension intersystems-community.vscode-objectscript && \
  find extensions -type f -exec code-server --install-extension {} \;

WORKDIR /opt/intersystems

CMD [ "--auth=none", "--disable-telemetry" ]

Vous pouvez définir des paramètres par défaut dans le fichier settings.json pour le niveau utilisateur et si certaines extensions dont vous avez besoin ne sont pas disponibles sur open-vsx, téléchargez-les manuellement, placez-les dans le dossier des extensions à côté du Dockerfile, et elles seront également installées.

Vous êtes maintenant en mesure de lancer un nouveau serveur de code avec toutes les extensions dont vous avez besoin installées.

docker run -it -p 8080:8080 -v `pwd`:/opt/realworld -w /opt/realworld &lt;strong>caretdev/code-server&lt;/strong> --auth=none

Et la coloration syntaxique est déjà là, il ne reste plus qu'à faire fonctionner IRIS lui-même, et cela peut être fait avec un docker-compose étendu, où le code-server sera juste un autre service à côté d'IRIS.

0
0 66
Article Lorenzo Scalese · Mars 13, 2023 4m read

Ce Template s'agit d'un modèle de l' application API REST construite avec ObjectScript dans Intersystems IRIS. Il dispose également d'une spécification OPEN API, peut être développé avec Docker et VSCode et peut être déployé en tant que module IPM.

Conditions préalables

Assurez-vous que vous avez installé git et Docker desktop.

Installation avec IPM

zpm:USER>install rest-api-template

Installation pour le développement

Créez votre référentiel à partir d'un modèle.

0
0 145
Article Lorenzo Scalese · Mars 3, 2023 9m read

Bonjour à la communauté,

Dans la première partie, nous avons décrit tous les packages, les bibliothèques utilisées et les services REST.  J'aimerais maintenant détailler un peu plus les services convertisseur et validateur.  Par défaut, OpenAPI-Suite envoie une requête HTTP  converter.swagger.io si a spécification est de version inférieure à 3.0 et une autre requête HTTP à validator.swagger.io pour simplifier la structure de la spécification.  

Bien que l'utilisation d'utilitaires en ligne soit pratique, dans certains cas, nous pourrions préférer avoir notre propre instance du convertisseur et du validateur.  Par exemple, si OpenAPI-Suite est mis à disposition sur un serveur dans une organisation pour les développeurs ObjectScript, il peut être préférable d'éviter les requêtes vers les services externes (confidentialité, éviter les limites de taux de demande,...).  Ceux-ci sont disponibles en images Docker, il suffit d'exécuter : 

docker run -d -p 8085:8080 --name swagger-converter swaggerapi/swagger-converter:latest
docker run -d -p 8086:8080 --name swagger-validator-v2 swaggerapi/swagger-validator-v2:latest
0
0 60
Article Lorenzo Scalese · Fév 27, 2023 18m read

Salut la communauté,

J'aimerais vous présenter ma dernière application OpenAPI-Suite, c'est un ensemble d'outils permettant de générer du code ObjectScript à partir d'une specification OpenAPI version 3.0. L'application permet de:

  • Générer les classes serveur REST.  C'est assez similaire au code généré par ^%RESTla valeur ajoutée est le support de la version 3.0.
  • Générer les classes pour un client HTTP.
  • Générer une production cliente (business services, business operation, business process, Ens.Request, Ens.Response).
  • Disposer d'une interface web pour générer et télécharger le code ou générer et compiler directement sur le serveur.
  • Convertir les spécifications de version 1.x, 2.x en version 3.0.

Aperçu

OpenAPI-Suite est divisée en plusieurs packages et utilise différentes bibliothèques de la communauté des développeurs ainsi que des services REST publics.  Vous pouvez voir sur le schéma ci-dessous, tous les packages développés et les bibliothèques et services web utilisés:
 

0
0 202
Article Lorenzo Scalese · Fév 8, 2023 1m read

Les snippets sont l'une des fonctions les plus utiles de Studio.

Voici comment ajouter des snippets à VSCode.

Voici une instruction générale.

  1. Allez dans Fichier - Préférences - Snippets utilisateur et choisissez objectscript.

  2. Ajoutez votre snippet, voici un exemple :

    "SQL Statement": { "prefix": ["sql"], "body": ["#dim rs As %SQL.ISelectResult", "set rs = ##class(%SQL.Statement).%ExecDirect(,"SELECT * FROM")", "while rs.%Next() {", "\twrite rs.ID, !", "}"] }

Ici :

  • prefix (préfixe) - ce que vous devez saisir pour que le snippet apparaisse
  • body (corps) - corps du snippet

Mieux encore, les snippets peuvent inclure des espaces réservés, par exemple :

"Method": {
    "prefix": ["method"],
    "body": ["set sc = ##class(${1:class}).${2:method}()"]
}

Après avoir inséré ce snippet, vous êtes automatiquement déplacé au début du premier espace réservé, utilisez <TAB> pour ne pas itérer dans les espaces réservés.

Bonne chance pour le codage !

2
0 193
Article Lorenzo Scalese · Fév 1, 2023 3m read

Ajout de VSCode dans votre conteneur IRIS

L'une des façons les plus simples de mettre en place des environnements de développement reproductibles est de créer des conteneurs pour ces environnements. Je trouve que lors d'itérations rapides, il est très pratique d'héberger une instance de vscode dans mon conteneur de développement. Ainsi, j'ai créé un rapide script de conteneur pour ajouter un vscode basé sur un navigateur dans un conteneur IRIS. Cela devrait fonctionner pour la plupart des conteneurs 2021.1+. Mon référentiel de code est disponible ici

Conteneur InterSystems IRIS avec vscode et pré-connecté

Réf.Valeur
Utilisateur_SYSTEM
Mot de passeSYS

image

Aperçu

Ce projet crée un conteneur IRIS avec une version hébergée (basée sur le web) de vscode disponible dans le même conteneur IRIS. Ceci fournit :

  • Modification du code du même conteneur
  • Pré-connexion à l'instance IRIS du conteneur
  • Liens à partir du Portail de gestion
  • Démarrage automatique de l'IDE avec le conteneur

Démarrage rapide

  1. Téléchargez ou git clone https://github.com/nickmitchko/Hosting-vscode-in-a-container.git
  2. A la racine du projet, lancez docker build . -t vscode-irishealth-ml:latest --no-cache
  3. Lancez docker-compose up
    • Vous n'utilisez pas docker compose ? Voir ici
  4. Naviguez vers Management Portal
  5. Connectez-vous avec l'utilisateur et le mot de passe figurant en haut de ce guide
  6. Cliquez sur VSCODE Link dans la fenêtre Favoris
  7. Lorsque vous y êtes invité, utilisez le même mot de passe dans le vscode pour vous connecter à l'instance IRIS.
# Nouveau dossier pour le projet
mkdir vscode-iris
cd vscode-iris

# Clonez le référentiel ici
git clone https://github.com/nickmitchko/Hosting-vscode-in-a-container.git .

# Créez une image
docker build . -t vscode-irishealth-ml:latest --no-cache

# Exécution (A) ou (B) uniquement
#
# (A) Exécution du fichier de composition
docker-compose up
# OU (B) si vous préférez un démon
docker-compose up -d

Ajoutez la persistance

Si vous cherchez une instance iris persistante, il faut commenter les lignes 16-20 du fichier docker-compose.yml. Cela permet d'ajouter un montage de stockage persistant au conteneur.

    volumes:
    - "./durable/:/durable/"
    environnement:
    - ISC_DATA_DIRECTORY=/durable/iconfig

Modification de l'image de base

Cette image est construite à partir des images zpm de la communauté des développeurs d'InterSystems (disponible ici). Ces images incluent la commande zpm qui nous permet d'installer facilement à partir du référentiel de paquets, mais la licence communautaire n'est que de 90 jours.

La balise d'image utilisée pour les constructions est la suivante :

FROM intersystemsdc/irishealth-ml-community:latest

Si vous voulez remplacer l'image, remplacez la première ligne du fichier docker par l'étiquette de l'image souhaitée (soit une instance IRIS personnalisée, soit une instance prise en charge). Par exemple :

FROM containers.intersystems.com/intersystems/irishealth-community:2021.2.0.651.0

Pas de Docker-Compose

Si vous n'utilisez pas docker compose, vous pouvez toujours exécuter le conteneur comme suit :

# Après avoir créé le conteneur
# --après la commande est nécessaire
docker run --name vscode -d \
    --publish 1972:1972 \
    --publish 52773:52773 \
    --publish 51773:51773 \
    --publish 53773:53773 \
    --publish 8080:8080 \
    --publish 8888:8888 \
    vscode-irishealth-ml:latest \
    --after "/bin/bash /install/boot.sh"
0
0 939
Article Lorenzo Scalese · Jan 18, 2023 4m read

Bonjour aux développeurs d'InterSystems !

Récemment, j'ai mis à jour le modèle FHIR dev template afin qu'il publie maintenant un paquet IPM fhir-server permettant de faire de la configuration du serveur FHIR d'InterSystems une procédure manuelle, automatique ou programmatique triviale d'une seule commande.

Découvrez ci-dessous comment vous pouvez en bénéficier.

TLDR

USER>zpm "install fhir-server"

Tous les détails ci-dessous.

Configuration du serveur InterSystems FHIR sans IPM 

Bien sûr, vous pouvez configurer le serveur InterSystems FHIR sans utiliser le gestionnaire de paquets IPM. Vous avez le choix :

  1. Vous pouvez configurer un serveur FHIR en nuage et l'essayer pendant plusieurs jours en suivant les instructions suivantes. Il s'agira d'un serveur FHIR d'InterSystems dans le nuage AWS.

  2. Vous pouvez configurer le serveur FHIR d'InterSystems en exécutant InterSystems IRIS for Health en suivant les étapes suivantes.

  3. Vous pouvez également cloner git le dépôt de ce modèle et l'exécuter dans un répertoire cloné :

$ docker-compose up -d

pour que le serveur FHIR d'InterSystems fonctionne sur votre ordinateur portable.

Ce que je suggère dans l'article est le point 2 où vous pouvez sauter toutes les étapes manuelles et avoir le serveur FHIR en marche sur un ordinateur portable IRIS soit dans Docker soit dans l'OS hôte.

**Configuration du serveur FHIR avec IPM **

AVIS DE NON-RESPONSABILITÉ ! Les étapes décrites ci-dessous se rapportent à une instance d'IRIS for Health récemment installée ou à une utilisation avec des images Docker. Le paquet crée un nouvel espace de noms et une nouvelle application Web, ce qui pourrait nuire à la configuration précédente.

IPM est l'acronyme anglais pour InterSystems Package manager, précédemment connu sous le nom de ZPM. Vérifiez que [IPM-client est installé] (https://openexchange.intersystems.com/package/InterSystems-Package-Manager-1). Vous pouvez le vérifier en exécutant la commande zpm dans le terminal IRIS et en obtenant le résultat suivant :

IRISAPP>zpm     

=============================================================================
|| Welcome to the Package Manager Shell (ZPM).                             ||
|| Enter q/quit to exit the shell. Enter ?/help to view available commands ||
=============================================================================
zpm:IRISAPP>

Vous aurez besoin d'IRIS for Health pour les versions 2022.x et plus récentes.  

Comment exécuter iris for health sur votre ordinateur portable ?

Exécution d'une opération hôte

Téléchargez la version la plus récente d'IRIS for Health sur le [site d'évaluation d'InterSystems] (https://evaluation.intersystems.com/Eval/index.html) qui correspond à votre plate-forme (Windows, Mac, Linux) et installez-la. Installez ZPM. Voici un exemple :

USER>zn "%SYS" d ##class(Security.SSLConfigs).Create("z") s r=##class(%Net.HttpRequest).%New(),r.Server="pm.community.intersystems.com",r.SSLConfiguration="z" d r.Get("/packages/zpm/latest/installer"),$system.OBJ.LoadStream(r.HttpResponse.Data,"c")

Exécution d'une version docker

Appelez votre terminal pour le lancement :

docker run --rm --name iris4h -d --publish 9091:1972 --publish 9092:52773 intersystemsdc/irishealth-community

Puis lancez le terminal :

docker exec -it iris4h iris session IRIS

Installation du serveur FHIR

Après avoir fait fonctionner IRIS sur l'hôte ou simplement dans le terminal IRIS :

USER>zpm "install fhir-server"

Cela installera le serveur FHIR dans l'espace de noms FHIRSERVER avec des paramètres :

Set appKey = "/fhir/r4"
Set strategyClass = "HS.FHIRServer.Storage.Json.InteractionsStrategy"
set metadataPackages = $lb("hl7.fhir.r4.core@4.0.1")

Set metadataConfigKey = "HL7v40"

L'API REST FHIR sera disponible ici : http://yourserver/fhir/r4.

Il ajoutera également quelques données synthétiques.

Comment comprendre que le serveur fonctionne ?

Pour tester la version hôte :

http://localhost:52773/fhir/r4/metadata

Pour tester la version docker :

http://localhost:9092/fhir/r4/metadata

zpm installe également l'interface utilisateur simple qui est disponible à l'adresse suivante : yourserver/fhirUI/FHIRAppDemo.html.

Et vous verrez apparaître quelque chose comme ceci (avec le patient id=1 entré) :

Comment ça marche ?

En fait, vous pouvez observer ce qui est installé avec ce module ZPM dans le scénario suivant following module.xml. On peut voir qu'il importe du code, installe l'application frontale de démonstration fhirUI, exécute le script de post-installation, qui appelle la méthode suivante. Le script de la méthode effectue la configuration du serveur FHIR.

Installation programmatique du serveur FHIR

Vous pouvez également l'installer de manière programmatique en utilisant la commande suivante :

set sc=$zpm("install fhir-server")

Joyeux codage FHIR !

0
0 64
Article Lorenzo Scalese · Jan 9, 2023 109m read

Création et utilisation des nouvelles extensions SDA pour le stockage d'éléments de données personnalisés

Dans HSCore 15.01, il existe une nouvelle façon de stocker les éléments de données personnalisés.  HealthShare peut désormais utiliser des extensions personnalisées pour de nombreux éléments SDA.

Cet article a pour but de :

  1. Montrer comment configurer votre système pour utiliser les extensions SDA.
  2. Créer une nouvelle propriété d'extension SDA
  3. Utiliser la nouvelle propriété d'extension SDA dans les transactions HL7
  4. Interagir avec les nouvelles données
  5. Montrer la nouvelle extension SDA utilisée dans une personnalisation du Rapport de résumé du patient (Patient Summary Report).
<td>
   
</td>
 
 
Note: Pour cet article, je me sers de build :
HS-2016.1.1.108.0-hscore15.01_hsaa15_hspi15_hsviewer15.01_linkage15-b2136-win_x64
J'ai également créé le système en utilisant “d ##class(HS.Util.Installer).InstallBusDemo()”

 

Configurez votre système pour les extensions SDA

Cette section décrit comment configurer les extensions SDA pour un environnement HSCore 15.01.

Création d'un nouvel espace de noms

Dans le cadre des nouvelles extensions SDA, le nom de l'espace de noms personnalisé doit être HSCUSTOM.

Vous pouvez l'ajouter en allant dans : Management Portal->System Administration->Configuration->System Configuration->Namespaces (Portail de gestion->Administration du système->Configuration->Configuration du système->Espaces de noms).

Étapes pour créer un nouvel espace de noms :

  1. Cliquez sur le bouton "Create New Namespace" (créer un nouvel espace de noms).
  2. Entrez HSCUSTOM dans le champ “Name of the namespace” (Nom de l'espace de noms) (obligatoire).
  3. Sélectionnez le bouton "Create New Database" (Créer une nouvelle base de données)

  • Saisissez HSCUSTOM dans le champ "Enter the name of your database" (Entrez le nom de votre base de données).
  • Pour le champ “Database directory” (Répertoire de la base de données) :
    1. Cliquez sur le bouton "Browse...".
    2. Créez un nouveau dossier/répertoire, j'ai nommé mon répertoire "HSCUSTOM", il se trouve dans le répertoire “mgr”.
    3. Cliquez sur le bouton “OK”
  • Cliquez sur le bouton “Next”

  • Acceptez les valeurs par défaut, et cliquez sur le bouton "Next".
  • Créez une nouvelle ressource appelée %DB_HSCUSTOM et octroyez-lui des droits de lecture et d'écriture
  • Cliquez sur le bouton "Finish" (terminer).

4.  De retour à l'écran "New Namespace" (nouvel espace de noms), cliquez sur le bouton "Save" (enregistrer).

Cette procédure a pour effet de créer un nouvel espace de noms HSCUSTOM avec tous les mappings par défaut.

Exportation du paquet HS.Local

Une des choses que nous devons faire est de copier les classes et le code de la base de données HSLIB vers la base de données HSCUSTOM.

Vous pouvez le faire de plusieurs façons.  Je vais vous parler de la façon de le faire à partir de Studio ou de Terminal.

Exportation à partir de Studio :

  1. Connectez-vous à Studio
  2. Changez l'espace de noms en espace de noms HSLIB (remarque : ceci peut être fait à partir de n'importe quel espace de noms qui a un paquetage HS mappé à HSLIB)
  3. Allez dans le menu "Tools->Export" (outils - exportation).
  4. Cliquez sur le bouton "Add" (ajouter)
  5. Sélectionnez le dossier HS/Local, sélectionnez tout et cliquez sur le bouton "Open" (ouvrir).
  6. Cela va tout sélectionner
  7. Sélectionnez un fichier local ou distant pour exporter ces classes
    • Dans cet exemple, j'ai nommé le fichier "HSLocal.xml"

      8.  Cliquez sur le bouton "OK". 

Exportation à partir d'une session Terminal :

  1. Connectez-vous à Terminal
  2. Changez l'espace de noms en espace de noms HSLIB ( remarque : ceci peut être fait à partir de n'importe quel espace de noms qui a un paquetage HS mappé à HSLIB )
  3. HSLIB>d $system.OBJ.Export("HS.Local.*.cls","C:\Intersystems\Export\HSLocal.xml")

Ajoutez une nouvelle cartographie de paquet

Le paquet "HS.Local" doit être référencé à partir du nouvel espace de noms HSCUSTOM.  Lorsque HSCUSTOM sera créé, "HS" sera mappé vers HSLIB.  Vous devrez ajouter "HS.Local" à l'espace de noms HSCUSTOM, car il est actuellement pointé vers HSLIB.

Vous pouvez le faire manuellement via le Management Portal (Portail de gestion) ou de manière programmatique.

Management Portal (Portail de gestion) :

  1. Allez dans le Portail de gestion->Administration du système->Configuration->Configuration du système->Espaces de noms.
  2. Recherchez HSCUSTOM dans la colonne des espaces de noms et sélectionnez le lien "Package Mapping".
  3. Cliquez sur le bouton "New"
  4. Sélectionnez HSCUSTOM dans le menu déroulant "Pakage Database Location" (location de la base de données de paquet)
  5. Sélectionnez le bouton radio "Specify a new package” (Spécifier un nouveau paquet).
  6. Entrez HS.Local dans le champ "Packaga Name" (Nom du paquet)

         7. Cliquez sur le bouton "OK"

Par programmation :

Vous pouvez créer du code pour ajouter ce mappage à un espace de noms.

  1. Déplacez-vous vers l'espace de noms %SYS
    • HSCUSTOM> ZN “%SYS”
  2. Définissez la propriété de la base de données
    • %SYS> set tProperties("Database")="HSCUSTOM"
  3. Créez le mappage
    • %SYS>w ##class(Config.MapPackages).Create("HSCUSTOM","HS.Local",.tProperties)

Remarque:  Vous devrez le faire pour chaque espace de noms et instance qui est un espace de noms HealthShare, à l'exception des espaces de noms Library.   Il est important d'avoir ces mappages pour que les autres espaces de noms puissent accéder au code HSCUSTOM à utiliser dans leur traitement, comme les applications telles que Patient Index et Health Insight.

Importation du paquet HS.Local

Maintenant que les paquets HS.Local pointent vers HSCUSTOM, vous pouvez déplacer les classes que nous avons exportées précédemment dans l'espace de noms HSCUSTOM.

Vous pouvez le faire de plusieurs façons.  Je vais vous parler de la façon de le faire à partir de Studio ou de Terminal.

Importation à partir de Studio:

  1. Connectez-vous à Studio
  2. Changez l'espace de noms en espace de noms HSCUSTOM.
  3. Allez dans le menu Tools->Import Local (Outils->Importer Local).
  4. Sélectionnez le fichier que vous avez exporté
  5. Appuyez sur le bouton "Open" (ouvrir).
  6. Vous devriez voir toutes les classes cochées et l'option "Compile Imported Items" (Compiler les éléments importés) cochée.

     7.  Cliquez sur le bouton "OK". 

Importation à partir d'une session Terminal :

  1. Connectez-vous à Terminal
  2. Changer l'espace de noms en HSCUSTOM
  3. HSLIB>d $system.OBJ.Load("C:\Intersystems\Export\HSLocal.xml",”ck”)

Résumé

Nous avons maintenant l'infrastructure pour les nouvelles extensions SDA personnalisées de HSCore 15.01.  Nous avons les classes HS.Local définies dans une nouvelle base de données HSCUSTOM et nous avons tous les espaces de noms qui pointent vers la localisation appropriée.

Si vous avez plus d'une instance de cache, l'espace de noms HSCUSTOM et les mappages HS.Local doivent se trouver sur chaque instance qui exécute HealthShare.

Création d'une nouvelle extension SDA personnalisée

Maintenant que nous avons les éléments nécessaires, nous allons créer une nouvelle propriété personnalisée.

Nous allons commencer par créer une propriété personnalisée pour le Patient SDA (SDA du patient).

En regardant les annotations HL7, il semble que "Veterans Military Status" (statut militaire des vétérans), qui est le PID, pièce 27, n'est pas utilisé dans SDA, alors essayons de créer ceci comme notre extension SDA personnalisée.

Comme la pièce PID 27 est un champ d'entrée codé, nous allons montrer que les nouvelles extensions SDA personnalisées sont plus que la paire nom/valeur précédente, il s'agit maintenant d'un type de données plus complexe.  Dans cet exemple, nous créons un type de propriété personnalisé.

Edit HS.Local.SDA3.PatientExtension.cls

Nous devons ajouter la nouvelle propriété à HS.Local.SDA3.PatientExtension.cls

  1. Connectez-vous à Studio
  2. Changez l'espace de noms en HSCUSTOM
  3. Modifiez HS.Local.SDA3.PatientExtension.cls
  4. Ajouter une classe personnalisée "Custom Class"
    • Cette classe représente un type de données complexe qui aura :
      • Champ de code
      • Description du Champ

    5.   Ajoutez la propriété VeteransMilitaryStatus

  • Propriété VeteransMilitaryStatus En tant que CUSTOM.SDA3.CodeTableDetail.VeteransMilitaryStatus;

      6.  Compilez HS.Local.SDA3.PatientExtension.cls

      7.  Compilez la classe HS.SDA3.Patient

      8.  Compilez la classe HS.Registry.Patient

Cette propriété est maintenant disponible pour être ajoutée/modifiée/supprimée à partir du streamlet SDA.

Utiliser la nouvelle propriété d'extension SDA dans les transactions HL7

Avant de pouvoir utiliser la propriété d'extension SDA, nous devons créer une nouvelle classe personnalisée qui étendra la classe HS.Gateway.HL7.HL7ToSDA3.  Ce code sera exécuté sur la passerelle EDGE.

Voici un exemple de code de la nouvelle classe personnalisée :

Class CUSTOM.Gateway.HL7.HL7ToSDA3 Extends HS.Gateway.HL7.HL7ToSDA3 [ Not ProcedureBlock ]
{

/// Méthode de rappel pour le traitement personnalisé du streamlet Patient
ClassMethod OnPatient()
{
                do ..write(cr_"&lt;Extension>")
                set tVMSCode = $$$xml($g(^||d(s,27,1)))
                set tVMSDescription = $$$xml($g(^||d(s,27,2)))
                if tVMSCode'="" {
                                do ..write(cr_"&lt;VeteransMilitaryStatus>")
                                do ..write(cr_""_tVMSCode_"")
                                do ..write(cr_"&lt;Description>"_tVMSDescription_"&lt;/Description>")
                                do ..write(cr_"&lt;/VeteransMilitaryStatus>")
                }             
                do ..write(cr_"&lt;/Extension>")
                Quit
}

Mise à jour de Production de Edge Gateway Ensemble.

Modifiez l'opération : HS.Gateway.HL7.InboundProcess et changez le paramètre "HL7ToSDA3Class" pour utiliser la nouvelle classe que nous venons de créer.

Cliquez sur le bouton "Apply" (appliquer) pour enregistrer les modifications.

Nous utilisons le message HL7 suivant :  (Notez que la pièce PID 27 a une valeur de "V^Veteran")

MSH|^~\&||HC6|||||ADT^A04|||2.5EVN|A04|20160711094500PID|||STM123^^^HC6^MR||Bolton^George||19271014|M|||1 Memorial Drive^^Cambridge^MA^02142||||||||028345081||||||||V^Veteran

 

Nous traitons maintenant ce HL7 sur une passerelle Edge Gateway.

Maintenant si nous regardons la trace, nous pouvons voir nos données dans le <Patient> SDA.

Interaction avec les nouvelles données

Maintenant que nous avons les données stockées dans SDA qui se trouvent sur le Edge (et le Registry (Registre)), nous pouvons les examiner de plusieurs façons.

Sur l'Edge, je peux exécuter le code suivant :

ClassMethod ListVeteransStatus()
{
                #dim tPatient as HS.SDA3.Patient            
                set sql="Select ID from HS_SDA3_Streamlet.Patient"
                set statement = ##class(%SQL.Statement).%New()
                set tSC = statement.%Prepare(sql)
                quit:$$$ISERR(tSC) tSC
                set result = statement.%Execute()
                while result.%Next() {
                      set tStreamletID = result.ID
                      set tSC = ##class(HS.SDA3.Container).LoadSDAObject(tStreamletID,.tPatient)
                      w !,tPatient.Name.GivenName_" "_tPatient.Name.FamilyName
                      w ": Veteran Status = "_tPatient.Extension.VeteransMilitaryStatus.Description
                }             
                Quit
}

 

Et j'obtiens ce qui suit :

HSEDGE1>d ##class([sample class]).ListVeteransStatus()
George Bolton: Veteran Status = Veteran

On peut voir que nous utilisons la propriété "Extension.VeteransMilitaryStatus.Description" pour afficher les nouvelles données personnalisées.

Sur HSREGISTRY, nous pouvons consulter les éléments suivants :

HSREGISTRY>s oPat=##class(HS.Registry.Patient).%OpenId(1)HSREGISTRY>w oPat.Extension.VeteransMilitaryStatus.CodeVHSREGISTRY>w oPat.Extension.VeteransMilitaryStatus.DescriptionVeteran

 

Pour les informations sur les patients, nous pouvons voir que la propriété personnalisée est disponible non seulement dans le SDA qui est stocké sur la passerelle Edge Gateway, mais aussi dans le registre des patients "Patient Registry".

Utilisation d'un SDA personnalisé dans une personnalisation du rapport sommaire du patient "Patient Summary Report".

Ceci n'est qu'un aperçu rapide de la manière dont vous pouvez utiliser la nouvelle extension SDA dans un code personnalisé.

Dans cet exemple, nous allons créer un rapport sommaire du patient "Patient Summary Report".

J'ai créé une classe CUSTOM.Reports.Patient.Summary, je peux simplement ajouter un nouvel élément à afficher en utilisant le code suivant :

&lt;item field="Extension/VeteransMilitaryStatus/Description" >
        &lt;caption value="Military Status" style="padding:5px;"/>
&lt;/item>

Now, when we bring up the patient and view Patient Summary Reports we can see the following:

Summary

This article showed how to set up the environment to use the new SDA extensions.  It showed how to populate the new property using HL7.  We showed how to access the data.  In this case, we were using patient data, so we were able to access it from both the Edge gateway and Registry.  We also used this new information in an example using a custom patient summary report.  Now that this data is in the SDA, it can be used by Health Insight and Patient Index as well as any other place that uses SDA.

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

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


Les stipulations de base de cet exemple sont les suivantes :

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

  

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


PDF Processing

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

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

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

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


HL7 Processing

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

=== BPL ===

Le BPL accomplit les tâches suivantes :

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

 

=== DTL ===

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

 

=== DTL Code Action ===

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

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

   Try {

     Set pdfStreamObj = pdfStreamContainer.StreamGet()

   }

   Catch {

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

      Quit

   }

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

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

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


Sample Production

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

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

 

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

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

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

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

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

Problème

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

Approche

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

Exécution

Le code (partie 1)

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

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

 

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

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

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

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

 

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

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

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

 

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

Comme vous pouvez le voir, nous obtenons deux traces.

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

 

La transformée

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

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

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

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

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

Le code (partie 2)

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

Class Demo.Processes.MessageSplitter Extends Ens.BusinessProcess{

Property TargetConfigName As Ens.DataType.ConfigName;
Parameter SETTINGS = "TargetConfigName";

Method OnRequest(pRequest As EnsLib.HL7.Message) As %Status{

   Set ORCCount = pRequest.GetValueAt("PIDgrpgrp(1).ORCgrp(*)")
    For i=1:1:ORCCount {
        Set contextString = ##class(Ens.StringContainer).%New()
       Set contextString.StringValue = i
       $$$QuitOnError(##Class(Demo.Transformations.R01Split).Transform(pRequest,.SplitR01,.contextString))
        $$$QuitOnError(..SendRequestAsync(..TargetConfigName,SplitR01,0))
    }
   Quit $$$OK
 }
}

 

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

 

Conclusion

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

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

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

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

0
0 65
Article Lorenzo Scalese · Déc 9, 2022 3m read

Une production simple qui permet de charger des paquets de transactions FHIR dans le serveur FHIR® d'InterSystems® via Box et Dropbox.&nbsp ; En utilisant les composants de connexion MFT inclus et un processus personnalisé Custom Business Process de 14 lignes, cette production traitera vos paquets de transactions vers les ressources FHIR pour une consommation immédiate à la manière magique digne d'Harry Potter.  Idéal pour les Hackathons, la recherche et les cocktails FHIR®.

Tout d'abord, je vous propose une brève visite vidéo de la production, des connexions MFT et de la configuration de l'application Oauth2 pour Box et Dropbox sur IRIS. Ensuite, je vous propose quelques étapes pour vous aider à utiliser le Fournisseur de MFT de votre choix et le flux de travail de votre choix (Desktop, API ou Web Console glisser-déposer).


<iframe width="560" height="315" src="https://www.youtube.com/embed/8q4frFgDpY0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

Quelques gaffes :

  • Les rappels OAUTH2 Call Backs nécessitent des Points d'extrémité SSL pour qu'IRIS fournisse la redirection... le mieux est de tenter le coup sur Health Connect Cloud !
  • Dropbox pour les entreprises a des difficultés avec les jetons basés sur l'équipe, mais Dropbox personnel fonctionne bien. Ce n'est pas un problème, mais cela demande un peu plus.
  • Faites attention au "/" de l'URL de base de Dropbox lorsque vous configurez les connexions MFT (assurez-vous qu'il existe).
  • Les adaptateurs MFT sortants doivent avoir un "/" de fin de ligne pour les chemins d'accès à Box et DropBox.

Maintenant, étant donné que la performance exceptionnelle du contenu alimenté par OBS n'a peut-être pas été à la hauteur, voici les étapes à suivre si la Documentation d'InterSytems ne suffit pas.

Présentation des étapes :

  • Créez l'application FHIRDrop ou FHIRBox à un point, puis STOP ! (Collaborez et écoutez)
  • Configurez les connexions MFT sur votre serveur FHIR InterSystems, HealthConnect ou I4H.
  • Complétez votre application FHIRDrop ou FHIRBox, en fournissant l'url de redirection de la connexion MFT.
  • Autorisez vos connexions MFT.
  • Créez votre production.
  • Lâchez-la comme si c'était chaud ! 
  

Création de l'application FHIRDrop ou FHIRBox

L'idée ici est de "démarrer" la configuration de votre application dans chacune des consoles Box et Dropbox Developer, ce qui vous permet de révéler l'identifiant et le secret du client, puis de laisser l'onglet en suspens et de passer aux connexions IRIS MFT.

   

 

 

(Collaborez et écoutez)
Rassemblez votre identifiant et votre secret client, fermez l'onglet du navigateur et passez à la suite :

Configurez la ou les connexions MFT

L'URL de base : https://api.box.com/2.0

 

L'URL de base : https://api.dropboxapi.com/2/ (attention à la barre de fraction de fin)

 

L'enregistrement complet de l'application

Maintenant, retournez à l'enregistrement de l'application et complétez l'App.&nbsp ; Assurez-vous de brancher l'URL de redirection de l'étape ci-dessus, et ajoutez les paramètres qui ont quelque chose à faire avec file.read, file.write.

  

Autorisation de vos connexions de transfert de fichiers gérées

Revenez à vos connexions de transfert de fichiers gérées et "autorisez" vos connexions en invoquant "Get Access Token".

  

Création de votre production

Production

La source pour les processus opérationnels personnalisés, la production est ici : https://gitlab.com/isc_cloud/fhir-drop-fhir-box-mft-2-fhir

Lâchez-la comme si c'était chaud !

  


Et maintenant, obtenez FHIR !

![image](/sites/default/files/inline/images/gar.png)
0
0 79