#Importation et exportation de données

0 Abonnés · 14 Publications

Cette rubrique regroupe les publications qui décrivent des approches, des outils et des solutions pour importer et exporter des données dans InterSystems IRIS et d'autres produits de la plateforme de données InterSystems : CSV, JSON, SQL, fichiers plats, fichiers binaires, globales, flux, etc.

Article Iryna Mykhailova · Oct 23, 2025 6m read

Salut,

C'est moi encore 😁. Je travaille actuellement à la génération de fausses données patients à des fins de test avec Chat-GPT et Python. J'aimerais également partager mon apprentissage. 😑

Tout d'abord, créer un service d'API REST personnalisé est facile en utilisant %CSP.REST.

Commençons ! 😂

1. Créez une classe datagen.restservice qui étend %CSP.REST.

Class datagen.restservice Extends%CSP.REST
{
Parameter CONTENTTYPE = "application/json";
}

2. Ajoutez une fonction genpatientcsv() pour générer les données du patient et les regrouper dans une chaîne csv

0
0 18
Article Corentin Blondeau · Avr 28, 2025 5m read

Bonjour à tous,
Cet article fait suite à la question que j'avais posée à la communauté L'adaptateur UDP ne fonctionne pas
Dans cet article, je vais vous présenter les points suivants
1) Qu'est-ce que "UDP"?
2) L'état actuel d'Iris avec UDP
3) Ma solution avec l'adaptateur UDP


1) Qu'est-ce que "UDP"?

L'acronyme UDP signifie User Datagram Protocol (protocole de datagramme utilisateur). Il s'agit de l'un des principaux protocoles composant la suite IP (Internet Protocol), utilisé pour la transmission de données sur un réseau. Voici quelques fonctionnalités clés de l'UDP:

  1. Sans connexion : L'UDP n'établit pas de connexion avant d'envoyer des données, ce qui signifie qu'il peut envoyer des messages (datagrammes) sans échange préalable.
  2. Peu fiable : Il n'y a aucune garantie que les messages envoyés via l'UDP arriveront à destination. Il n'y a pas de récupération des erreurs ni de retransmission des paquets perdus.
  3. Vitesse : Étant donné que l'UDP est sans connexion et qu'il ne nécessite pas de vérification ou de récuperation des erreurs, il est généralement plus rapide que le TCP (Transmission Control Protocol), ce qui le rend approprié pour les applications où la vitesse est cruciale.
  4. Constrruit autour des datagrammes : L'UDP envoie des messages sous forme de paquets discrets, qui peuvent être de longueur variable. Chaque paquet est traité indépendamment.
  5. Cas d'utilisation : L'UDP est couramment utilisé dans les applications où la vitesse est plus importante que la fiabilité, telles que la diffusion vidéo en continu, les jeux en ligne, la voix sur IP (VoIP) et les communications en temps réel.

En général, l'UDP est un protocole léger qui est utile pour des applications spécifiques où une faible latence est essentielle.


2) L'état actuel d'Iris avec UDP

Bien entendu, InterSystems IRIS permet d'utiliser ce protocole pour envoyer et recevoir des données.
En tant que protocole REST, il y a deux façons de le faire :
- avec ##class(%Net.UDP).%New().
- avec EnsLib.UDP.OutboundAdapter et EnsLib.UDP.InboundAdapter

##class(%Net.UDP).%New()

Même si la documentation de la classe et très détaillée, voici le lien de la documentation d'InterSystems sur la façon de l'utiliser.
Pour envoyer/recevoir des données, il faut utiliser une instance de class(%Net.UDP).%New() et une méthode associée.
En résumé, pour envoyer des données (sur localhost avec le port 3001) :

SET client = ##class(%Net.UDP).%New()
SET addrbin = ##class(%Net.UDP).GetHostAddr("127.0.0.1")
Set status = client.Send("message text", addrbin, 3001)

Pour recevoir des données (sur localhost avec le port 3001) :

Set sobj = ##class(%Net.UDP).%New(3001,"127.0.0.1")
Set data = sobj.Recv()

 

EnsLib.UDP.OutboundAdapter and EnsLib.UDP.InboundAdapter

Ce dernier est plus simple : c'est un adaptateur.
La documentation : EnsLib.UDP.OutboundAdapter et EnsLib.UDP.InboundAdapter
Pour envoyer des données :

Set status = ..Adapter.SendStream(stream)

Pour obtenir des données :

Set status = ..Adapter.Receive(stream)


Cependant, cela ne fonctionne pas. J'ai posé ma question à la communauté "L'adaptateur UDP ne fonctionne pas" et j'ai créé un ticket pour le WRC.
La réponse a été formulée comme suit :

Cet exécutable sous-jacent n'est plus installé dans le produit depuis Ensemble 2018.1.

J'ai vérifié au niveau interne et le JIRA DP-437486 a été soumis pour mettre à jour ces adaptateurs afin d'utiliser la classe %Net.UDP, mais cela sera soumis à l'approbation de la gestion des produits et aux ressources de développement disponibles.

Malheureusement, la seule option possible actuellement est de créer votre propre adaptateur personnalisé en utilisant la classe %Net.UDP.

Voici les deux principales différences entre la classe (%Net.UDP) et l'adaptateur EnsLib.UDP.OutboundAdapter 

  • La classe %Net.UDP utilise la classe système $System.UDP, elle utilise donc le code du noyau Cache/IRIS pour envoyer/recevoir les messages UDP, alors que l'adaptateur UDP utilise un tuyau de commande pour appeler des exécutables externes afin d'envoyer/recevoir le message UDP.
  • La classe %Net.UDP envoie/lit une chaîne de caractères alors que l'adaptateur UDP utilise un flux pour envoyer/lire les messages.

3) Ma solution avec l'adaptateur UDP

J'ai donc écrit ma propre méthode (approuvée par le service d'assistance) pour envoyer des données :
 

/// Adaptateur pour l'envoi de données avec une connexion UDPClass USER.UDP.OutboundAdapter Extends Ens.Adapter
{

/// Hôte du serveur UDPProperty Host As%String [ Required ];/// Port du serveur UDPProperty Port As%Integer [ Required ];/// si 1, le texte qui sera envoyé est affichéProperty UDPTrace As%Integer(VALUELIST = ",0,1") [ InitialExpression = 0, Required ];Parameter SETTINGS = "Host:Basic,Port:Basic,UDPTrace:Basic";/// Envoi de "texte" par le biais de la connexion UDP Method SendDataText(text As%String) As%Status { Try { Set status = $$$OKIf..UDPTrace=1 { Do..ShowText(text) }

    <span class="hljs-keyword">Set</span> udpClient = <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%Net.UDP</span>).<span class="hljs-built_in">%New</span>()
    <span class="hljs-keyword">Set</span> addrbin = <span class="hljs-keyword">##class</span>(<span class="hljs-built_in">%Net.UDP</span>).GetHostAddr(<span class="hljs-built_in">..Host</span>)

    <span class="hljs-keyword">Set</span> sentBytes = udpClient.Send(text, addrbin, <span class="hljs-built_in">..Port</span>)
}
<span class="hljs-keyword">Catch</span> exception {
    <span class="hljs-keyword">Set</span> status = exception.AsStatus()
}    
<span class="hljs-keyword">Return</span> status

}

/// Conversion du "stream" en texte et son envoi via la connexion UDP Method SendDataStream(stream As%Stream.Object) As%Status { Try { Do stream.Rewind() Set text = ""While 'stream.AtEnd{ Set text = text _ stream.Read() }

    <span class="hljs-keyword">Set</span> status = <span class="hljs-built_in">..SendDataText</span>(text)
}
<span class="hljs-keyword">Catch</span> exception {
    <span class="hljs-keyword">Set</span> status = exception.AsStatus()
}    
<span class="hljs-keyword">Return</span> status

}

/// Conversion de l'"objet" en json et son envoi via la connexion UDP Method SendDataJSONObject(object As%RegisteredObject, format As%String = "iw") As%Status { Try { Set status = ##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(.stream,object,,,,format) $$$ThrowOnError(status)

    <span class="hljs-keyword">Set</span> status = <span class="hljs-built_in">..SendDataStream</span>(stream)
}
<span class="hljs-keyword">Catch</span> exception {
    <span class="hljs-keyword">Set</span> status = exception.AsStatus()
}    
<span class="hljs-keyword">Return</span> status

}

/// Un texte est pris en entrée,/// Les traces de l'objet associé sont affichées Method ShowText(text As%String) { Set nbParty = $SYSTEM.SQL.CEILING($LENGTH(text)/1100) For ii=1:1:nbParty { $$$TRACE($EXTRACT(text,ii,ii+1100)) } }

}

J'espère que cet article a été, sinon utile, du moins intéressant.


Je n'ai pas testé EnsLib.UDP.InboundAdapter, donc n'hésitez pas à ajouter plus d'informations à la discussion.
Corentin

2
0 39
Article Guillaume Rongier · Avr 14, 2025 5m read

Les référentiels, applications et serveurs FHIR servent généralement des données cliniques en petites quantités, par exemple pour renvoyer des données sur un patient, ses médicaments, ses vaccins, ses allergies, ou d'autres renseignements. Cependant, il est courant qu'une grande quantité de données au format FHIR/JSON soit demandée pour être utilisée pour charger des Data Lakes, identifier des cohortes étudiées, la santé de la population ou transférer des données d'un DME (dossier médical électronique) à un autre. Pour répondre à ces scénarios d'activités commerciales qui nécessitent d'importantes extractions et charges de données, il est recommandé d'utiliser la fonctionnalité d'accès aux données en masse FHIR fournie par l'institution HL7.

InterSystems IRIS for Health met en œuvre l' ccès aux données de masse FHIR (FHIR Bulk Data Access) avec la fonctionnalité BFC - Bulk FHIR Coordinator (https://docs.intersystems.com/irisforhealthlatest/csp/docbook/DocBook.UI.Page.cls?KEY=HXFHIR_bulk). Selon la documentation d'InterSystems, le BFC « simplifie l'interaction des données de masse FHIR pour les clients et, pour ne pas surcharger un serveur FHIR avec des demandes de données de masse, le Bulk FHIR Coordinator (BFC) d'InterSystems sert d'intermédiaire dans l'interaction entre un client de données de masse et un point d'extrémité de serveur de ressources FHIR pour les demandes de données de masse. Le Bulk FHIR Coordinator peut faciliter l'exportation de données de masse FHIR pour les serveurs de ressources FHIR qui ne prennent pas en charge nativement l'interaction de données de masse". Le diagramme de la documentation illustre comment le coordinateur FHIR en bloc sert d'intermédiaire dans l'interaction entre un client et les endpoints FHIR:

Le Coordinateur Bulk FHIR sert d'intermédiaire entre un client et un endpoint FHIR

Ainsi, si vous souhaitez migrer d'un serveur FHIR vers IRIS for Health, vous pouvez utiliser BFC pour vous connecter à un serveur FHIR cible afin d'obtenir toutes les données dont vous avez besoin sous forme de fichiers json (ressources ndjson), et les importer dans votre serveur FHIR InterSystems.

Dans cet article, vous découvrirez étape par étape les différentes manières de configurer et d'utiliser BFC pour obtenir toutes les données patient d'un référentiel FHIR dans des fichiers JSON en vue d'une utilisation ultérieure.

Étape 1 - Obtenir/configurer une instance IRIS for Health pour utiliser BFC

Si vous n'avez pas d'instance IRIS for Health, cliquez sur le lien suivant pour en obtenir une: https://openexchange.intersystems.com/package/iris-fhir-template. Suivez les procédures d'installation suivantes:

1.1 Installation de Docker:

Clone/git extrait le référentiel dans n'importe quel répertoire local

git clone https://github.com/intersystems-community/iris-fhir-template.git

Ouvrez le terminal dans ce répertoire et lancez:

docker-compose up -d


1.2 Ou installation de l'IPM (nécessite un IRIS for Health en cours d'exécution):

Ouvrez l'installation d'IRIS for Health avec le client IPM installé. Appel dans n'importe quel espace de nom:

USER>zpm "install fhir-server"

Ainsi, le serveur FHIR sera installé dans l'espace de noms FHIRSERVER.

Ou encore, pour une installation programmée, on peut appeler ce qui suit:

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

Étape 2 - Création d'un nouveau justificatif d'identité

1. Access the Management Portal for the namespace FHIRSERVER (http://localhost:32783/csp/sys/%25CSP.Portal.Home.zen?$NAMESPACE=FHIRSERVER).

2. Accédez à l'Interopérabilité > Configuration > Informations d'identification:

3. Paramétrez les valeurs BulkCreds et enregistrez:

  • Identifiant: BulkCreds
  • Nom d'utilisateur: _SYSTEM
  • Mot de passe: SYS

Étape 3 - Configuration du BFC (Bulk Data Coordinator)

1. Accédez à BFC UI (http://localhost:32783/csp/healthshare/fhirserver/bulkfhir/index.html):

2. Appuyez sur le bouton + Nouvelle configuration > Création d'une nouvelle:

3. Configuration des paramètres:

  • Nom: BFC_Patients
  • Endpoint BFC (tout chemin relatif): /bulkfhir/patients
  • Gardez tous les autres paramètres tels quels

4. Appuyez sur le bouton Next.

5. Selectionnez HS.BulkFHIR.Auth.BasicAuth.Adapter et appuyez sur le bouton Next

6. Selectionnez HS.BulkFHIR.Fetch.PureFHIR.Adapter pour le champ Fetch Adapter et définissez les valeurs suivantes:

  • URL de l'endpoint: http://fhir-template:52773/fhir/r4
  • Configuration SSL: BFC_SSL
  • Type d'autorisation: HTTP
  • Identifiant HTTP: BulkCreds
  • Acceptez les valeurs par défaut pour tous les autres champs

7. Appuyez sur le bouton Next.

8. Selectionnez la valeur de HS.BulkFHIR.Storage.File.Adapter pour le champ Storage Adapter et définissez les valeurs suivantes:

  • Storage Adapter: HS.BulkFHIR.Storage.File.Adapter
  • URL du fichier: /bulkfhir/file
  • Répertoire: /usr/irissys/mgr/Temp/BulkFHIR/FHIRSERVER/

9. Appuyez sur le bouton Next.

10. Vérifiez la configuration et cliquez sur le bouton de configuration "Configure" en bas de la page:

11. Vous verrez le message de réussite: 

Étape 4 (étape finale) - Récupération de vos données de masse

1. Accéder à Exportations (Exports):

2. Appuyez sur le bouton + Nouvelle demande d'exportation:

3. Sélectionnez la valeur du champ de configuration BFC_Patients et appuyez sur le bouton Next

4. Sélectionnez l'option Patient et appuyez sur le bouton Export Now (Exportation immédiate):

5. Vous verrez le message de réussite:

6. Appuyez sur le bouton d'actualisation "Refresh" en haut de la page:

7. Consultez la session de configuration BFC_Patients terminée:

8. Appuyez sur le dernier bouton de téléchargement "Download" ():

9. Consultez la liste des fichiers json exportés:

10. Téléchargez n'importe quel fichier et consultez son contenu:

11. Pour lancer une exportation FHIR en masse à partir d'un client REST, envoyez une requête GET à votre endpoint BFC en indiquant l'opération souhaitée, par exemple:

  •     Système — GET https://bfcEndpoint/$export
  •     Patient — GET https://bfcEndpoint/Patient/$export
  •     Groupe — GET https://bfcEndpoint/Group/groupID/$export

12. Plus de détails sur l'utilisation via l'API: https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.UI.Page.cls?KEY=HXFHIR_bulk#HXFHIR_bulk_export_rest_initiate

13. Plus de détails sur l'utilisation de BFC:

  • https://docs.intersystems.com/healthconnectlatest/csp/docbook/DocBook.UI.Page.cls?KEY=HXFHIR_bulk#HXFHIR_bulk_intro
  • https://www.youtube.com/watch?v=J-AVP9MFMWI

Profitez-en!!

0
0 35
Article Sylvain Guilbaud · Jan 31, 2025 4m read

Préférez-vous ne pas lire? Regardez la vidéo de démonstration que j'ai créée:

<iframe allowfullscreen="" frameborder="0" height="360" src="https://www.youtube.com/embed/-OwOAHC5b3s" width="640"></iframe>


En tant que développeur d'interfaces, je reçois souvent des questions qui nécessitent d'étudier de grandes quantités de messages. Par exemple, lors d'une réunion récente, notre chef de projet m'a demandé combien de sites utilisaient réellement notre nouvelle interface de commandement.

D'habitude, j'essaie de copier la sortie de la visionneuse de messages pour la coller dans Excel ou simplement d'exécuter un rapport de messages pour chaque site qui passe des commandes et d'utiliser le nombre de messages renvoyés…

Cette fois-ci, en utilisant l'extension de navigateur Iris Whiz browser extension j'avais des options.

Option 1 - Simple: Exportation de CSV

Une idée mise en œuvre à partir du portail InterSystems Ideas, il suffit de cliquer sur le bouton Export (Exporter) en tant que CSV dans la barre de boutons d'IRIS Whiz pour télécharger la recherche en cours en tant que fichier CSV pour une manipulation facile d'Excel/Sheets.

Option 2 - Chic: Analyse

Dans ce cas, je venais de compléter l'outil d'analyse dans mon extension de navigateur Iris Whiz.

En ajoutant la valeur PV1-3.2 à mes critères de recherche de messages dans la visionneuse de messages (Message Viewer), j'ai pu facilement exécuter le rapport, cliquer sur Analyse et avoir instantanément les renseignements sous la forme d'un simple diagramme en forme de beignet - aucune exportation n'a été nécessaire.

 

 

Ensuite, le chef de projet a voulu savoir quels types d'examens étaient commandés par ces sites. J'ai ajouté la valeur OBR-4.2 à mes critères de recherche et j'ai relancé le rapport. En cliquant sur le bouton d'analyse, j'ai pu voir les sites qui passaient commande et les examens commandés. (Chaque critère de recherche de message est présenté sous la forme d'un graphique en anneau, étiqueté à la fin de la partie graphique de la page d'analyse)

La troisième question se pose.

Quelles commandes sont passées par quels sites?

En cliquant sur le site voulu dans le graphique interactif en anneau, j'ai pu visualiser les données dans la visionneuse de données de la page d'analyse. Un autre clic sur le bouton de filtrage à l'intérieur de cette boîte applique cette sélection de données comme filtre à tous les graphiques - ce qui signifie que le graphique en anneau des examens ne montre plus que les examens commandés pour ce site.

Graphique de site et graphique d'examen filtré par site:

 

Et enfin, la question la plus difficile.

Quand tout cela se produit-il?

Passer en revue les messages dans la page de visualisation des messages pour voir quand les commandes sont passées n'est pas une bonne idée...

Heureusement, j'ai ajouté une chronologie à la page d'analyse.

J'ai supprimé le filtre et cliqué sur le bouton 'Show on Line Graph' (Affichage du graphique linéaire) (activé pour le graphique PV1-3 dans la capture d'écran ci-dessus) afin d'afficher les données du site sur le graphique chronologique en haut de la page.

Une rapide capture d'écran nous a permis d'envoyer ce rapport à nos sites afin qu'ils puissent confirmer le nombre de commandes pour chaque jour et s'assurer que tout fonctionnait comme prévu.
Ces rapports devaient être exécutés chaque semaine, mais heureusement pour moi, cette tâche avait été simplifiée, notamment grâce à la fonction de recherche sauvegardée dans la page de visualisation des messages, qui me permettait de ne jamais avoir à me soucier des critères de recherche à ajouter.

 

Conclusions

1. Données sensibles:

Les données de votre recherche dans la visionneuse de messages (Message Viewer) sont envoyées dans un nouvel onglet du navigateur et disparaissent dès que l'onglet est fermé - vous n'avez donc pas à vous soucier de l'enregistrement de données sensibles dans le navigateur. Si vous souhaitez enregistrer un rapport utilisez la fonctionnalité par défaut d'InterSystems pour les recherches enregistrées et exécutez simplement le rapport à nouveau à une date ultérieure. J'avais prévu un mécanisme d'enregistrement des recherches à partir de la page d'analyse, mais il n'a pas été retenu dans cette version.

2. Vitesse:

La page d'analyse est alimentée par la recherche de messages et je n'ai pas mis de limites strictes à la quantité de données pouvant être affichées. Plus vous ajoutez de messages et de critères de recherche, plus la page d'analyse ralentira. C'est pourquoi j'ai ajouté une fenêtre contextuelle si vous essayez de charger plus de 200 messages, ce qui vous permet de choisir de charger ou non le diagramme à barres en haut de la page. 

Le diagramme à barres présente chaque message sous la forme d'une case à sélectionner. En cliquant sur une case du diagramme, le message est ajouté à la liste des messages sélectionnés dans la fenêtre de visualisation des données (à gauche de la page). Vous pouvez alors cliquer sur le bouton 'View Selected Messages' (Voir les messages sélectionnés) pour ouvrir ces messages dans une nouvelle page et profiter des fonctionnalités de comparaison des messages de l'extension.

Lorsque vous cliquez sur ce bouton, essayez de ne pas sélectionner trop de messages. Un maximum de 10 devrait suffire. 

Si vous téléchargez le diagramme à barres avec de grands ensembles de données (10 000), cela ne sera certainement pas bon pour votre navigateur, mais je vous laisse le soin de choisir.

0
0 45
Article Sylvain Guilbaud · Jan 29, 2025 3m read

Lors du dernier concours InterSystems "Bringing Ideas to Reality", j'ai parcouru le portail d'idées à la recherche de problèmes d'interface utilisateur à traiter. 

<iframe allowfullscreen="" frameborder="0" height="360" src="https://www.youtube.com/embed/zw51X1JQhQ0" width="640"></iframe>

J'ai implémenté les idées suivantes dans l'extension de navigateur IRIS Whiz browser extension, so if you use the management portal to help with your day-to-day integration management this extension could be for you!

Fonctionnalité ajoutée: Rafraîchissement de la file d'attente

Iris a désormais une liste déroulante de rafraîchissement automatique pour la page des files d'attente (Queues). Cette option permet de rafraîchir la file d'attente à l'intervalle sélectionné. Cette fonctionnalité ne s'applique pas à Ensemble, qui en dispose déjà.

C'est utile si vous avez un concours de clics à venir et que vous avez besoin du repos de votre doigt de clic.

Implémenté à partir de l'idée: https://ideas.intersystems.com/ideas/DPI-I-487

 

Fonctionnalité ajoutée : Exportation de la recherche au format CSV

Dans la page de la visionneuse de messages Message Viewer, vous pouvez cliquer sur le bouton Iris Whiz Export pour télécharger une copie CSV des données contenues actuellement dans votre table de recherche.

Utile si vous voulez faire une analyse rapide de vos données sans utiliser la nouvelle page Chart.JS que j'ai mis une éternité à créer (voir ici en action!).

Implémenté à partir de l'idée: https://ideas.intersystems.com/ideas/DPI-I-566

 

Fonctionnalité ajoutée : Tri de la file d'attente des pages de production

Ajout d'options de tri pour l'onglet "file d'attente" de la page de production. Le tri par défaut est le nombre d'erreurs. Cliquez sur l'en-tête d'un table pour passer de l'ordre de tri asc à l'ordre de tri desc. Utilisez la barre de recherche pour trouver rapidement des éléments.

Utile si vous ne voulez pas faire défiler sur l'écran pour accéder à la plus grande file d'attente.

Implémenté à partir de l'idée: https://ideas.intersystems.com/ideas/DPI-I-628

 

Fonctionnalité Ajoutée: Ordre insensible à la casse de la liste déroulante des catégories

Permet de classer par ordre alphabétique la liste déroulante des catégories dans la page de production, quel que soit la casse. Sans cela, l'ordre est dépendant de la casse.

Utile si vous voulez trouver des choses dans la liste des catégories, sans avoir à tout recatégoriser dans la même casse pour y parvenir.

Implémenté à partir de l'idée: https://ideas.intersystems.com/ideas/DPI-I-625

Bonus! 

Il existe également un taux de rafraîchissement dans l'onglet de la visionneuse de messages dans la page de production.  Cela rafraîchira également votre onglet de file d'attente si vous sélectionnez un intervalle et naviguez vers l'onglet de la file d'attente. 

Si l'une de ces idées vous plaît, téléchargez l'extension de navigateur et communiquez moi vos commentaires. Vous trouverez une vidéo d'installation sur la liste d'OpenExchange que je vous recommande de regarder car vous devrez en réaliser une partie pour que la plupart des fonctionnalités fonctionnent!

0
0 36
Article Iryna Mykhailova · Avr 29, 2024 12m read

Le défi du Lo-Code

Imaginons la scène.  Vous travaillez tranquillement au sein de Widgets Direct, le premier détaillant de Widgets et d'accessoires pour Widgets sur Internet.   Votre patron vous annonce une nouvelle désastreuse : certains clients ne sont peut-être pas satisfaits de leurs widgets et nous avons besoin d'une application d'assistance pour assurer le suivi de ces réclamations.   Pour rendre les choses plus intéressantes, il veut que cette application ait une très faible empreinte de code et vous demande de livrer une application en moins de 150 lignes de code à l'aide d'InterSystems IRIS.  Est-ce possible?

Avertissement : cet article présente la construction d'une application très basique et omet, par souci de concision, des éléments de détail tels que la Sécurité et la Gestion des erreurs.   Cette application ne doit être utilisée qu'à titre de référence ni pour une application de production.  Cet article utilise IRIS 2023.1 comme plate-forme de données, certaines fonctionnalités décrites ne sont pas disponibles dans les versions antérieures

Étape 1 – Définition d'un modèle de données

Nous commençons par définir un nouvel espace de noms propre - avec une base de données de codes et de données. Bien que tout soit regroupé dans une seule base de données, il est utile de diviser ces bases pour permettre l'actualisation des données.

Notre système d'assistance a besoin de 3 classes de base : un objet Ticket qui peut contenir des actions pour documenter les interactions entre un conseiller du personnel UserAccount et un contact client UserAccount.  Définissons ces classes avec quelques propriétés de base:

19 lignes de code – et nous avons notre modèle de données complet!  Nous avons défini 2 classes comme Persistent afin qu'elles puissent être sauvegardées dans la base de données, et nous héritons également de %JSON.Adapter, ce qui nous permet d'importer et d'exporter très facilement nos objets au format JSON.  En guise de test, nous configurons notre premier utilisateur dans Terminal, nous le sauvegardons et nous vérifions que l'application JSONExport fonctionne correctement

Tout cela semble bon.  Le patron nous a laissé un fichier csv avec une liste d'employés et de clients.   Nous pourrions écrire un code pour analyser ce fichier et le charger, mais y a-t-il un moyen plus simple?

Étape 2 – TÉLÉCHARGEMENT DES DONNÉES

InterSystems IRIS fournit une instruction de chargement de données (LOAD DATA) simple à utiliser en SQL qui permet de télécharger facilement des données à partir d'un fichier CSV, y compris les options permettant d'analyser les en-têtes et de renommer les champs.  Utilisons-la pour télécharger notre table d'utilisateurs:

Nous pouvons utiliser les étiquettes d'en-tête pour extraire ces données et les télécharger dans la base de données de la manière suivante:

Les 300 lignes ont été importées en une seule commande.   Les 4 lignes supplémentaires de code portent notre compte courant à 23 lignes de code écrites.   Nous pouvons rapidement vérifier que ces enregistrements sont corrects avec une sélection SQL de base

Nous avons maintenant nos données de départ, construisons donc quelques API de base pour permettre à un front-end d'être connecté.  Nous construirons notre API comme un service REST qui sert et accepte JSON.

Étape 3 – Création d'une API REST

InterSystems IRIS fournit un support REST natif par le biais de l'héritage de la classe %CSP.REST, nous allons donc créer une classe REST.Dispatch et hériter de %CSP.REST.   Cette classe est composée de deux sections: une UrlMap XData qui associe les URL et les Verbes aux méthodes, et les méthodes qui sont appelées par ces Urls.

Notre Produit minimum viable nécessite 4 opérations: la récupération de la liste des utilisateurs pour le personnel ou les clients, la récupération des derniers tickets collectés, la récupération d'un ticket unique par son numéro d'identification, et enfin la création d'un nouveau ticket.   Nous définissons nos verbes, et puis les méthodes.

GetUserList est un Curseur de base SQL intégré qui fournit les données directement en JSON.  Nous pouvons alors analyser ces données avec la fonctionnalité JSON native, les placer dans un tableau JSON et les servir en tant que corps de la réponse.  Nous passons la variable "staff" de l'URL directement à la requête pour modifier le contexte des données.

TicketSummary est presque identique, mais la requête accède alors à la table des tickets

TicketSummary est le service le plus simple.  Nous ouvrons l'objet par ID, et écrivons le %JSONExport intégré à la sortie.  Si l'objet ne se charge pas, alors nous écrivons un paquet d'erreurs

Enfin, notre méthode d'UploadTicket est la plus complexe. Nous devons lire le payload de l'objet de la requête, l'analyser en JSON, puis l'appliquer à une nouvelle instance de Ticket en utilisant %JSONImport.  Nous définissons également l'OpenDate et l'OpenTime à partir de l'heure actuelle, au lieu de les attendre en entrée.  Après une sauvegarde réussie, nous renvoyons la représentation JSON de l'objet, ou si l'objet ne se télécharge pas, nous renvoyons une erreur.

Avec ces services, nous ajoutons 60 lignes de code supplémentaires à notre total.  Nous atteignons maintenant un total de 89 lignes de code pour cette application

Nous devons maintenant créer une application Web sous Sécurité>Applications.  Celle-ci doit être définie comme une application de type REST, et le NOMCLASSE (Classname) doit être défini comme la classe de répartition (Dispatch) que nous venons de créer (Remarque: vous devrez accorder un rôle de sécurité approprié à cette application afin qu'elle puisse accéder au code et aux données).  Après avoir sauvegardé, les services REST peuvent maintenant être appelés à partir de l'URL que vous avez définie

Nous pouvons appeler l'UserList pour vérifier

Nous sommes maintenant prêts à créer des données.  Utilisons notre client REST pour envoyer un payload au service de création de tickets.  Nous fournissons un mot-clé, une description, un conseiller (Advisor) et un contact (Contact), et nous recevons en retour le fichier JSON du ticket que nous avons créé, y compris l'OpenDate et le TicketId

Nous avons maintenant notre Produit minimum viable.  En utilisant un constructeur de formulaire frontal de notre choix, nous pouvons maintenant envoyer et recevoir des informations sur les tickets via nos services REST.

Étape 4 – Exigences d'interopérabilité

Vous avez maintenant construit une application de ticketing de base en seulement 89 lignes de code écrites.   Votre patron doit être impressionné?   Oui, mais il a de mauvaises nouvelles.  Vous avez oublié une exigence.   Widgets Direct a un contrat spécial avec les régions francophones et tous les billets rédigés en français doivent être envoyés à Mme Bettie Francis pour un premier examen.   Heureusement, vous avez un collègue qui a suivi l'excellent article de Robert Luman "Sur la prise en charge du langage naturel par Python" ("Python Natural Language Support") et qui a créé un service REST capable d'accepter un échantillon de texte et d'en identifier la langue.   Pouvons-nous utiliser l'Interopérabilité d'InterSystems IRIS pour appeler ce service et mettre automatiquement à jour le conseiller de Mme Francis lorsque le texte est en français?

Nous devons commencer par la création de Classes de messages afin d'avoir un moyen d'envoyer et de recevoir nos demandes.  Nous avons besoin d'une requête qui contiendra l'identifiant du ticket et le texte de l'échantillon, et d'une réponse qui renverra le Code de la langue et la Description. Ceux-ci hériteront d' Ens. Request et d'Ens. Response

6 autres lignes de code écrites nous permettent d'atteindre 95 LOC.  Nous devons maintenant créer notre opération, qui enverra la requête au service de votre collègue et récupérera la réponse.  Nous définissons une opération Outbound, avec des propriétés pour le serveur et l'URL, et nous les exposons à la configuration de l'utilisateur en les incluant dans le paramètre SETTINGS.   Cela nous permettra de mettre facilement à jour la requête si le chemin d'accès au serveur change.   Nous créons une méthode d'aide pour configurer une requête HTTPRequest, puis nous l'utilisons pour appeler le service et remplir notre réponse

27 lignes de code supplémentaires nous amènent à plus de 100, nous avons maintenant 122 lignes écrites.   Nous devons maintenant configurer cette classe dans notre production Ensemble.  Nous devons maintenant configurer cette classe dans notre production Ensemble. Accédez à la configuration de la production sous Interopérabilité, et cliquez sur Ajouter (Add) sous l'En-tête Opérations (Operations Header).  Configurez votre opération avec le nom de la classe et un nom d'affichage

Nous pouvons ensuite cliquer dessus pour accéder aux paramètres (Settings), entrer le nom du serveur et l'URL et activer l'Opération. 

Nous avons maintenant besoin d'une deuxième opération qui prend l'identifiant d'un ticket et associe le conseiller à l'identifiant d'un compte d'utilisateur fourni.  Nous avons besoin à la fois d'un message et d'une classe d'opération, mais dans ce cas, nous ne renverrons pas de réponse, l'opération exécutera la tâche sans feedback

Les 12 lignes de code supplémentaires nous amènent à 134 lignes écrites.  Nous pouvons ajouter cette Opération à la Production de la même manière que nous avons ajouté le Service linguistique (Language Service), mais dans ce cas, nous n'avons pas de configuration à définir.

Nous avons ensuite besoin d'un routeur capable d'appeler le service, d'évaluer la réponse et, éventuellement, d'appeler l'Opération du conseiller français (French Advisor Operation).  Nous allons vers Interoperability>Build>BusinessProcess et accédons à l'outil de création de règles visuelles.  Nous définissons d'abord nos contextes pour la requête (Request) et la réponse (Response) et nous ajoutons un élément d'appel (Call).  Nous définissons nos entrées et nos sorties sur les classes de messages que nous avons créées, puis nous mappons les entrées à l'aide du générateur de requêtes.  Assurez-vous que l'option "Asynchrone" (Asynchronous) n'est pas cochée, car nous voulons attendre la réponse avant de continuer.

Nous ajoutons ensuite un élément "Si" (If) pour évaluer le code de langue renvoyé.  S'il s'agit de "fr", nous voulons faire appel à l'opération de FrenchAdvisor

Mme Francis est l'utilisateur ID 11, nous configurons donc notre objet d'appel (Call object) pour qu'il fournisse notre message AdvisorUpdate au service FrenchAdvisor, et nous utilisons le constructeur de requêtes pour transmettre le TicketID et une valeur fixe de 11 au paramètre Advisor

Nous pouvons maintenant l'ajouter à la Production en cliquant sur Ajouter (Add) sous l'en-tête Processus, en sélectionnant la classe et en lui donnant un nom d'affichage "FrenchRouter". 

Nous avons maintenant notre routage en place. Nous avons juste besoin d'un service pour rechercher les nouveaux tickets et les envoyer au routeur pour traitement.  Nous définissons une classe de service basée sur un adaptateur SQL de la manière suivante (en ajoutant 8 lignes de code supplémentaires à notre compte):

Nous l'ajoutons ensuite à notre production comme nous l'avons fait avec les objets Operation et Process.  Nous devons configurer l'adaptateur SQL.   Nous fournissons les détails de connexion via un DSN de type ODBC à la base de données locale, ainsi qu'une requête SQL de base que le Service utilisera pour interroger les tickets sur un minuteur défini dans le paramètre d'intervalle d'appel CallInterval.   Cette requête est associée au paramètre Key Field Name qui définit la clé unique de la requête et empêche le renvoi d'enregistrements déjà envoyés

Avec ceci en place, nous avons maintenant une production complète qui va scanner les nouveaux tickets, passer le texte à un service externe pour analyser la langue, et éventuellement réinitialiser le conseiller en fonction de la réponse.  Essayons cela!  Nous commençons par envoyer une requête en anglais, qui nous est retournée sous la forme TicketId 70.  Nous attendons une seconde, et accédons à cet enregistrement via le service GetTicket REST, ici le conseiller est inchangé par rapport à la requête originale

Répétons l'opération avec le texte en français (French Text)

Lorsque nous réclamons le ticket 71, notre conseiller a été changé en Mme Francis, comme nous nous y attendions! Nous pouvons le vérifier dans l'Interoperability en localisant le message dans Visual Trace, et en vérifiant que les Opérations ont été appelées comme prévu.

Nous en sommes maintenant à 142 lignes de code écrites, et nous avons une application d'InterSystems IRIS qui persiste les données, les a chargées en utilisant LOAD DATA, fournit une API REST de base pour la visualisation et l'édition des données, et un moteur d'intégration avancé fournissant un Support de décision basé sur des appels à un service REST externe.  Assurément, personne ne peut demander mieux?

Étape 5 – En savoir plus: Analyse

Votre application est un succès retentissant et les données affluent.   L'accès à ces données précieuses nécessite une certaine expertise, et la direction de Widgets Direct souhaite obtenir des informations.  Est-il possible de fournir un accès interactif aux données?

Grâce à InterSystems IRIS Analytics, nous pouvons fournir un accès facile et rapide à des outils de manipulation de données évolués.   Nous devons d'abord activer le support Analytics sur notre application web interne: 

Cela nous permet d'utiliser la section Analytics par rapport à notre Espace de noms.  Commencez par ouvrir Analytics>Architect.  Sélectionnez Nouveau (New) et remplissez le formulaire pour créer un Cube d'analyse pour la classe de tickets. 

Ensuite, nous pouvons configurer nos dimensions et une liste déroulante Drilldown à l'aide du constructeur Visual Builder.   Cette vue est accessible par glisser-déposer.  Une liste peut également être créée à l'aide d'un constructeur visuel simple, afin de personnaliser ce que l'utilisateur voit lorsqu'il interroge un point de données

Une fois la configuration de base établie, nous pouvons Sauvegarder, Compiler et Construire le Cube.  Cela permettra de configurer tous les indices et d'activer le Cube pour l'analyse dans le logiciel Analyzer.   Ouvrez l'Analyzer pour commencer à jouer avec les données.   Dans l'exemple présenté, nous pouvons facilement comparer les conseillers par rapport à une hiérarchie d'années et de trimestres filtrés par le contact en question.   Une fois que vous avez cliqué sur une cellule, vous pouvez appuyer sur l'icône des jumelles pour appeler la liste Drilldown que vous avez créée en vue d'une analyse et d'une exportation plus approfondies

Conclusion

Avec seulement 142 lignes de code, nous disposons d'une application BackEnd moderne, simple mais fonctionnelle, avec des outils permettant la communication inter-applications et l'analyse avancée.   Il s'agit d'une mise en œuvre simplifiée à l'extrême qui ne doit être utilisée que comme exemple des éléments de base nécessaires à la construction d'une application de base de données dans IRIS.  Comme il a été mentionné au début de l'article, ce code n'est pas prêt pour la production et, dans le cadre d'une utilisation réelle, les développeurs doivent se référer à la documentation et aux meilleures pratiques d'InterSystems IRIS pour s'assurer que leur code est robuste, sécurisé et évolutif (aucune de ces caractéristiques ne s'applique à cette base de code).  Bon codage!

1
0 99
Article Iryna Mykhailova · Avr 22, 2024 3m read

Dans les versions récentes d'IRIS, une nouvelle commande puissante de chargement de données a été introduite dans SQL : LOAD DATA. Cette fonctionnalité a été hautement optimisée pour importer des données dans IRIS de manière extrêmement rapide, permettant d'insérer des centaines de gigaoctets de données en quelques secondes au lieu d'heures ou de jours.

Il s’agit d’une amélioration très intéressante. Cependant, un gros problème persiste lors du chargement des données. À savoir le temps et les tracas nécessaires pour :

0
0 59
Article Pierre LaFay · Avr 9, 2024 5m read

J'ai récemment eu besoin de surveiller depuis HealthConnect les enregistrements présents dans une base de données NoSQL dans le Cloud, plus précisément Cloud Firestore, déployé dans Firebase. D'un coup d'œil rapide, j'ai pu voir à quel point il serait facile de créer un adaptateur ad-hoc pour établir la connexion en tirant parti des capacités d'Embedded Python, et je me suis donc mis au travail.

Préparation de l'environnement

0
0 75
Article Pierre LaFay · Fév 25, 2024 3m read

Je souhaite aborder les problèmes désagréables liés à la lecture d'un texte plat en ASCII, UTF*
excluant explicitement HTML, EBCDIC, et autres encodages.
D'après Wikipediail existe au moins 8 variantes de caractères de contrôle.

  • CR+LF est typique de Windows
  • LF est typique du monde Linux/UNIX
  • CR est le préféré de Mac

Comme vous pouvez le déduire des noms, l'inspiration vient des machines à écrire mécaniques.

Dans IRIS* comme dans Caché ou Ensemble ou ... les classes %Stream* et %File* offrent la même propriété avec la même valeur par défaut.

1
0 66
Question Sylvain Guilbaud · Août 23, 2023

Est-il prévu que LOAD DATA prenne en compte plusieurs formats de DATE/DATETIME avec, par exemple un paramètre de indiquant le format utilisé dans les données sources ?

exemple :

LOAD DATA .../...
USING
{
  "from": {
    "file": {
       "dateformat": "DD/MM/YYYY"
    }
  }
}
3
0 88
Article Iryna Mykhailova · Oct 13, 2023 12m read

La série d'articles relatifs à l'application QuinielaML se poursuit. Dans cet article, nous verrons comment préparer les données brutes que nous avons capturées à l'aide de la fonctionnalité Embedded Python.

Bienvenue à toutes et à tous !

Happy Artist GIF by VIRTUTE - Find & Share on GIPHY

Introduction

Si vous vous souvenez de l'article précédent, nous avons capturé les données des résultats des matches de football de première et deuxième division pour les 23 dernières années en utilisant Embedded Python à partir d'un site web externe. Maintenant que nous disposons des données brutes, nous allons les transformer et les préparer pour faciliter la maintenance de l'application et l'utilisation de notre modèle de prédiction.

QUINIELA_Object.RawMatch

Voyons sous quelle forme se présentent les données que nous avons saisies dans notre base de données IRIS :

Comme vous le voyez dans la capture d'écran suivante, elles sont peu différentes des informations présentes sur le site web de BDFutbol :

 

Tableaux principaux :

Pour faciliter la maintenance ultérieure des données communes et améliorer les performances du modèle de prédiction, nous avons défini deux tableaux principaux pour stocker les équipes et les arbitres. Ces tableaux ne comprendront que la colonne contenant l'identifiant de l'enregistrement et le nom. Examinons les deux tableaux :

QUINIELA_Object.Referee:

Сorrespondance avec le tableau principal d'arbitres.

QUINIELA_Object.Team

Сorrespondance avec le tableau principal d'arbitres.

 

Préparation de données

Avec nos tableaux principaux et nos données brutes, nous pouvons maintenant entreprendre le processus de préparation de ces données en vue de la formation de notre modèle. Nous avons ici l'opération métier qui sera chargée de la préparation :

Class QUINIELA.BO.PrepareBO Extends Ens.BusinessOperation
{

Parameter INVOCATION = "Queue";
Method PrepareData(pRequest As QUINIELA.Message.PrepareRequest, pResponse As QUINIELA.Message.PrepareResponse) As%Status
{
    Set sc = $$$OKset pResponse = ##class(QUINIELA.Message.PrepareResponse).%New()
    set pResponse.Operation = pRequest.Operation
    
    set sqlTruncateTrain = "TRUNCATE TABLE QUINIELA_Object.MatchTrain"set statementTruncateTrain = ##class(%SQL.Statement).%New()
    set statusTruncateTrain = statementTruncateTrain.%Prepare(sqlTruncateTrain)
    if ($$$ISOK(statusTruncateTrain)) {
        set resultSetTruncateTrain = statementTruncateTrain.%Execute()
        if (resultSetTruncateTrain.%SQLCODE = 0) {
            set sqlMatchTrain = "INSERT INTO QUINIELA_Object.MatchTrain (Day, Division, Journey, LocalTeam, Referee, Result, VisitorTeam, IntDay) "_
                "SELECT "_
                "TO_DATE(RM.Day,'DD/MM/YYYY') AS DayTransformed, "_
                "RM.Division, "_
                "RM.Journey, "_
                "LT.ID as LocalTeam, "_
                "R.ID as Referee, "_
                "CASE WHEN CAST(RM.GoalsLocal As INTEGER) > CAST(RM.GoalsVisitor As INTEGER) THEN 1 WHEN CAST(RM.GoalsLocal As INTEGER) < CAST(RM.GoalsVisitor As INTEGER) THEN 2 ELSE 0 END as Result, "_
                "VT.ID as VisitorTeam, "_
                "CAST({fn CONCAT({fn CONCAT(SUBSTR(RM.Day,7,4),SUBSTR(RM.Day,4,2))},SUBSTR(RM.Day,1,2))} As INTEGER) as IntDay "_
                "FROM "_
                "QUINIELA_Object.RawMatch RM "_
                "LEFT JOIN QUINIELA_Object.Team LT ON UPPER(RM.LocalTeam) = UPPER(LT.Name) "_
                "LEFT JOIN QUINIELA_Object.Team VT ON UPPER(RM.VisitorTeam) = UPPER(VT.Name) "_
                "LEFT JOIN QUINIELA_Object.Referee R ON UPPER(RM.Referee) = UPPER(R.Name)"set statementMatchTrain = ##class(%SQL.Statement).%New()
            set statusMatchTrain = statementMatchTrain.%Prepare(sqlMatchTrain)
            if ($$$ISOK(statusMatchTrain)) {
                set resultSetMatchTrain = statementMatchTrain.%Execute()
                if (resultSetMatchTrain.%SQLCODE = 0) {
                    set sqlUpdateLocalStreak = "UPDATE QUINIELA_Object.MatchTrain SET QUINIELA_Object.MatchTrain.LocalStreak = "_
                        "(SELECT SUM(CASE WHEN IsVictory = 1 THEN 4-%VID ELSE 0 END) FROM "_
                        "(SELECT TOP 3 SubMatch.IntDay, "_
                        "CASE WHEN Result = 1 THEN 1 ELSE 0 END AS IsVictory "_
                        "FROM QUINIELA_Object.MatchTrain AS SubMatch "_
                        "WHERE "_
                        "UPPER(SubMatch.LocalTeam) = UPPER(QUINIELA_Object.MatchTrain.LocalTeam) "_
                        "AND SubMatch.IntDay < QUINIELA_Object.MatchTrain.IntDay "_
                        "ORDER BY SubMatch.IntDay DESC)) "set statementUpdateLocalStreak = ##class(%SQL.Statement).%New()
                    set statusUpdateLocalStreak = statementUpdateLocalStreak.%Prepare(sqlUpdateLocalStreak)
                    if ($$$ISOK(statusUpdateLocalStreak)) {
                        set resultSetUpdateLocalStreak = statementUpdateLocalStreak.%Execute()
                        if (resultSetUpdateLocalStreak.%SQLCODE = 0) {
                            set sqlUpdateVisitorStreak = "UPDATE QUINIELA_Object.MatchTrain SET QUINIELA_Object.MatchTrain.VisitorStreak = "_
                                "(SELECT SUM(CASE WHEN IsVictory = 1 THEN 4-%VID ELSE 0 END) FROM "_
                                "(SELECT TOP 3 SubMatch.IntDay, "_
                                "CASE WHEN Result = 2 THEN 1 ELSE 0 END AS IsVictory "_
                                "FROM QUINIELA_Object.MatchTrain AS SubMatch "_
                                "WHERE "_
                                "UPPER(SubMatch.VisitorTeam) = UPPER(QUINIELA_Object.MatchTrain.VisitorTeam) "_
                                "AND SubMatch.IntDay < QUINIELA_Object.MatchTrain.IntDay "_
                                "ORDER BY SubMatch.IntDay DESC)) "set statementUpdateVisitorStreak = ##class(%SQL.Statement).%New()
                            set statusUpdateVisitorStreak = statementUpdateVisitorStreak.%Prepare(sqlUpdateVisitorStreak)
                            if ($$$ISOK(statusUpdateVisitorStreak)) {
                                set resultSetUpdateVisitorStreak = statementUpdateVisitorStreak.%Execute()
                                set sc = statusUpdateVisitorStreak
                            }
                            else {
                                set sc = statusUpdateVisitorStreak
                            }
                        }
                    }
                    else {
                        set sc = statusUpdateLocalStreak
                    }
                }
            }
            else {
                set sc = statusMatchTrain
            }
        }
    }
    
    set pResponse.Status = "Finished"Return sc
}

XData MessageMap
{
"QUINIELA.Message.PrepareRequest">
    PrepareData
}

}

Examinons maintenant en détail chacune des instructions SQL que nous exécutons :

  1. Nous quittons le tableau de données d'apprentissage :

    TRUNCATETABLE QUINIELA_Object.MatchTrain
  2. Nous lançons une insertion massive dans notre tableau d'apprentissage QUINIELA_Object.MatchTrain

    INSERTINTO QUINIELA_Object.MatchTrain (Day, Division, Journey, LocalTeam, Referee, Result, VisitorTeam, IntDay)
    SELECTTO_DATE(RM.Day,'DD/MM/YYYY') AS DayTransformed,
    RM.Division,
    RM.Journey,
    LT.ID as LocalTeam,
    R.ID as Referee,
    CASEWHENCAST(RM.GoalsLocal AsINTEGER) > CAST(RM.GoalsVisitor AsINTEGER) THEN1WHENCAST(RM.GoalsLocal AsINTEGER) < CAST(RM.GoalsVisitor AsINTEGER) THEN2ELSE0ENDasResult,
    VT.ID as VisitorTeam,
    CAST({fn CONCAT({fn CONCAT(SUBSTR(RM.Day,7,4),SUBSTR(RM.Day,4,2))},SUBSTR(RM.Day,1,2))} AsINTEGER) as IntDay
    FROM
    QUINIELA_Object.RawMatch RM
    LEFTJOIN QUINIELA_Object.Team LT ONUPPER(RM.LocalTeam) = UPPER(LT.Name)
    LEFTJOIN QUINIELA_Object.Team VT ONUPPER(RM.VisitorTeam) = UPPER(VT.Name)
    LEFTJOIN QUINIELA_Object.Referee R ONUPPER(RM.Referee) = UPPER(R.Name)
    Pour y parvenir, nous remplaçons le libellé par le nom de l'arbitre et les équipes par leur référence dans les tableaux principaux. Nous obtenons également le résultat à partir des scores du match, 0 pour le match nul, 1 pour la victoire à domicile et 2 pour la victoire à l'extérieur. La colonne
**Résultat** est celle qui définit notre modèle de prédiction comme un modèle de classification, c'est-à-dire que chaque correspondance est classée dans l'une des trois classes (1, X ou 2).  
  1. Nous calculons les séries pour chaque équipe selon qu'elle joue à domicile ou à l'extérieur. Nous avons ajouté ces colonnes pour améliorer, autant que possible, la performance du modèle prédictif. Nous avons supposé qu'une équipe qui a plusieurs victoires consécutives à domicile ou à l'extérieur a plus de facilité à poursuivre les victoires en étant "sur une série". Le calcul se fait de la manière suivante : on obtient les 3 derniers matchs (à domicile pour l'équipe qui joue à domicile ou à l'extérieur pour celle qui joue à l'extérieur), si elle a gagné le dernier match on lui attribue 3 points, si elle a gagné le match avant-dernier on lui attribue 2 points et si elle a gagné le match précédent avant-dernier on lui attribue 1 point, enfin on additionne les points obtenus ce qui donne une valeur numérique à la série. Série à domicile :
UPDATE QUINIELA_Object.MatchTrain SET QUINIELA_Object.MatchTrain.LocalStreak = 
    (SELECTSUM(CASEWHEN IsVictory = 1THEN4-%VID ELSE0END) FROM
        (SELECT TOP 3 SubMatch.IntDay, 
            CASEWHENResult = 1THEN1ELSE0ENDAS IsVictory 
        FROM QUINIELA_Object.MatchTrain AS SubMatch 
        WHEREUPPER(SubMatch.LocalTeam) = UPPER(QUINIELA_Object.MatchTrain.LocalTeam) 
            AND SubMatch.IntDay < QUINIELA_Object.MatchTrain.IntDay 
        ORDERBY SubMatch.IntDay DESC
        )
    )

Série à l'extérieur:

UPDATE QUINIELA_Object.MatchTrain SET QUINIELA_Object.MatchTrain.VisitorStreak = 
    (SELECTSUM(CASEWHEN IsVictory = 1THEN4-%VID ELSE0END) 
    FROM
        (SELECT TOP 3 SubMatch.IntDay, 
            CASEWHENResult = 2THEN1ELSE0ENDAS IsVictory 
        FROM QUINIELA_Object.MatchTrain AS SubMatch 
        WHEREUPPER(SubMatch.VisitorTeam) = UPPER(QUINIELA_Object.MatchTrain.VisitorTeam) 
            AND SubMatch.IntDay < QUINIELA_Object.MatchTrain.IntDay 
            ORDERBY SubMatch.IntDay DESC
        )
    )

Voyons quel est le résultat de cette série d'insertions et de mises à jour consultant le tableau QUINIELA_Object.MatchTrain :

Comme vous voyez, nous avons transformé les champs de texte en valeurs numériques... ou non ? Examinons la définition de la classe :

Class QUINIELA.Object.MatchTrain Extends (%Persistent, %JSON.Adaptor) [ DdlAllowed ]
{

/// Jour du matchProperty Day As%Date;/// Équipe localeProperty LocalTeam As%String;/// Équipe jouant à l'extérieurProperty VisitorTeam As%String/// Série localeProperty LocalStreak As%Integer;/// Série de victoires de l'équipe jouant à l'extérieurProperty VisitorStreak As%Integer/// ArbitreProperty Referee As%String;/// RésultatProperty Result &As%String/// DivisionProperty Division As%String;/// ItinéraireProperty Journey As%String;/// Nombre entier de joursProperty IntDay As%Integer;
}

Comme vous voyez, les références aux tableaux principaux sont toujours de type %String. Pourquoi ? Sur cette page vous trouverez l'explication de la documentation, mais en résumé, c'est parce que, bien qu'elles soient réellement numériques, elles ne correspondent pas à des valeurs quantifiables, mais à des identifiants.

Parfait, nous avons maintenant tout ce qu'il faut pour créer et former notre modèle prédictif.

Création et formation du modèle prédictif

Grâce à la fonctionnalité d'IntegratedML, cette étape est très simple pour nous, puisqu'il nous suffit d'exécuter deux commandes simples dans notre base de données. Examinons la transaction métier que nous avons créée à cette fin :

Class QUINIELA.BO.TrainBO Extends Ens.BusinessOperation
{

Parameter INVOCATION = "Queue";/// Description
Method CreateAndTrainModel(pRequest As QUINIELA.Message.TrainRequest, pResponse As QUINIELA.Message.TrainResponse) As%Status
{
        Set tSC = $$$OKset pResponse = ##class(QUINIELA.Message.TrainResponse).%New()
        set pResponse.Operation = pRequest.Operation
        set pResponse.Status = "In Process"set sql = "SELECT MODEL_NAME FROM INFORMATION_SCHEMA.ML_MODELS WHERE MODEL_NAME = 'QuinielaModel'"set statement = ##class(%SQL.Statement).%New()
        set status = statement.%Prepare(sql)
        $$$TRACE(status)
        if ($$$ISOK(status)) {
            set resultSet = statement.%Execute()
            $$$TRACE(resultSet.%SQLCODE)
            if (resultSet.%SQLCODE = 0) {
                while (resultSet.%Next() '= 0) {
                    if (resultSet.%GetData(1) '= "") {
                        set sqlDrop = "DROP MODEL QuinielaModel"set statementDrop = ##class(%SQL.Statement).%New()
                        set statusDrop = statementDrop.%Prepare(sqlDrop)
                        if ($$$ISOK(statusDrop)) {
                            set resultSetDrop = statementDrop.%Execute()
                            if (resultSetDrop.%SQLCODE = 0) {
                                set tSC = statusDrop                                                                
                            }
                        }
                    }
                }
            }            
        }
        $$$TRACE("Creating model")
        set sqlCreate = "CREATE MODEL QuinielaModel PREDICTING (Result) FROM QUINIELA_Object.MatchTrain"set statementCreate = ##class(%SQL.Statement).%New()
        set statusCreate = statementCreate.%Prepare(sqlCreate)
        if ($$$ISOK(statusCreate)) {
            $$$TRACE("Model created")
            set resultSetCreate = statementCreate.%Execute()
            if (resultSetCreate.%SQLCODE = 0) {
                set tSC = statusCreate                                
            }
        }
        else
        {
            set tSC = statusDrop
        }

        $$$TRACE("Training model")
        set sqlTrain = "TRAIN MODEL QuinielaModel"set statementTrain = ##class(%SQL.Statement).%New()
        set statusTrain = statementTrain.%Prepare(sqlTrain)
        if ($$$ISOK(statusTrain)) {
            set resultSetTrain = statementTrain.%Execute()
            if (resultSetTrain.%SQLCODE = 0) {
                // VALIDATION OF THE MODEL WITH THE PRE-LOADED MATCHESset sqlValidate = "VALIDATE MODEL QuinielaModel FROM QUINIELA_Object.MatchTrain"set statementValidate = ##class(%SQL.Statement).%New()
                set statusValidate = statementValidate.%Prepare(sqlValidate)
                set resultSetValidate = statementValidate.%Execute()
                set tSC = statusValidate                                    
            }
        }
        else {
            set tSC = statusTrain
        }
        
        set pResponse.Status = "Finished"Return tSC
}

XData MessageMap
{
"QUINIELA.Message.TrainRequest">
    CreateAndTrainModel
}

}

Analysons ce que fait notre BO :

  1. Création du modèle prédictif et définition de la valeur à prédire en indiquant la colonne de notre tableau d'entraînement correspondant.

    CREATEMODEL QuinielaModel PREDICTING (Result) FROM QUINIELA_Object.MatchTrain
  2. Formation de notre modèle nouvellement créé.

    TRAIN MODEL QuinielaModel
  3. Validation du modèle créé sur la base du tableau d'entraînement utilisé.

    VALIDATE MODEL QuinielaModel FROM QUINIELA_Object.MatchTrain

Avec ces trois étapes très simples, notre modèle est prêt à générer des prédictions. Examinons la qualité de notre modèle. Pour ce faire, exécutons la requête suivante :

SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS

Avec cette requête, nous obtiendrons les mesures suivantes :

Eh bien, pas si mal, pour la victoire au niveau local, le taux de réussite est de 52%, pour la victoire au niveau des visiteurs de 41% et pour les matchs nuls de 37%, nous dépassons le taux de réussite de 33% dû au pur hasard !

<img alt="Boxing Memes on X: "Le compatriote de Golovkin réagit à sa victoire... #GolovkinGeale http://t.co/MDW6F5eJlz" / X" src="https://pbs.twimg.com/media/BthSZmaIcAAsIXU.jpg">

0
0 91
Article Evgeny Shvarov · Fév 20, 2023 2m read

Salut les développeurs ! Nous avons souvent besoin de déployer des données en même temps que des morceaux de code de l'application. Et pour les développeurs d'InterSystems IRIS, la question peut se poser comme suit : "Comment puis-je déployer les données que j'ai dans les globales ?"

InterSystems IRIS Globals Model QuickStart | InterSystems

Je vous propose ici l'une des approches suivantes : le déploiement de données globales à l'aide du gestionnaire de paquet ZPM package manager.

0
0 79
Article Lucas Enard · Sept 30, 2022 9m read

Dans ce GitHub nous recueillons des informations à partir d'un csv, nous utilisons une DataTransformation pour les transformer en un objet FHIR, puis nous sauvegardons ces informations sur un serveur FHIR, et tout cela en utilisant uniquement Python.

The objective is to show how easy it is to manipulate data into the output we want, here a FHIR Bundle, in the IRIS full Python framework.

1. Fhir-orga-dt

2. Préalables

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

Si vous travaillez à l'intérieur du conteneur, comme il est montré dans 3.3., vous n'avez pas besoin d'installer fhirpy et fhir.resources.

Si vous n'êtes pas dans le conteneur, vous pouvez utiliser pip pour installer fhirpy et fhir.resources.
Vérifiez fhirpy et fhir.resources pour plus d'information.

3. Installation

3.1. Installation pour le développement

Clone/git tire le repo dans n'importe quel répertoire local, par exemple comme indiqué ci-dessous :

git clone https://github.com/LucasEnard/fhir-client-python.git

Ouvrez le terminal dans ce répertoire et lancez :

docker build .

3.2. Portail de gestion et VSCode

Ce référentiel est prêt pour VS Code.

Ouvrez le dossier fhir-client-python cloné localement dans VS Code.

Si vous y êtes invité (coin inférieur droit), installez les extensions recommandées.

3.3. Avoir le dossier ouvert à l'intérieur du conteneur

Vous pouvez être à l'intérieur du conteneur avant de coder si vous le souhaitez.
Pour cela, il faut que docker soit activé avant d'ouvrir VSCode.
Ensuite, dans VSCode, lorsque vous y êtes invité ( coin inférieur droit ), rouvrez le dossier à l'intérieur du conteneur afin de pouvoir utiliser les composants python qu'il contient.
La première fois que vous effectuez cette opération, cela peut prendre plusieurs minutes, le temps que le conteneur soit préparé.

Si vous n'avez pas cette option, vous pouvez cliquer dans le coin inférieur gauche et cliquer sur Ouvrir à nouveau dans le conteneur puis sélectionner De Dockerfile

Plus d'informations ici

Architecture


En ouvrant le dossier à distance, vous permettez à VS Code et à tous les terminaux que vous ouvrez dans ce dossier d'utiliser les composants python dans le conteneur.

4. Serveur FHIR

Pour compléter cette présentation, nous allons utiliser un serveur fhir.
Ce serveur fhir était déjà intégré lorsque vous avez cloné et construit le conteneur.

L'url est localhost:52773/fhir/r4

5. Présentation pas à pas

Présentation complète de la réalisation d'IRIS en Python.

5.1. Messages and objects

Les objets et les messages contiennent les informations entre nos services, nos processus et nos opérations.

Dans le fichier obj.py nous devons créer une classe de données qui correspond au csv, ceci sera utilisé pour garder l'information avant de faire la DataTransformation.
Dans notre exemple, le csv organisation.csv ressemble à ceci,

active;name;city;country;system;value
true;Name1;city1;country1;phone;050678504
false;Name2;city2;country2;phone;123456789

Par conséquent, l'objet ressemblera à ceci,

@dataclass
# > Cette classe représente une organisation simple
class BaseOrganization:
    active:bool = None
    name:str = None
    city:str = None
    country:str = None
    system:str = None
    value:str = None

Dans le fichier msg.py, nous aurons deux types de requêtes, la première contenant les informations d'une organisation avant la DataTransformation et la seconde contenant les informations de l'organisation après la DataTransformation.

5.2. Service aux entreprises

Dans le fichier bs.py, nous avons le code qui nous permet de lire le csv et pour chaque ligne du csv (donc pour chaque organisation), le mapper dans un objet que nous avons créé plus tôt. Ensuite, pour chacune de ces lignes (organisation), nous créons une requête et l'envoyons à notre processus pour effectuer la DataTransformation.

# Nous ouvrons le fichier
with open(self.path + self.filename,encoding="utf-8") as csv:
    # Nous le lisons et le mappons en utilisant l'objet BaseOrganization précédemment utilisé
    reader = DataclassReader(csv, self.fhir_type ,delimiter=";")
    # Pour chacune de ces organisations, nous pouvons créer une requête et l'envoyer au processus
    for row in reader:
        msg = OrgaRequest()
        msg.organization = row
        self.send_request_sync('Python.ProcessCSV',msg)

5.3. Service aux entreprises

Dans le fichier bp.py, nous avons la DataTransformation, qui convertit un simple objet python contenant peu d'informations en un objet FHIR R4

Voici les étapes pour effectuer une DataTransformation en utilisant du python embarqué sur notre organisation simple,

# Création de l'objet Organisation
organisation = Organisation()

# Mappage des informations de la demande à l'objet Organisation
organization.name = base_orga.name

organization.active = base_orga.active

## Création de l'objet Adresse et mappage des informations
## de la demande à l'objet Addresse
adresse = Addresse()
adress.country = base_orga.country
adress.city = base_orga.city

### Configuration de l'adresse de notre organisation à celle que nous avons créée
organization.address = [adress]

## Création de l'objet ContactPoint et mise en correspondance
## des informations de la demande avec l'objet ContactPoint
telecom = ContactPoint()
telecom.value = base_orga.value
telecom.system = base_orga.system

### Configuration de la télécommunication de notre organisation à celle que nous avons créée
organization.telecom = [telecom]

# Maintenant, notre DT est achevé, nous avons une organisation d'objets qui est
# un objet FHIR R4 et qui contient toutes nos informations csv.

Après cela, notre mappage est terminé et notre DT fonctionne.
Maintenant, nous pouvons envoyer cette ressource FHIR R4 nouvellement créée à notre FhirClient qui est notre opération.

5.4. Opération d'un entreprise

Dans le fichier bo.py nous avons le FhirClient, ce client crée une connexion à un serveur fhir qui contiendra les informations recueillies par le csv.

Dans cet exemple, nous utilisons un serveur fhir local qui n'a pas besoin d'une clé api pour se connecter.
Pour s'y connecter, nous devons utiliser la fonction on_init,

if not hasattr(self,'url'):
    self.url = 'localhost:52773/fhir/r4'

self.client = SyncFHIRClient(url=self.url)

Maintenant, lorsque nous recevons un message/demande, nous pouvons, en trouvant le type de ressource de la ressource que nous envoyons avec notre demande au client, créer un objet lisible par le client, puis le sauvegarder sur le serveur fhir.

# Obtenez le type de ressource de la demande ( ici "Organisation" )
resource_type = request.resource["resource_type"]

# Créer une ressource de ce type en utilisant les données de la demande
resource = construct_fhir_element(resource_type, request.resource)

# Sauvegarder la ressource sur le serveur FHIR en utilisant le client
self.client.resource(resource_type,**json.loads(resource.json())).save()

Il est à noter que le client fhir fonctionne avec n'importe quelle ressource de FHIR R4 et pour utiliser et modifier notre exemple, nous devons seulement changer la DataTransformation et l'objet qui contient les informations csv.

5.5. Conclusion de la présentation

Si vous avez suivi ce parcours, vous savez maintenant exactement comment lire un csv d'une représentation d'une ressource FHIR R4, utiliser une DataTransformation pour le transformer en un véritable objet FHIR R4 et le sauvegarder sur un serveur.

6. Création d'une nouvelle DataTransformation

Ce référentiel est prêt à être codé en VSCode avec les plugins InterSystems. Ouvrez /src/python pour commencer à coder ou utiliser l'autocomplétion.

Étapes pour créer une nouvelle transformation
Pour ajouter une nouvelle transformation et l'utiliser, la seule chose que vous devez faire est d'ajouter votre csv nommé Patient.csv (par exemple) dans le dossier src/python/csv.
Ensuite, créez un objet dans src/python/obj.py appelé BasePatient qui met en correspondance votre csv.
Maintenant, créez une requête dans src/python/msg.py appelée PatientRequest qui a une variable resource de type BasePatient.
L'étape finale est la DataTransformation, pour cela, allez dans src/python/bp.py et ajoutez votre DT. Ajoutez d'abord if isinstance(request, PatientRequest): puis mappez votre ressource de requête à une fhir.resource Patient.
Maintenant si vous allez dans le portail de gestion et changez le paramètre du ServiceCSV pour ajouter filename=Patient.csv vous pouvez juste démarrer la réalisation et voir votre transformation se dérouler et votre client envoyer les informations au serveur.

Étapes détaillées pour créer une nouvelle transformation
Si vous n'êtes pas sûr de ce qu'il faut faire ou de la manière de le faire, voici une création étape par étape d'une nouvelle transformation :

Créez le fichier Patient.csv and le dossier src/python/csv pour les remplir avec le suivant:

family;given;system;value
FamilyName1;GivenName1;phone;555789675
FamilyName2;GivenName2;phone;023020202

Notre CSV contient un nom de famille, un prénom et un numéro de téléphone pour deux patients.


Dans src/python/obj.py écrivez :

@dataclass
class BasePatient:
    family:str = None
    given:str = None
    system:str = None
    value:str = None


Dans src/python/msg.py écrivez :

from obj import BasePatient
@dataclass
class PatientRequest(Message):
    resource:BasePatient = None


Dans src/python/bp.py écrivez :

from msg import PatientRequest
from fhir.resources.patient import Patient
from fhir.resources.humanname import HumanName

Dans src/python/bp.py dans la fonction on_request écrivez :

if isinstance(request,PatientRequest):
    base_patient = request.resource

    patient = Patient()

    name = HumanName()
    name.family = base_patient.family
    name.given = [base_patient.given]
    patient.name = [name]

    telecom = ContactPoint()
    telecom.value = base_patient.value
    telecom.system = base_patient.system
    patient.telecom = [telecom]

    msg = FhirRequest()
    msg.resource = patient

    self.send_request_sync("Python.FhirClient", msg)


Maintenant si vous allez dans le portail de gestion et changez le paramètre du ServiceCSV pour ajouter filename=Patient.csv vous pouvez juste arrêter et redémarrer la production et voir votre transformation se dérouler et votre client envoyer les informations au serveur.

Settings

7. Ce qu'il y a dans le référentiel

7.1. Dockerfile

Le dockerfile le plus simple pour démarrer un conteneur Python.
Utilisez docker build . pour construire et rouvrir votre fichier dans le conteneur pour travailler à l'intérieur de celui-ci.

7.2. .vscode/settings.json

Fichier de paramètres.

7.3. .vscode/launch.json

Fichier de configuration si vous voulez déboguer.

0
0 126