#Bonnes pratiques

0 Abonnés · 21 Publications

Recommandations sur les bonnes pratiques à suivre pour mieux développer, tester, déployer et gérer les solutions sur les plateformes de données d'InterSystems.. 

Article Sylvain Guilbaud · Mars 11, 2024 6m read

Définition de la variable d'environnement TZ sur Linux

La liste de contrôle de la mise à jour (Update Checklist) pour v2015.1 recommande de définir la variable d'environnement TZ sur les plates-formes Linux et renvoie à la page de manuel de tzset. Cette recommandation vise à améliorer les performances des fonctions de Cache liées à l'heure. Vous pouvez en savoir plus à ce sujet ici:

https://community.intersystems.com/post/linux-tz-environment-variable-not-being-set-and-impact-caché

La page de manuel de mon système de test CentOS 7 ( la même chose pour RHEL 6) indique ce qui suit:

“La fonction tzset() initialise la variable tzname à partir de la variable  d'environnement  TZ.   Cette fonction est automatiquement appelée par les autres fonctions de conversion de l'heure qui dépendent du fuseau horaire.”

Alors, comment définissez-vous TZ? Comment affecte-t-elle les horaires sur un serveur Linux? Voici ce que nous pouvons apprendre:

Le fuseau horaire du système --

Pour mon test, j'utilise Ensemble 2016.1 sur un système virtuel CentOS. Tout d'abord, vérifions le fuseau horaire du système. Pour cela, il faut utiliser l'utilitaire system-config-date.

Qu'en est-il de "L'horloge du système utilise l'UTC" ? Il s'agit de l'horloge matérielle du serveur. Sur un serveur dédié, l'UTC est très répandu. Lorsque Linux est utilisé dans une configuration à double démarrage avec Windows, ce n'est pas le cas (Windows utilise l'heure locale pour son horloge système).

Comme les configurations à double démarrage ne sont pas courantes pour les installations de Cache' et d'Ensemble, la question n'est pas abordée plus en détail. Les idées clés ici consistent à régler le fuseau horaire du système sur celui du serveur et à régler correctement l'heure de l'horloge matérielle.

 

L'heure et la date vues par les utilisateurs –

Jetons un coup d'œil. Voici un extrait de ma session de Terminal:

Tout semble en ordre. Mon processus shell (qui exécute la commande date) et mon processus Cache' affichent la même heure à l'exception des quelques secondes nécessaires à la saisie de la commande WRITE.

Définition de la variable TZ --

Maintenant, définissons TZ dans l'environnement. La commande à utiliser est tzselect. Voici un script de la sortie de la commande pour définir TZ

[ehemdal@localhost ~]$ tzselect

Veuillez indiquer un lieu afin que les règles relatives au fuseau horaire puissent être définies correctement.

Veuillez sélectionner un continent ou un océan.

  1. Afrique

  2. Amériques

  3. Antarctique

  4. Océan Arctique

  5. Asie

  6. Océan Atlantique

  7. Australie

  8. Europe

  9. Océan Indien

  10. Océan Pacifique

  11. aucun - Je veux spécifier le fuseau horaire en utilisant le format Posix TZ.

#? 2

Veuillez sélectionner un pays.

  1. Anguilla 19) République dominicaine 37) Pérou

  2. Antigua -et-Barbuda & 20) Équateur 38) Porto Rico

  3. Argentine 21) Salvador 39) Saint-Barthélemy

  4. Aruba 22) Guyane française 40) Saint-Christophe-et-Niévès;

  5. Bahamas 23) Groenland 41) Sainte-Lucie

  6. Barbade 24) Grenade 42) Saint-Martin (Néerlandais)

  7. Bélize 25) Guadeloupe 43) Saint-Martin (Français)

  8. Bolivie 26) Guatemala 44) Saint-Pierre-et-Miquelon;

  9. Brésil 27) Guyane 45) Saint Vincent

  10. Canada 28) Haïti 46) Suriname

  11. Caraïbes NL 29) Honduras 47) Trinité-et-Tobago;

  12. Îles Caïmans 30) Jamaïque 48) Îles Turques et Caïques Est

  13. Chili 31) Martinique 49) États-Unis

  14. Colombie 32) Mexique 50) Uruguay

  15. Costa Rica 33) Montserrat 51) Vénézuéla

  16. Cuba 34) Nicaragua 52) Îles Vierges britanniques

  17. Curaçao 35) Panama 53) Îles Vierges (États-Unis)

  18. Dominique 36) Paraguay

#? 49

Veuillez sélectionner l'une des régions de fuseau horaire suivantes.

  1. Est (la plupart des régions) 16) Centre-ND (Morton rural)

  2. Est-MI (la plupart des régions) 17) Centre-ND (Mercer)

  3. Eastern - KY (région de Louisville) 18) Montana (la plupart des régions)

  4. Est - KY (Wayne) 19) Montana - ID (sud); OR (est)

  5. Est - IN (la plupart des régions) 20) MST - Arizona (sauf Navajo)

  6. Est - IN (Da, Du, K, Mn) 21) Pacifique

  7. Est - IN (Pulaski) 22) Alaska (la plupart des régions)

  8. Est - IN (Crawford) 23) Alaska - région de Juneau

  9. Est - IN (Pike) 24) Alaska - région de Sitka

  10. Est - IN (Switzerland) 25) Alaska - Île d'Annette

  11. Centre (la plupart des régions) 26) Alaska - Yakutat

  12. Centre - IN (Perry) 27) Alaska (ouest)

  13. Centre - IN (Starke) 28) Îles Aléoutiennes

  14. Centre - MI (frontière du Wisconsin) 29) Hawaï

  15. Centre - ND (Oliver)

#? 1

Les informations suivantes ont été données:

  États-Unis

  Est (la plupart des régions)

Par conséquent, TZ='America/New_York' sera utilisé.

L'heure locale est maintenant: Mar 31 mai 11:21:04 EDT 2016.

Le temps universel est maintenant: Mar 31 mai 15:21: 04 UTC 2016.

Les informations ci-dessus sont-elles correctes?

  1. Oui

  2. Non

#? 1

Vous pouvez rendre cette modification permanente par vous-même en ajoutant la ligne

  TZ='America/New_York'; export TZ

dans le fichier '.profile' dans votre répertoire personnel, puis déconnectez-vous et reconnectez-vous.

Voici à nouveau la valeur de TZ, cette fois sur la sortie standard, afin que vous

puissiez utiliser la commande /usr/bin/tzselect dans des scripts shell:

America/New_York

[ehemdal@localhost ~]$

Le fichier ~/.profile (s'il existe) est exécuté lorsque vous vous connectez et définit la variable TZ pour vous. Vous pouvez utiliser un fichier d'initialisation différent si vous utilisez un shell autre que /bin/sh ou /bin/bash. J'ai défini cette variable et je me suis reconnecté. Si vous mettez à jour un fichier comme /etc/profile, vous pouvez appliquer cela à tous les utilisateurs.

Ici, vous pouvez voir que la TZ est définie pour mon utilisateur (ehemdal), mais PAS pour l'utilisateur root.

TZ et l'heure et la date vues par les utilisateurs –

Que se passe-t-il si un utilisateur se connecte à votre serveur depuis un autre fuseau horaire ? La variable TZ permet de conserver l'heure locale de l'utilisateur tout en laissant la gestion des fuseaux horaires et de l'heure d'été au système d'exploitation. Cela affecte également l'heure utilisée par Cache'. Par exemple, j'ai décidé de changer mon fuseau horaire pour celui d'Honolulu.

Voici deux captures d'écran qui montrent le résultat.

Le processus de mon utilisateur a défini le fuseau horaire sur Pacifique/Honolulu. Le processus root n'a pas défini de TZ (il utilise donc le fuseau horaire du système America/New_York). Au niveau du système d'exploitation (avec la commande date), l'affichage reflète l'heure locale pour les deux utilisateurs. La commande date reflète l'heure locale de l'utilisateur (HST pour l'utilisateur ehemdal et EDT pour root). Comme $HOROLOG obtient sa valeur à partir de l'heure du système d'exploitation disponible pour le processus utilisateur, les valeurs de $H sont DIFFÉRENTES pour les deux utilisateurs.

J'ai choisi l'heure d'Honolulu comme exemple intéressant car Hawaï n'observe pas l'heure d'été.  En paramétrant correctement TZ pour tous les utilisateurs, l'heure locale peut "avancer et reculer" pour les utilisateurs qui observent l'heure d'été, et rester stable pour ceux qui n'observent pas l'heure d'hiver.

1
0 490
Article Lorenzo Scalese · Oct 30, 2024 10m read

L'utilisation traditionnelle d'une production IRIS consiste, pour un adaptateur entrant, à recevoir des données d'une source externe, à envoyer ces données à un service IRIS, puis à faire en sorte que ce service envoie ces données par l'intermédiaire de la production.

Cependant, grâce à un adaptateur entrant personnalisé, nous pouvons faire en sorte qu'une production IRIS soit plus performante. Nous pouvons utiliser une production IRIS pour traiter les données de notre propre base de données sans aucun déclencheur externe.

BEn utilisant une production IRIS de cette manière, vos tâches de traitement des données peuvent désormais tirer parti de toutes les fonctionnalités intégrées d'une production IRIS, y compris:

  • Suivi et contrôle avancés
  • Traitement multithread pour l'évolutivité
  • Logique métier basée sur la configuration
  • Opérations IRIS intégrées pour se connecter rapidement à des systèmes externes
  • Récupération rapide des défaillances du système

La documentation pour créer un adaptateur entrant personnalisé peut être consultée à: https://docs.intersystems.com/hs20231/csp/docbook/DocBook.UI.Page.cls?KEY=EGDV_adv#EGDV_adv_adapterdev_inbound

Regardons 3 exemples d'une production simple configurée pour traiter des objets "Fish" à partir d'une base de données.

Dans le premier exemple, nous allons créer une production pilotée par les données qui traitera continuellement les données.

Dans le deuxième exemple, nous modifierons cette production pour traiter les données uniquement à des moments précis.

Dans le troisième exemple, nous modifierons cette production pour traiter les données uniquement lorsqu'elles sont déclenchées via une tâche système.

Exemple 1: Traitement continu des données

Cet exemple est une simple production configurée pour traiter continuellement des objets "Fish" à partir d'une base de données. Tout ce que fait la production est de rechercher continuellement de nouveaux objets fish, de convertir ces objets fish en JSON, puis de recracher ce JSON dans un fichier.

Tout d'abord, nous créons l'objet Fish que nous avons l'intention de traiter:

Class Sample.Fish Extends (%Persistent, Ens.Util.MessageBodyMethods, %JSON.Adaptor, %XML.Adaptor)
{

Parameter ENSPURGE As%Boolean = 0;Property Type As%String;Property Size As%Numeric;Property FirstName As%String;Property Status As%String [ InitialExpression = "Initialized" ];
Index StatusIndex On Status;
}

L'état est important car c'est ainsi que nous distinguerons l'état des objets fish non traités de l'état des objets traités.

En fixant ENSPURGE à 0 empêchera cet objet d'être purgé avec les en-têtes du message à l'avenir.

Deuxièmement, nous créons un adaptateur personnalisé pour rechercher les nouveaux objets fish:

Class Sample.Adapter.FishMonitorAdapter Extends Ens.InboundAdapter
{

/// La valeur d'état d'objet Fish sera demandée par l'adaptateur. Tous les objets Fish correspondants auront leur état défini par SetFishStatus et seront ensuite envoyés au service.Property GetFishStatus As%String [ InitialExpression = "Initialized", Required ];/// L'état d'objet Fish correspond à la valeur que le service attribue à l'objet Fish avant qu'il ne soit envoyé au service.Property SetFishStatus As%String [ InitialExpression = "Processed", Required ];Parameter SETTINGS = "GetFishStatus:Basic,SetFishStatus:Basic";Parameter SERVICEINPUTCLASS = "Sample.Fish";
Method OnTask() As%Status
{
	//Curseur pour rechercher les objets Fish correspondantsset getFishStatus = ..GetFishStatus
	&sql(declare fishCursor cursorforselectIDinto :fishId
		from Sample.Fish
		whereStatus = :getFishStatus)
	
	//Exécution du curseur
	&sql(open fishCursor)
	for {
		&sql(fetch fishCursor)
		quit:SQLCODE'=0//Changez l'état de chaque objet Fish correspondant et envoyez-le au service (BusinessHost).set fishObj = ##class(Sample.Fish).%OpenId(fishId)
		set fishObj.Status = ..SetFishStatus$$$ThrowOnError(fishObj.%Save())
		$$$ThrowOnError(..BusinessHost.ProcessInput(fishObj))
	}
	&sql(close fishCursor)
	if SQLCODE < 0 {
		throw##class(%Exception.SQL).CreateFromSQLCODE(SQLCODE,%msg)
	}
	
	quit$$$OK
}

La méthode OnTask() recherche tous les objet Fish correspondant à la valeur GetFishStatus configurée. Pour chaque objet Fish trouvé, elle modifie son état en fonction de la valeur SetFishStatus configurée, puis le transmet à la méthode ProcessInput du service.

Troisièmement, nous créons un service personnalisé pour utiliser cet adaptateur:

Class Sample.Service.FishMonitorService Extends Ens.BusinessService
{

/// Élément de configuration auquel les messages doivent être envoyésProperty TargetConfigName As Ens.DataType.ConfigName;Parameter SETTINGS = "TargetConfigName:Basic";Parameter ADAPTER = "Sample.Adapter.FishMonitorAdapter";
Method OnProcessInput(pInput As Sample.Fish, pOutput As%RegisteredObject) As%Status
{
    quit:..TargetConfigName=""//Envoyer l'objet Fish vers la cible configuréequit..SendRequestAsync(..TargetConfigName, pInput)
}

}

Ce service prend les objet Fish en entrée et les transmet via une requête asynchrone à la cible configurée.

Quatrièmement, nous créons un processus métier personnalisé pour convertir l'objet Fish en JSON.

Class Sample.Process.FishToJSONProcess Extends Ens.BusinessProcess
{

/// Élément de configuration auquel les messages doivent être envoyésProperty TargetConfigName As Ens.DataType.ConfigName;Parameter SETTINGS = "TargetConfigName:Basic";
Method OnRequest(pRequest As Sample.Fish, Output pResponse As Ens.Response) As%Status
{
	//Convertissez l'objet Fish en un flux JSONdo pRequest.%JSONExportToStream(.jsonFishStream)
	//Créez un nouveau conteneur de flux avec un flux JSONset tRequest = ##class(Ens.StreamContainer).%New(jsonFishStream)
	//Envoyez le conteneur de flux à la cible configuréequit..SendRequestAsync(..TargetConfigName, tRequest, 0)
}

Method OnResponse(request As Ens.Request, ByRef response As Ens.Response, callrequest As Ens.Request, callresponse As Ens.Response, pCompletionKey As%String) As%Status
{
    quit$$$OK
}

}

La méthode OnRequest() est la seule méthode qui agisse. Il accepte l'objet Fish, génère un flux JSON à partir de l'objet Fish, conditionne ce flux dans un conteneur Ens.StreamContainer, puis transmet ce conteneur de flux via une requête asynchrone à la cible configurée.

Enfin, nous configurons la production:

Class Sample.DataProduction Extends Ens.Production
{

XData ProductionDefinition
{
<Production Name="Sample.DataProduction" LogGeneralTraceEvents="false">
  <Description></Description>
  <ActorPoolSize>2</ActorPoolSize>
  <Item Name="Sample.Service.FishMonitorService" Category="" ClassName="Sample.Service.FishMonitorService" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
    <Setting Target="Host" Name="TargetConfigName">Sample.Process.FishToJSONProcess</Setting>
  </Item>
  <Item Name="Sample.Process.FishToJSONProcess" Category="" ClassName="Sample.Process.FishToJSONProcess" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
    <Setting Target="Host" Name="TargetConfigName">EnsLib.File.PassthroughOperation</Setting>
  </Item>
  <Item Name="EnsLib.File.PassthroughOperation" Category="" ClassName="EnsLib.File.PassthroughOperation" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
    <Setting Target="Adapter" Name="FilePath">C:\temp\fish\</Setting>
  </Item>
</Production>
}

}

Il ne reste plus qu'à la tester. Pour cela, il suffit d'ouvrir une fenêtre de terminal et de créer un nouvel objet Fish.

En regardant les messages de production, nous pouvons voir que l'objet Fish a été trouvé et transformé:

Nous pouvons inspecter la trace des deux messages:

En regardant le dossier de sortie (C:\temp\fish\), nous pouvons voir le fichier de sortie:

Exemple 2: Traitement des données basé sur les horaires

Pour les cas d'utilisation où nous ne voulons traiter les données qu'à des moments précis, comme la nuit, nous pouvons configurer le service pour qu'il s'exécute selon des horaires précis.

Pour modifier l'exemple 1 pour qu'il fonctionne selon des horaires, nous créons d'abord une spécification de l'horaire. La documentation sur la manière de procéder est disponible ici: https://docs.intersystems.com/iris20231/csp/docbook/DocBook.UI.PortalHelpPage.cls?KEY=Ensemble%2C%20Schedule%20Editor

Ensuite, nous modifions la configuration du service pour utiliser cet horaire:

Class Sample.DataProduction Extends Ens.Production
{

XData ProductionDefinition
{
<Production Name="Sample.DataProduction" LogGeneralTraceEvents="false">
  <Description></Description>
  <ActorPoolSize>2</ActorPoolSize>
  <Item Name="Sample.Service.FishMonitorService" Category="" ClassName="Sample.Service.FishMonitorService" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="@Midnight Processing">
    <Setting Target="Host" Name="TargetConfigName">Sample.Process.FishToJSONProcess</Setting>
  </Item>
  <Item Name="Sample.Process.FishToJSONProcess" Category="" ClassName="Sample.Process.FishToJSONProcess" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
    <Setting Target="Host" Name="TargetConfigName">EnsLib.File.PassthroughOperation</Setting>
  </Item>
  <Item Name="EnsLib.File.PassthroughOperation" Category="" ClassName="EnsLib.File.PassthroughOperation" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
    <Setting Target="Adapter" Name="FilePath">C:\temp\fish\</Setting>
  </Item>
</Production>
}

}

Maintenant, lorsque nous regardons cet onglet "Tâches" du service, nous voyons qu'il n'y a aucun tâche en cours:

Désormais, ce service n'aura plus que des tâches à exécuter entre minuit et 1 heure du matin.

Exemple 3: Traitement de données sur la base des événements avec le Gestionnaire de tâches

Pour les cas d'utilisation où nous ne voulons traiter les données qu'une seule fois à un moment précis ou lorsqu'un événement particulier a lieu, nous pouvons configurer le service pour qu'il ne s'exécute que lors de l'exécution d'une tâche système.

Pour modifier l'exemple 1 afin qu'il ne s'exécute que lorsqu'il est déclenché par une tâche, nous créons d'abord une tâche personnalisée pour déclencher le service.

Class Sample.Task.TriggerServiceTask Extends%SYS.Task.Definition
{

/// Le nom du service métier que cette tâche doit exécuter.Property BuinessServiceName As%String [ Required ];
Method OnTask() As%Status
{
	#dim pBusinessService As Ens.BusinessService
	$$$ThrowOnError(##class(Ens.Director).CreateBusinessService(..BuinessServiceName, .pBusinessService))
	Quit pBusinessService.OnTask()
}

}

Deuxièmement, nous configurons une nouvelle tâche système. La documentation sur la configuration des tâches système est disponible ici: https://docs.intersystems.com/iris20233/csp/docbook/Doc.View.cls?KEY=GSA_manage_taskmgr

Ceci est la partie personnalisée du processus de configuration pour cet exemple:

En outre, je configure la tâche pour qu'elle soit exécutée à la demande, mais vous pouvez aussi établir un horaire.

Enfin, nous configurons la production:

Class Sample.DataProduction Extends Ens.Production
{

XData ProductionDefinition
{
<Production Name="Sample.DataProduction" LogGeneralTraceEvents="false">
  <Description></Description>
  <ActorPoolSize>2</ActorPoolSize>
  <Item Name="Sample.Service.FishMonitorService" Category="" ClassName="Sample.Service.FishMonitorService" PoolSize="0" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
    <Setting Target="Host" Name="TargetConfigName">Sample.Process.FishToJSONProcess</Setting>
  </Item>
  <Item Name="Sample.Process.FishToJSONProcess" Category="" ClassName="Sample.Process.FishToJSONProcess" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
    <Setting Target="Host" Name="TargetConfigName">EnsLib.File.PassthroughOperation</Setting>
  </Item>
  <Item Name="EnsLib.File.PassthroughOperation" Category="" ClassName="EnsLib.File.PassthroughOperation" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
    <Setting Target="Adapter" Name="FilePath">C:\temp\fish\</Setting>
  </Item>
</Production>
}

}

Notez que nous avons fixé le PoolSize de Sample.Service.FishMonitorService à 0.

Il ne reste plus qu'à la tester. Pour cela, il suffit d'ouvrir une fenêtre de terminal et de créer un nouvel objet Fish.

En regardant les messages de production, nous pouvons voir que l'objet Fish n'a pas encore été transformé:

Ensuite, nous exécutons la tâche à la demande pour déclencher le service:

Maintenant, en regardant les messages de production, nous pouvons voir que le service a été déclenché, ce qui a permis de trouver et de traiter l'objet Fish:

Nous pouvons inspecter la trace des deux messages:

En regardant le dossier de sortie (C:\temp\fish\), nous pouvons voir le fichier de sortie:

Conclusion

Les exemples ci-dessus sont assez simples. Vous pouvez cependant configurer les productions pour en faire beaucoup plus. Y compris…

En fait, il est possible de réaliser ici tout ce qui peut être fait dans le cadre d'une production typique d'IRIS.

0
0 44
Article Sylvain Guilbaud · Oct 25, 2024 7m read

Dans le paysage actuel des données, les activités commerciales sont confrontées à différents défis. L'un d'entre eux consiste à réaliser des analyses à partir d'une couche de données unifiée et harmonisée, accessible à tous les utilisateurs. Une couche capable de fournir les mêmes réponses aux mêmes questions, indépendamment du dialecte ou de l'outil utilisé. La plate-forme de données InterSystems IRIS répond à cette question en ajoutant la solution 'Adaptive Analytics' (Analyse adaptative) qui peut fournir cette couche sémantique unifiée. Il y a beaucoup d'articles dans DevCommunity sur l'utilisation de cette couche sémantique via des outils décisionnels. Cet article couvrira la partie concernant la façon de l'utiliser avec l'IA et également la façon d'obtenir des informations en retour. Allons-y étape par étape...

Qu'est-ce que la solution 'Adaptive Analytics'?

Vous pouvez facilement trouver une définition sur le site web de la Communauté de développeurs. En quelques mots, elle peut fournir des données sous une forme structurée et harmonisée à divers outils de votre choix pour une utilisation et une analyse ultérieures. Elle fournit les mêmes structures de données à différents outils décisionnels. Mais... elle peut également fournir les mêmes structures de données à vos outils IA/ML!

Adaptive Analytics a un composant supplémentaire appelé AI-Link qui construit ce pont entre l'IA et d'informatique décisionnelle.

Qu'est-ce que AI-Link exactement?

Il s'agit d'un composant Python conçu pour permettre une interaction programmatique avec la couche sémantique dans le but de rationaliser les étapes clés du flux de travail de l'apprentissage automatique (ML) (par exemple, l'ingénierie des fonctionnalités).

Avec AI-Link, vous pouvez:

  • accéder de manière programmatique aux fonctionnalités de votre modèle de données analytiques;
  • faire des requêtes, explorer les dimensions et les mesures;
  • alimenter des pipelines de ML; ... et renvoyer les résultats vers votre couche sémantique pour qu'ils soient à nouveau utilisés par d'autres (par exemple, par le biais de Tableau ou d'Excel).

Comme il s'agit d'une bibliothèque Python, elle peut être utilisée dans n'importe quel environnement Python. Y compris les Notebooks. Dans cet article, je vais donner un exemple simple pour atteindre une solution d'analyse adaptative à partir d'un Notebook Jupyter avec l'aide d'AI-Link.

Voici le référentiel git qui aura le Notebook complet à titre d'exemple : https://github.com/v23ent/aa-hands-on

**Conditions préalables **

Les étapes suivantes supposent que vous ayez rempli les conditions préalables ci-dessous:

  1. La solution 'Adaptive Analytics' est en place et fonctionne (avec IRIS Data Platform en tant qu'entrepôt de données).
  2. Jupyter Notebook est opérationnel
  3. La connexion entre 1. et 2. peut être établie

Étape 1: Configuration

Tout d'abord, installons les composants nécessaires dans notre environnement. Ainsi, nous téléchargerons quelques paquets nécessaires au bon déroulement des étapes suivantes. 'atscale' - c'est notre paquetage principal pour se connecter 'prophet' - c'est le paquet dont nous aurons besoin pour faire des prédictions.

pip install atscale prophet

Ensuite, nous devons importer des classes clés représentant certains concepts clés de notre couche sémantique. Client - c'est la classe que nous utiliserons pour établir une connexion avec Adaptive Analytics; Project - c'est la classe qui représente les projets à l'intérieur d'Adaptive Analytics; DataModel - c'est la classe qui représentera notre cube virtuel;

from atscale.client import Client
from atscale.data_model import DataModel
from atscale.project import Project
from prophet import Prophet
import pandas as pd

Étape 2: Connexion

Maintenant, nous devrions être prêts à établir une connexion avec notre source de données.

client = Client(server='http://adaptive.analytics.server', username='sample')
client.connect()

Continuez et spécifiez les détails de connexion de votre instance d'Adaptive Analytics. Lorsque l'on vous demande l'organisation, répondez dans la boîte de dialogue et entrez votre mot de passe de l'instance AtScale.

Une fois la connexion établie, vous devrez sélectionner votre projet dans la liste des projets publiés au serveur. Vous obtiendrez la liste des projets sous la forme d'une invite interactive et la réponse devrait être l'identifiant entier du projet. Le modèle de données est ensuite sélectionné automatiquement s'il est le seul.

project = client.select_project()   
data_model = project.select_data_model()

Étape 3: Explorez votre jeu de données

Il existe un certain nombre de méthodes préparées par AtScale dans la bibliothèque de composants AI-Link. Elles permettent d'explorer votre catalogue de données, d'interroger les données et même d'ingérer des données en retour. La documentation d'AtScale a une référence API complète décrivant tout ce qui est disponible. Voyons d'abord quel est notre jeu de données en appelant quelques méthodes de data_model :

data_model.get_features()
data_model.get_all_categorical_feature_names()
data_model.get_all_numeric_feature_names()

Le résultat devrait ressembler à ceci image

Après avoir examiné un peu la situation, nous pouvons interroger les données qui nous intéressent à l'aide de la méthode 'get_data'. Elle renverra un pandas DataFrame contenant les résultats de la requête.

df = data_model.get_data(feature_list = ['Country','Region','m_AmountOfSale_sum'])
df = df.sort_values(by='m_AmountOfSale_sum')
df.head()

Ce qui affichera votre trame de données: image

Préparons un ensemble de données et affichons-le rapidement sur le graphique

import matplotlib.pyplot as plt

# Nous enregistrons des ventes pour chaque date
dataframe = data_model.get_data(feature_list = ['Date','m_AmountOfSale_sum'])

#  Création d'un graphique linéaire
plt.plot(dataframe['Date'], dataframe['m_AmountOfSale_sum'])

# Ajout des étiquettes et d'un titre
plt.xlabel('Days')
plt.ylabel('Sales')
plt.title('Daily Sales Data')

# Affichage du graphique
plt.show()

Résultat: image

Étape 4: Prédiction

La prochaine étape consistera à tirer profit du pont AI-Link - faisons quelques prédictions simples!

# Chargement des données historiques pour l'entraînement du modèle
data_train = data_model.get_data(
    feature_list = ['Date','m_AmountOfSale_sum'],
    filter_less = {'Date':'2021-01-01'}
    )
data_test = data_model.get_data(
    feature_list = ['Date','m_AmountOfSale_sum'],
    filter_greater = {'Date':'2021-01-01'}
    )

Nous disposons ici de 2 jeux de données différents: pour entraîner notre modèle et pour le tester.

# Pour l'outil que nous avons choisi pour faire la prédiction 'Prophète' ,nous devrons spécifier 2 colonnes: 'ds' et 'y'
data_train['ds'] = pd.to_datetime(data_train['Date'])
data_train.rename(columns={'m_AmountOfSale_sum': 'y'}, inplace=True)
data_test['ds'] = pd.to_datetime(data_test['Date'])
data_test.rename(columns={'m_AmountOfSale_sum': 'y'}, inplace=True)

# Initialisation et ajustement du modèle Prophet
model = Prophet()
model.fit(data_train)

Et puis nous créons une autre trame de données pour accueillir notre prédiction et l'afficher sur le graphique

# Création d'une trame de données prochaine pour la prévision
future = pd.DataFrame()
future['ds'] = pd.date_range(start='2021-01-01', end='2021-12-31', freq='D')

# Prédictions
forecast = model.predict(future)
fig = model.plot(forecast)
fig.show()

Résultat: image

Étape 5: Réécriture

Une fois notre prédiction en place, nous pouvons la renvoyer à l'entrepôt de données et ajouter un agrégat à notre modèle sémantique afin de la refléter pour d'autres utilisateurs. La prédiction serait disponible via n'importe quel autre outil décisionnel pour les analystes décisionnels et les utilisateurs commerciaux. La prédiction elle-même sera placée dans notre entrepôt de données et y sera stockée.

from atscale.db.connections import Iris
db = Iris(
    username,
    host,
    namespace,
    driver,
    schema, 
    port=1972,
    password=None, 
    warehouse_id=None
    )

data_model.writeback(dbconn=db,
                    table_name= 'SalesPrediction',
                    DataFrame = forecast)

data_model.create_aggregate_feature(dataset_name='SalesPrediction',
                                    column_name='SalesForecasted',
                                    name='sum_sales_forecasted',
                                    aggregation_type='SUM')

Fin

C'est ça! Bonne chance avec vos prédictions!

0
0 86
Article Ben Spead · Oct 24, 2024 15m read

Vous ne le réalisez peut-être pas, mais votre compte de connexion InterSystems peut être utilisé pour accéder à un très large éventail de services InterSystems pour vous aider à apprendre et à utiliser Intersystems IRIS et d'autres technologies InterSystems plus efficacement.  Poursuivez votre lecture pour en savoir plus sur la manière de découvrir de nouvelles connaissances techniques et de nouveaux outils grâce à votre compte de connexion InterSystems.  Après votre lecture, veuillez participer au sondage en bas de page, afin que nous puissions voir dans quelle mesure cet article vous a été utile!

Qu'est-ce qu'un compte de connexion InterSystems? 

Le compte de connexion InterSystems est utilisé pour accéder à divers services en ligne destinés aux clients potentiels, aux partenaires et aux utilisateurs d'InterSystems.  Il s'agit d'un ensemble unique d'informations d'identification utilisées dans plus de 15 applications externes.  Certaines applications (comme WRC ou iService) nécessitent une activation spécifique pour que l'accès soit accordé par le compte.  Il est probable qu'il existe des ressources qui vous aideront mais dont vous ne connaissiez pas l'existence - assurez-vous de lire toutes les options et d'essayer un nouvel outil pour vous aider à améliorer votre niveau technique!!

Catalogue d'Applications

Vous pouvez consulter tous les services disponibles avec votre compte de connexion InterSystems en visitant le Catalogue d'applications d'InterSystems, situé à l'adresse suivante:  https://Login.InterSystems.com.  Ce catalogue ne répertorie que les applications ou services auxquels vous avez actuellement l'accès.  Il se souvient de vos applications les plus fréquemment utilisées et les place en tête de liste pour vous faciliter la tâche. 

N'oubliez pas de marquer la page d'un signet pour accéder facilement à tous ces outils dans la boîte à outils de votre compte de connexion InterSystems!

Détails de l'application 

Il est maintenant temps d'entrer dans les détails des applications individuelles et de voir comment elles peuvent vous aider en tant que développeur travaillant avec les technologies d'InterSystems!  Lisez la suite et essayez de trouver une nouvelle application à utiliser pour la première fois afin d'améliorer votre efficacité et vos compétences en tant que développeur....

 Getting Started (Pour Commencer) - gettingstarted.intersystems.com 

Audience

  • Toute personne souhaitant explorer l'utilisation de la plateforme de données InterSystems IRIS®

Description

  • Avec InterSystems IRIS, vous apprendrez à développer rapidement des applications critiques à forte intensité de données.
  • Les vidéos et les tutoriels permettent de travailler en utilisant SQL, Java, C#/.Net, Node.js, Python ou InterSystems ObjectScript.
  • Pour travailler sur les tutoriels, utilisez un bac à sable gratuit, basé sur le cloud et intégré au navigateur : IRIS+IDE+Web Terminal. 

Comment cela aide à améliorer votre niveau technique

  • Vous vous orientez rapidement vers la technologie d'InterSystems et vous la voyez en action avec des exemples et du code de travail réels!
  • Vous pouvez explorer l'utilisation d'autres langages de programmation populaires avec InterSystems IRIS.

 Apprentissage en ligne - learning.intersystems.com

Audience

  • Tous les utilisateurs actuels et potentiels qui utilisent les solutions d'InterSystems 

Description

  • Des documents autodidactes pour développer et prendre en charge les applications les plus importantes au monde:
    • Exercices pratiques
    • Vidéos
    • Cours en Ligne
    • Parcours d'apprentissage

Comment cela aide à améliorer votre niveau technique

  • Apprenez, apprenez, apprenez!! 
  • Rien ne vous permettra de devenir un développeur plus efficace plus rapidement que de suivre un formateur technique compétent qui vous guidera à travers de nouveaux concepts à utiliser dans vos projets InterSystems IRIS! 

 Documentation - docs.intersystems.com 

Audience

  • Tous les utilisateurs actuels et potentiels qui utilisent les solutions d'InterSystems

Description

  • Documentation pour toutes les versions de nos produits
  • Liens vers la documentation externe si nécessaire
  • Tout le contenu récent est alimenté par notre nouveau moteur de recherche.
  • La page de recherche vous permet de filtrer par produit, version, et autres facettes.
  • Certaines documentations nécessitent une autorisation (via le compte de connexion InterSystems):
    • Les documents AtScale sont disponibles pour les utilisateurs d'Adaptive Analytics
    • Les documents HealthShare sont disponibles pour les utilisateurs de HealthShare
  • Assurez-vous d'utiliser la nouvelle dynamique de liste de contrôle de l'impact des mises à niveau Upgrade Impact Checklist dans le serveur Docs!

Comment cela aide à améliorer votre niveau technique

  • Utilisation rapide du matériel de référence en classe et la documentation de l'API.
  • Recherche de l'exemple de code. 
  • Il est possible de lire une documentation d'utilisation détaillée pour les sections d'InterSystems IRIS dans lesquelles vous souhaitez approfondir vos connaissances.
  • Il est possible de demander des précisions supplémentaires ou de signaler des problèmes directement à partir des pages de documentation grâce à la fonctionnalité "Feedback".

 Évaluation - evaluation.intersystems.com

Audience

  • Les utilisateurs qui souhaitent télécharger des logiciels ou des licences d'InterSystems à des fins d'évaluation ou de développement

Description

  • Téléchargement d'InterSystems IRIS et d'InterSystems IRIS for Health.
  • Tout le monde peut télécharger des kits d'Édition communautaire.
  • Les utilisateurs existants peuvent également demander une licence puissante pour évaluer les fonctionnalités d'entreprise.
  • Des versions préliminaires sont disponibles avant la publication.
  • Les paquets du programme d'accès anticipé permettent de fournir un retour d'information sur les futurs produits et fonctionnalités.

Comment cela aide à améliorer votre niveau technique

  • Il est possible d'essayer les versions d'aperçu des logiciels pour voir comment les nouvelles fonctionnalités peuvent aider à accélérer votre développement.
  • Il est possible de tester les fonctionnalités d'Enterprise en demandant une licence d'évaluation.
  • Vous pouvez vous assurer que tous les développeurs de votre organisation ont la dernière version d'InterSystems IRIS installée sur leurs machines.
  • Vous pouvez fournir des commentaires à InterSystems Product Management sur les fonctionnalités Early Access afin de vous assurer qu'elles répondront aux besoins de votre équipe une fois qu'elles seront entièrement disponibles.

 Communauté de développeurs - community.intersystems.com

Audience

  • Tous ceux qui travaillent avec la technologie d'InterSystems (employés, utilisateurs, partenaires et clients potentiels d'InterSystems)

Description

  • Suivi des annonces relatives aux produits et services d'InterSystems.
  • Recherche d'articles sur une variété de sujets techniques.
  • Questions et réponses de la part de la communauté.
  • Découverte des offres d'emploi ou des développeurs disponibles à l'embauche.
  • Participation à des concours dotés de prix en espèces d'une valeur de 1 000 dollars.
  • Toujours être au courant de ce qui se passe chez InterSystems!

Comment cela aide à améliorer votre niveau technique

  • Grâce à l'accès aux principaux experts mondiaux de la technologie InterSystems, vous pouvez apprendre auprès des meilleurs et rester engagé face aux questions, tendances et sujets les plus brûlants.
  • Réception automatique dans votre boîte de réception de mises à jour sur les nouveaux produits, les nouvelles versions et les opportunités du Programme d'accès anticipé.
  • Vous pouvez obtenir l'aide de vos collègues pour répondre à vos questions et surmonter les obstacles.
  • Vous pouvez avoir des discussions enrichissantes avec les chefs de produits et les développeurs de produits d'InterSystems - apprenez à la source!
  • Vous pouvez améliorer vos compétences en partageant des solutions techniques et du code et en profitant du retour d'information de vos collègues.

 Idées InterSystems - ideas.intersystems.com

Audience

  • Tous ceux qui souhaitent partager des idées pour améliorer la technologie d'InterSystems.

Description

  • Publication d'idées sur la façon d'améliorer la technologie InterSystems.
  • Découverte des commentaires existants et attribution d'un vote positif ou participation à des discussions.
  • InterSystems prendra en compte les idées les plus populaires pour les futures feuilles de route des produits.

Comment cela aide à améliorer votre niveau technique

  • Vous pouvez voir vos idées et vos besoins transformés en réalité dans les produits d'InterSystems ou dans les bibliothèques open source.
  • Vous pouvez vous familiariser avec les idées de vos collègues et apprendre à utiliser les produits d'InterSystems d'une nouvelle manière.
  • Il est possible de mettre en œuvre des idées suggérées par d'autres, d'explorer de nouvelles parties de la technologie d'InterSystems.

 Les Masters Mondiaux - globalmasters.intersystems.com

Audience

  • Tous ceux qui souhaitent promouvoir la technologie InterSystems et obtenir des badges et des goodies

Description

  • Plate-forme de gamification conçue pour permettre aux développeurs d'apprendre, de rester à jour et d'obtenir la reconnaissance de leurs contributions par le biais d'un contenu interactif.
  • Des points et des badges sont attribués aux utilisateurs pour:
    • Engagement auprès de la Communauté de développeurs
    • Engagement auprès d' Open Exchange
    • Publication de messages sur les médias sociaux concernant les produits et les technologies d'InterSystems
  • Des points peuvent être échangés contre des goodies InterSystems ou de la formation gratuite

Comment cela aide à améliorer votre niveau technique

  • Les défis attirent votre attention sur des articles ou des vidéos que vous avez peut-être manqués sur la communauté de développeurs, le site d'apprentissage ou la chaîne YouTube - vous apprendrez sans cesse de nouvelles choses à appliquer à vos projets!

 Open Exchange - openexchange.intersystems.com 

Audience

  • Les développeurs qui cherchent à publier ou à utiliser des progiciels et des outils réutilisables

Description

  • Outils et progiciels pour développeurs conçus avec les plates-formes de données et les produits d'InterSystems. 
  • Les progiciels sont publiés sous diverses licences logicielles (la plupart en open source).
  • Intégration avec GitHub pour la gestion des versions des progiciels, les discussions et la détection des bogues.
  • Il est possible de lire et de soumettre des commentaires et de trouver les progiciels les plus populaires.
  • Les développeurs peuvent soumettre des problèmes et apporter des améliorations aux progiciels via les demandes d'extraction de GitHub pour  aider à faire avancer les logiciels de la communauté.
  • Les développeurs peuvent voir les statistiques de trafic et de téléchargements des progiciels  publiés par eux

Comment cela aide à améliorer votre niveau technique

  • Pas besoin de réinventer la roue!  Utilisez les progiciels open source créés et maintenus par la Communauté de développeurs d'InterSystems pour résoudre des problèmes génériques, ce qui vous permet de vous concentrer sur le développement de solutions spécifiques à votre secteur d'activité.
  • La contribution à des progiciels open source est un excellent moyen de recevoir des commentaires constructifs sur votre travail et d'affiner vos modèles de développement.
  • En devenant un contributeur respecté à des projets open source, il est tout à fait possible de voir la demande augmenter pour vos compétences et vos connaissances. 

 WRC - wrc.intersystems.com

Audience

  • Système de suivi de tous les problèmes signalés par les utilisateurs sur InterSystems IRIS et InterSystems HealthShare.  Les utilisateurs disposant de SUTA peuvent travailler directement avec l'application.

Description

  • Application "Worldwide Response Center" (Centre de réponse mondial, alias "WRC Direct”).
  • Système de suivi des problèmes signalés par les utilisateurs. 
  • Ouverture de nouvelles demandes. 
  • Affichage de toutes les actions d'investigation et ajout de renseignements et de commentaires sur une demande. 
  • Affichage des informations statistiques sur l'historique de vos appels d'assistance. 
  • Clôture des demandes et fourniture d'un retour d'information sur le processus d'assistance. 
  • Examen des fichiers correctifs ad hoc. 
  • Suivi des demandes de modification de logiciel.
  • Téléchargement des versions actuelles du produit et du logiciel client.

Comment cela aide à améliorer votre niveau technique

  • Les techniciens d'InterSystems peuvent vous aider à surmonter les obstacles techniques que vous avez en ce qui concerne le développement ou la gestion des systèmes avec les produits InterSystems.
  • Rapport de bogues pour s'assurer que les problèmes sont corrigés dans des versions ultérieures.  

 iService - iservice.intersystems.com

Audience

  • Les utilisateurs nécessitant une assistance dans le cadre d'un contrat SLA

Description

  • Plateforme de billetterie d'assistance pour nos  utilisateurs des secteurs de la santé, du cloud et les utilisateurs hébergés.
  • Elle permet de calculer la conformité des contrats de niveau de service (SLA) et d'établir des rapports en fonction de règles.
  • La plateforme fournit des fonctionnalités avancées de recherche et d'exportation de facettes. 
  • Elle intègre un système complet de gestion de la sécurité clinique.

Comment cela aide à améliorer votre niveau technique

  • Les techniciens d'InterSystems peuvent vous aider à surmonter les obstacles techniques que vous avez en ce qui concerne le développement ou la gestion des systèmes avec les produits InterSystems pour la santé ou le cloud.
  • Rapport de bogues pour s'assurer que les problèmes sont corrigés dans des versions ultérieures.  

 ICR - containers.intersystems.com

Audience

  • Tous ceux qui souhaitent utiliser les conteneurs InterSystems

Description

  • Registre des conteneurs InterSystems
  • Registre de conteneurs accessible programmé et interface web pour la navigation.
  • Conteneurs de l'édition communautaire sont accessibles à tous.
  • Versions commerciales d'InterSystems IRIS et d'InterSystems IRIS for Health disponibles pour les utilisateurs soutenus.
  • Génération des jetons à utiliser dans les pipelines CICD pour récupérer automatiquement les conteneurs.

Comment cela aide à améliorer votre niveau technique

  • Il est possible d'augmenter la maturité de votre SDLC en passant à des pipelines CICD basés sur des conteneurs pour votre développement, vos tests et votre déploiement!

 Répertoire de partenaires - partner.intersystems.com 

Audience

  • Tous ceux qui cherchent à trouver un partenaire InterSystems ou un produit partenaire 
  • Partenaires cherchant à faire la publicité de leurs logiciels et services  

Description

  • Recherche de tous types de partenaires InterSystems:
    • Partenaires de mise en œuvre
    • Partenaires de solutions
    • Partenaires technologiques
    • Partenaire cloud
  • Les partenaires existants peuvent gérer leurs listes de services et de logiciels. 

Comment cela aide à améliorer votre niveau technique

  • Il est possible de faire appel à des experts certifiés sur une base contractuelle pour apprendre d'eux dans le cadre de vos projets.
  • Vous pouvez acquérir des licences pour des solutions d'entreprise basées sur la technologie InterSystems, ce qui vous évite d'avoir à tout construire à partir de zéro.
  • Vous pouvez apporter vos produits et services à un public plus large, augmentant ainsi la demande et vous obligeant à accroître votre capacité de livraison!

 CCR - ccr.intersystems.com 

Audience

  • Organisations sélectionnées gérant les changements apportés à une implémentation d'InterSystems (employés, partenaires et utilisateurs finaux)

Description

  • Enregistrement de contrôle des modifications
    • Application de flux de travail personnalisée construite sur notre propre technologie pour suivre toutes les personnalisations apportées aux produits de santé InterSystems installés dans le monde entier.
  • Versionnement et déploiement du code personnalisé sur site et des modifications de configuration.
  • Plusieurs niveaux et options de configuration du flux de travail.
  • Adaptation très souple aux besoins spécifiques de la phase du projet

Comment cela aide à améliorer votre niveau technique

  • Pour les équipes autorisées à l'utiliser, il est possible de trouver et de réutiliser du code ou des plans de mise en œuvre au sein de votre organisation, ce qui évite de devoir résoudre le même problème plusieurs fois.
  • Résolution beaucoup plus rapide des problèmes en production, ce qui laisse plus de temps pour le travail de développement. 

 Connexion Client - client.intersystems.com  

Audience

  • Disponible pour tous les clients de TrakCare

Description

  • InterSystems Client Connection est une plateforme de collaboration et de partage des connaissances pour les clients de TrakCare.
  • Cette communauté en ligne permet aux clients de TrakCare d'établir des relations plus nombreuses, plus efficaces et plus étroites.
  • Sur la plateforme de connexion client "Client Connection", les éléments suivants sont disponibles: 
    • Des nouvelles et des événements concernant TrakCare 
    • Des documents de lancement de TrakCare, par exemple des documents de lancement et des vidéos de prévisualisation
    • L'accès aux guides de produits les plus récents.
    • Des supports permettant d'approfondir les connaissances personnelles.
    • Des forums de discussion pour tirer parti de l'expertise des pairs. 

Comment cela aide à améliorer votre niveau technique

  • Les spécialistes techniques et d'application des sites TrakCare peuvent partager rapidement leurs questions et leurs connaissances, en se connectant à d'autres utilisateurs dans le monde entier.  Des réponses plus rapides signifient plus de temps pour élaborer des solutions!

 Commande en ligne - store.intersystems.com

Audience

  • Utilisateurs des opérations chez les partenaires d'application sélectionnés/utilisateurs finaux

Description

  • Possibilité pour les utilisateurs de choisir différents produits en fonction de leurs contrats et de créer de nouvelles commandes.  
  • Possibilité pour les utilisateurs de mettre à niveau ou d'échanger des commandes existantes.
  • Soumission des commandes à InterSystems Customer Operations pour process les traiter en vue de la livraison et de la facturation.
  • Possibilité pour les utilisateurs de migrer les licences existantes vers InterSystems IRIS.

Comment cela aide à améliorer votre niveau technique

  • Honnêtement, en aucune façon!  Il s'agit d'un outil utilisé par le personnel des opérations et non par les utilisateurs techniques, mais il est listé ici par souci d'exhaustivité puisque l'accès est contrôlé via le compte de connexion InterSystems ;)  

Autres choses à savoir sur votre compte de connexion InterSystems

Voici quelques autres informations utiles sur les comptes de connexion InterSystems...

Comment créer un compte de connexion

Les utilisateurs peuvent créer leur propre compte en cliquant sur "Créer un compte" sur n'importe quelle application publique d'InterSystems, y compris:

Par ailleurs, le FRC (First Response Center) d'InterSystems créera un compte de connexion pour les utilisateurs supportés la première fois qu'ils auront besoin d'accéder au Worldwide Response Center (WRC) ou à iService (ou les utilisateurs supportés peuvent également créer des comptes pour leurs collègues).

Avant d'utiliser un compte, l'utilisateur doit accepter les conditions générales, soit au cours de la procédure d'auto-enregistrement, soit lors de la première connexion.

Autres options de connexion

Certaines applications permettent de se connecter avec Google ou GitHub:

Il s'agit du même compte de connexion InterSystems, mais avec une authentification par Google ou GitHub.

Profil du compte

Si vous allez à https://Login.InterSystems.com et que vous vous authentifiez, vous pourrez accéder à la rubrique Options > Profil et apporter des modifications de base à votre compte.  L'adresse électronique peut être modifiée via Options > Change Email.  

Résolution des problèmes liés aux comptes de connexion

Les problèmes liés aux comptes de connexion InterSystems doivent être adressés à Support@InterSystems.com.  Veuillez inclure:

  • Nom d'utilisateur utilisé pour le login tenté
  • Adresse électronique
  • Type et version du navigateur
  • Messages d'erreur spécifiques et/ou captures d'écran
  • L'heure et la date à laquelle l'erreur a été reçue   
0
0 64
Article Guillaume Rongier · Oct 6, 2024 21m read

Cela fait maintenant plus de 2 ans que j'utilise quotidiennement Embedded Python. Il est peut-être temps de partager un retour d'expérience sur ce parcours.

Pourquoi écrire ce commentaire de retour d'expérience? Parce que, je suppose, je suis comme la plupart de mes collègues ici, un développeur ObjectScript, et je pense que la communauté bénéficierait de ce retour d'expérience et pourrait mieux comprendre les avantages et les inconvénients du choix de Embedded Python pour développer quelque chose dans IRIS. Et aussi éviter certains pièges.

image

Introduction

Je suis développeur depuis 2010, et j'ai travaillé avec ObjectScript depuis 2013.

Donc, c'est à peu près 10 ans d'expérience avec ObjectScript.

Depuis 2021, et la sortie de Embedded Python dans IRIS, je me suis lancé un défi :

  • Apprendre Python
  • Faire autant que possible tout ce qui est en Python.

Quand j'ai commencé ce parcours, je n'avais aucune idée de ce qu'était Python. J'ai donc commencé par les bases, et je continue d'apprendre chaque jour.

Débuter avec Python

L'avantage de Python est sa facilité d'apprentissage. C'est encore plus facile quand on connaît déjà ObjectScript.

Pourquoi ? Ils ont beaucoup de choses en commun.

ObjectScriptPython
Non typéNon typé
Langage de scriptLangage de script
Orienté objetOrienté objet
InterprétéInterprété
Intégration simple du CIntégration simple du C

Donc, si vous connaissez ObjectScript, vous en savez déjà beaucoup sur Python.

Mais il y a quelques différences, et certaines d'entre elles ne sont pas faciles à comprendre.

Python n'est pas ObjectScript

Mais il y a quelques différences, et certaines d'entre elles ne sont pas faciles à comprendre.

Pour moi il y a principalement 3 différences :

  • Pep8
  • Modules
  • Dunders

Pep8

Mais qu'est-ce que Pep8 ?

Il s'agit d'un ensemble de règles pour écrire du code Python.

pep8.org

Quelques-unes d'entre elles sont :

  • convention de nommage
  • noms de variables
    • snake_case
  • noms de classes
    • CamelCase
  • indentation
  • longueur de ligne
  • etc.

Pourquoi est-ce important ?

Parce que c'est la façon d'écrire du code Python. Et si vous ne suivez pas ces règles, vous aurez du mal à lire le code écrit par d'autres personnes, et elles auront du mal à lire le vôtre.

En tant que développeurs ObjectScript, nous avons aussi quelques règles à suivre, mais elles ne sont pas aussi strictes que Pep8.

J'ai appris Pep8 à la dure.

Je voudrais vous raconter petite histoire, je suis ingénieur commercial chez InterSystems et je fais beaucoup de démonstrations. Et un jour, que je faisais une démo de Embedded Python à un client, et ce client était un développeur Python, la conversation a tourné court lorsqu'il a vu mon code. Il m'a dit que mon code n'était pas du tout Python (il avait raison), je codais en Python comme je codais en ObjectScript. Et à cause de cela, il m'a dit qu'il n'était plus intéressé par Embedded Python. J'ai été choqué, et j'ai décidé d'apprendre Python de la bonne manière.

Donc, si vous voulez apprendre Python, apprenez d'abord Pep8.

Modules

Les modules sont quelque chose que nous n'avons pas en ObjectScript.

Habituellement, dans les langages orientés objet, vous avez des classes et des paquetages. En Python, vous avez des classes, des paquetages et des modules.

Qu'est-ce qu'un module ?

C'est un fichier avec une extension .py. Et c'est la façon d'organiser votre code.

Vous n'avez pas compris? Moi non plus au début. Prenons un exemple.

Habituellement, quand on veut créer une classe en ObjectScript, on crée un fichier .cls, et on y met sa classe. Et si vous voulez créer une autre classe, vous créez un autre fichier .cls. Et si vous voulez créer un paquetage, vous créez un dossier et vous y placez vos fichiers .cls.

En Python, c'est la même chose, mais Python apporte la possibilité d'avoir plusieurs classes dans un seul fichier. Ce fichier s'appelle un module. Pour information, c'est Pythonic quand il y a plusieurs classes dans un seul fichier.

Prévoyez donc comment vous allez organiser votre code, et comment vous allez nommer vos modules pour ne pas vous retrouver comme moi avec un tas de modules portant le même nom que vos classes.

Un mauvais exemple :

MyClass.py

class MyClass:
    def __init__(self):
        pass

    def my_method(self):
        pass

Pour instancier cette classe, vous allez faire :

import MyClass.MyClass # weird right ?

my_class = MyClass()

Bizarre, hein ?

Dunders

Les dunders sont des méthodes spéciales en Python. Elles sont appelées dunder parce qu'elles commencent et se terminent par un double soulignement.

Ce sont en quelque sorte nos méthodes % en ObjectScript.

Elles sont utilisées pour ce qui suit :

  • constructeur
  • surcharge d'opérateur
  • représentation d'objet
  • etc.

Exemple :

class MyClass:
    def __init__(self):
        pass

    def __repr__(self):
        return "MyClass"

    def __add__(self, other):
        return self + other

Ici, nous avons 3 méthodes de dunder :

  • __init__ : constructeur
  • __repr__ : représentation d'objet
  • __add__ : surcharge d'opérateur

Les méthodes Dunders sont partout en Python. C'est une partie importante de la langue, mais ne vous inquiétez pas, vous les apprendrez rapidement.

Conclusion

Python n'est pas ObjectScript, et vous devrez l'apprendre. Mais ce n'est pas si difficile, et vous l'apprendrez rapidement. Gardez simplement à l'esprit qu'il vous faudra apprendre Pep8, et comment organiser votre code avec des modules et des méthodes dunder.

De bons sites pour apprendre Python :


Embedded Python

Maintenant que vous en savez un peu plus sur Python, parlons de Embedded Python.

Qu'est-ce que Embedded Python ?

Embedded Python est un moyen d'exécuter du code Python dans IRIS. C'est une nouvelle fonctionnalité d' IRIS 2021.2+. Cela signifie que votre code Python sera exécuté dans le même processus qu'IRIS. Par ailleurs, chaque classe ObjectScript est une classe Python, de même pour les méthodes et les attributs et vice versa. 🥳 C'est génial !

Comment utiliser Embedded Python ?

Il y a 3 façons principales d'utiliser Embedded Python :

  • Utilisation la balise de langue dans ObjectScript
    • Méthode Foo() As %String [ Language = python ]
  • Utilisation de la fonction ##class(%SYS.Python).Import()
  • Utilisation de l'interpréteur python
    • python3 -c "import iris; print(iris.system.Version.GetVersion())"

Mais si vous voulez vous intéresser sérieusement à Embedded Python, vous aurez à éviter d'utiliser la balise de langue.

image

Pourquoi ?

  • Parce que ce n'est pas Pythonic
  • Parce que ce n'est pas ObjectScript non plus
  • Parce que vous n'avez pas de débogueur
  • Parce que vous n'avez pas de linter
  • Parce que vous n'avez pas de formateur
  • Parce que vous n'avez pas de cadre de test
  • Parce que vous n'avez pas de gestionnaire de paquets
  • Parce que vous mélangez 2 langues dans le même fichier
  • Parce que lorsque votre processus plante, vous n'avez pas de trace de pile
  • Parce que vous ne pouvez pas utiliser d'environnements virtuels ou d'environnements conda
  • ...

Ne vous méprenez pas, ça marche, ça peut être utile, si vous voulez tester quelque chose rapidement, mais à mon avis, ce n'est pas une bonne pratique.

Alors, qu'est-ce que j'ai appris de ces 2 années de Embedded Python, et comment l'utiliser de la bonne manière ?

Comment j'utilise Embedded Python

Je crois que vous avez deux options :

  • Utiliser les bibliothèques Python comme s'il s'agissait de classes ObjectScript
    • withqvec ##class(%SYS.Python).Import() function
  • Utiliser une première approche en python

Utilisation des bibliothèques et le code Python comme s'il s'agissait de classes ObjectScript

Vous voulez toujours utiliser Python dans votre code ObjectScript, mais vous ne voulez pas utiliser la balise de langue. Alors, que pouvez-vous faire ?

"Tout simplement" utilisez les bibliothèques et le code Python comme s'il s'agissait de classes ObjectScript. Prenons un exemplee :

Vous voulez utiliser la bibliothèque 'requests' ( c'est une bibliothèque pour faire des requêtes HTTP ) dans votre code ObjectScript.

Avec la balise de langue

ClassMethod Get() As %Status [ Language = python ]
{
	import requests

	url = "https://httpbin.org/get"
	# faire une requête d'obtention
	response = requests.get(url)
	# récupérer les données json de la réponse
	data = response.json()
	# itérer sur les données et imprimer les paires clé-valeur
	for key, value in data.items():
		print(key, ":", value)
}

Pourquoi je pense que ce n'est pas une bonne idée ?

Parce que vous mélangez 2 langues dans le même fichier, et que vous n'avez pas de débogueur, de linter, de formateur, etc. Si ce code plante, vous aurez du mal à le déboguer. Vous n'avez pas de trace de pile, et vous ne savez pas d'où vient l'erreur. Et vous n'avez pas d'auto-complétion.

Sans de balise de langue

ClassMethod Get() As %Status
{
	set status = $$$OK
    set url = "https://httpbin.org/get"
    // Importation du module Python "requests" en tant que classe ObjectScript
    set request = ##class(%SYS.Python).Import("requests")
    // Appel de la méthode get de la classe de requête
    set response = request.get(url)
    // Appel de la méthode json de la classe de réponse
	set data = response.json()
    // Ici, les données sont un dictionnaire Python
    // Pour parcourir un dictionnaire Python, vous devez utiliser la méthode dunder et items()
	// Importation du module Embedded Python
	set builtins = ##class(%SYS.Python).Import("builtins")
    // Ici, nous utilisons len du module intégré pour obtenir la longueur du dictionnaire
    For i = 0:1:builtins.len(data)-1 {
        // Maintenant, nous convertissons les éléments du dictionnaire en une liste, et nous obtenons la clé et la valeur en utilisant la méthode dunder __getitem__
		Write builtins.list(data.items())."__getitem__"(i)."__getitem__"(0),": ",builtins.list(data.items())."__getitem__"(i)."__getitem__"(1),!
	}
	quit status
}

Pourquoi je pense que c'est une bonne idée ?

Parce que vous utilisez Python comme s'il s'agissait d'ObjectScript. Vous importez la bibliothèque de requêtes comme une classe ObjectScript et vous l'utilisez comme une classe ObjectScript. Toute la logique est en ObjectScript, et vous utilisez Python comme une bibliothèque. Même pour la maintenance, c'est plus facile à lire et à comprendre, n'importe quel développeur ObjectScript peut comprendre ce code. L'inconvénient est que vous avez à savoir comment utiliser les méthodes de duners, et comment utiliser Python comme s'il s'agissait d'ObjectScript.

Conclusion

Croyez-moi, de cette manière vous obtiendrez un code plus robuste, et vous pourrez le déboguer facilement. Au début, cela semble difficile, mais vous découvrirez les avantages de l'apprentissage de Python plus rapidement que vous ne le pensez.

Utilisation de première approche en python

C'est la façon dont je préfère utiliser Embedded Python.

J'ai construit beaucoup d'outils en utilisant cette approche, et j'en suis très satisfait.

Quelques exemples :

Qu'est-ce qu'une première approche python ?

TIl n'y a qu'une seule règle : Le code Python doit être dans des fichiers .py, le code ObjectScript doit être dans des fichiers .cls

Comment y parvenir ?

L'idée est de créer des classes de wrappers ObjectScript pour appeler le code Python.


Prenons l'exemple de iris-fhir-python-strategy :

Exemple : iris-fhir-python-strategy

Tout d'abord, nous avons à comprendre comment fonctionne le serveur IRIS FHIR.

Chaque serveur IRIS FHIR met en œuvre une Stratégie.

Une Stratégie est un ensemble de deux classes :

SuperclassParamètres de sous-classe
HS.FHIRServer.API.InteractionsStrategyStrategyKey — Spécifie un identifiant unique pour la stratégie InteractionsStrategy.
InteractionsClass — Spécifie le nom de votre sous-classe Interactions.
HS.FHIRServer.API.RepoManagerStrategyClass — Spécifie le nom de votre sous-classe InteractionsStrategy.
StrategyKey — Spécifie un identifiant unique pour la stratégie InteractionsStrategy. Ceci doit correspondre au paramètre StrategyKey dans la sous-classe InteractionsStrategy.

Ces deux classes sont des classes abstraites Abtract Abstract.

  • HS.FHIRServer.API.InteractionsStrategy est une classe Abstract qui doit être mise en œuvre pour personnaliser le comportement du serveur FHIR.
  • HS.FHIRServer.API.RepoManager est une classe Abstract qui doit être mise en œuvre pour personnaliser le stockage du serveur FHIR.

Remarques

Pour notre exemple, nous nous concentrerons uniquement sur la classe HS.FHIRServer.API.InteractionsStrategy même si la classe HS.FHIRServer.API.RepoManager est également implémentée et obligatoire pour personnaliser le serveur FHIR. La classe HS.FHIRServer.API.RepoManager est mise en œuvre par HS.FHIRServer.Storage.Json.RepoManager qui est la mise en œuvre par défaut du serveur FHIR.

Où trouver le code

Tout le code source peut être trouvé dans le référentiel : iris-fhir-python-strategy Le dossier src contient les dossiers suivants :

  • python : contient le code python
  • cls : contient le code ObjectScript utilisé pour appeler le code python

Comment mettre en œuvre une Stratégie

Dans cette démonstration de faisabilité, nous nous intéresserons uniquement à la manière d'implémenter une Strategie en Python, et non à la manière de mettre en œuvre un RepoManager.

Pour mettre en œuvre une Strategie vous devez créer au moins deux classes :

  • Une classe qui hérite de la classe HS.FHIRServer.API.InteractionsStrategy
  • Une classe qui hérite de la classe HS.FHIRServer.API.Interactions

Mise en œuvre d'InteractionsStrategy

La classe HS.FHIRServer.API.InteractionsStrategy vise à personnaliser le comportement du serveur FHIR en remplaçant les méthodes suivantes :

  • GetMetadataResource : appelé pour récupérer les métadonnées du serveur FHIR
    • C'est la seule méthode que nous remplacerons dans cette preuve de concept

HS.FHIRServer.API.InteractionsStrategy a également deux paramètres :

  • StrategyKey : un identifiant unique pour la stratégie InteractionsStrategy
  • InteractionsClass : le nom de votre sous-classe Interactions

Mise en œuvre des Interactions

La classe HS.FHIRServer.API.Interactions vise à personnaliser le comportement du serveur FHIR en remplaçant les méthodes suivantes :

  • OnBeforeRequest : appelée avant l'envoi de la requête au serveur
  • OnAfterRequest : appelée après l'envoi de la requête au serveur
  • PostProcessRead : appelée une fois l'opération de lecture terminée
  • PostProcessSearch : appelée une fois l'opération de recherche terminée
  • Read : appelée pour lire une ressource
  • Add : appelée pour ajouter une ressource
  • Update : appelée pour mettre à jour une ressource
  • Delete : appelée pour supprimer une ressource
  • et bien d'autres...

Nous mettons en œuvre la classe HS.FHIRServer.API.Interactions dans la classe src/cls/FHIR/Python/Interactions.cls.

 
Spoiler
Class FHIR.Python.Interactions Extends (HS.FHIRServer.Storage.Json.Interactions, FHIR.Python.Helper)
{

Parameter OAuth2TokenHandlerClass As%String = "FHIR.Python.OAuth2Token";

Method %OnNew(pStrategy As HS.FHIRServer.Storage.Json.InteractionsStrategy) As%Status { // %OnNew est appelé lors de la création de l'objet.// Le paramètre pStrategy est l'objet de stratégie qui a créé cet objet.// La mise en œuvre par défaut ne fait rien// D'abord, le chemin d'accès à python est défini à partir d'une variable d'environnementset..PythonPath = $system.Util.GetEnviron("INTERACTION_PATH") // Définissez ensuite le nom de la classe python à partir de la variable d'environnementset..PythonClassname = $system.Util.GetEnviron("INTERACTION_CLASS") // Puis définissez le nom du module python à partir de la variable d'environnementset..PythonModule = $system.Util.GetEnviron("INTERACTION_MODULE")

<span class="hljs-keyword">if</span> (<span class="hljs-built_in">..PythonPath</span> = <span class="hljs-string">""</span>) || (<span class="hljs-built_in">..PythonClassname</span> = <span class="hljs-string">""</span>) || (<span class="hljs-built_in">..PythonModule</span> = <span class="hljs-string">""</span>) {
	<span class="hljs-comment">//quit ##super(pStrategy)</span>
	<span class="hljs-keyword">set</span> <span class="hljs-built_in">..PythonPath</span> = <span class="hljs-string">"/irisdev/app/src/python/"</span>
	<span class="hljs-keyword">set</span> <span class="hljs-built_in">..PythonClassname</span> = <span class="hljs-string">"CustomInteraction"</span>
	<span class="hljs-keyword">set</span> <span class="hljs-built_in">..PythonModule</span> = <span class="hljs-string">"custom"</span>
}


<span class="hljs-comment">// Définissez ensuite la classe python</span>
<span class="hljs-keyword">do</span> <span class="hljs-built_in">..SetPythonPath</span>(<span class="hljs-built_in">..PythonPath</span>)
<span class="hljs-keyword">set</span> <span class="hljs-built_in">..PythonClass</span> = <span class="hljs-keyword">##class</span>(FHIR.Python.Interactions).GetPythonInstance(<span class="hljs-built_in">..PythonModule</span>, <span class="hljs-built_in">..PythonClassname</span>)

<span class="hljs-keyword">quit</span> <span class="hljs-keyword">##super</span>(pStrategy)

}

Method OnBeforeRequest( pFHIRService As HS.FHIRServer.API.Service, pFHIRRequest As HS.FHIRServer.API.Data.Request, pTimeout As%Integer) { // OnBeforeRequest est appelée avant le traitement de chaque demande.if$ISOBJECT(..PythonClass) { set body = ##class(%SYS.Python).None() if pFHIRRequest.Json '= "" { set jsonLib = ##class(%SYS.Python).Import("json") set body = jsonLib.loads(pFHIRRequest.Json.%ToJSON()) } do..PythonClass."on_before_request"(pFHIRService, pFHIRRequest, body, pTimeout) } }

Method OnAfterRequest( pFHIRService As HS.FHIRServer.API.Service, pFHIRRequest As HS.FHIRServer.API.Data.Request, pFHIRResponse As HS.FHIRServer.API.Data.Response) { // OnAfterRequest est appelée après le traitement de chaque demande.if$ISOBJECT(..PythonClass) { set body = ##class(%SYS.Python).None() if pFHIRResponse.Json '= "" { set jsonLib = ##class(%SYS.Python).Import("json") set body = jsonLib.loads(pFHIRResponse.Json.%ToJSON()) } do..PythonClass."on_after_request"(pFHIRService, pFHIRRequest, pFHIRResponse, body) } }

Method PostProcessRead(pResourceObject As%DynamicObject) As%Boolean { // PostProcessRead est appelée après la lecture d'une ressource dans la base de données.// Renvoyez 1 pour indiquer que la ressource doit être incluse dans la réponse.// Renvoyez 0 pour indiquer que la ressource doit être exclue de la réponse.if$ISOBJECT(..PythonClass) { if pResourceObject '= "" { set jsonLib = ##class(%SYS.Python).Import("json") set body = jsonLib.loads(pResourceObject.%ToJSON()) } return..PythonClass."post_process_read"(body) } quit1 }

Method PostProcessSearch( pRS As HS.FHIRServer.Util.SearchResult, pResourceType As%String) As%Status { // PostProcessSearch est appelée après l'exécution d'une recherche.// Renvoyez $$$OK pour indiquer que la recherche a abouti.// Renvoyez un code d'erreur pour indiquer que la recherche a échoué.if$ISOBJECT(..PythonClass) { return..PythonClass."post_process_search"(pRS, pResourceType) } quit$$$OK }

Method Read( pResourceType As%String, pResourceId As%String, pVersionId As%String = "") As%DynamicObject { return##super(pResourceType, pResourceId, pVersionId) }

Method Add( pResourceObj As%DynamicObject, pResourceIdToAssign As%String = "", pHttpMethod = "POST") As%String { return##super(pResourceObj, pResourceIdToAssign, pHttpMethod) }

/// Renvoie de VersionId pour la version "supprimée" Method Delete( pResourceType As%String, pResourceId As%String) As%String { return##super(pResourceType, pResourceId) }

Method Update(pResourceObj As%DynamicObject) As%String { return##super(pResourceObj) }

}

La classe FHIR.Python.Interactions hérite de la classe HS.FHIRServer.Storage.Json.Interactions et de la classe FHIR.Python.Helper

La classe HS.FHIRServer.Storage.Json.Interactions est la mise en œuvre par défaut du serveur FHIR.

La classe FHIR.Python.Helper vise à aider à appeler du code Python à partir d'ObjectScript.

La classe FHIR.Python.Interactions remplacent les méthodes suivantes :

  • %OnNew : appelée lors de la création de l'objet
    • nous utilisons cette méthode pour définir le chemin python, le nom de la classe python et le nom du module python à partir des variables d'environnement
    • si les variables d'environnement ne sont pas définies, nous utilisons les valeurs par défaut
    • nous définissons également la classe python
    • nous appelons la méthode %OnNew de la classe parente
Method %OnNew(pStrategy As HS.FHIRServer.Storage.Json.InteractionsStrategy) As %Status
{
	// Définissez d'abord le chemin python à partir d'une variable d'environnement
	set ..PythonPath = $system.Util.GetEnviron("INTERACTION_PATH")
	// Puis définissez le nom de la classe python à partir de la variable d'environnement
	set ..PythonClassname = $system.Util.GetEnviron("INTERACTION_CLASS")
	// Puis définissez le nom du module python à partir de la variable d'environnement
	set ..PythonModule = $system.Util.GetEnviron("INTERACTION_MODULE")

	if (..PythonPath = "") || (..PythonClassname = "") || (..PythonModule = "") {
		// utilisez les valeurs par défaut
		set ..PythonPath = "/irisdev/app/src/python/"
		set ..PythonClassname = "CustomInteraction"
		set ..PythonModule = "custom"
	}

	// Ensuite, définissez la classe python
	do ..SetPythonPath(..PythonPath)
	set ..PythonClass = ..GetPythonInstance(..PythonModule, ..PythonClassname)

	quit ##super(pStrategy)
}
  • OnBeforeRequest : appelée avant l'envoi de la requête au serveur
    • nous appelons la méthode on_before_request de la classe python
    • nous passons l'objet HS.FHIRServer.API.Service, l'objet HS.FHIRServer.API.Data.Request, le corps de la requête et le timeout
Method OnBeforeRequest(
	pFHIRService As HS.FHIRServer.API.Service,
	pFHIRRequest As HS.FHIRServer.API.Data.Request,
	pTimeout As %Integer)
{
	// OnBeforeRequest est appelée avant le traitement de chaque requête.
	if $ISOBJECT(..PythonClass) {
		set body = ##class(%SYS.Python).None()
		if pFHIRRequest.Json '= "" {
			set jsonLib = ##class(%SYS.Python).Import("json")
			set body = jsonLib.loads(pFHIRRequest.Json.%ToJSON())
		}
		do ..PythonClass."on_before_request"(pFHIRService, pFHIRRequest, body, pTimeout)
	}
}
  • OnAfterRequest : appelée après l'envoi de la requête au serveur
    • nous appelons la méthode on_after_request de la classe python
    • nous passons l'objet HS.FHIRServer.API.Service, l'objet HS.FHIRServer.API.Data.Request, l'objet HS.FHIRServer.API.Data.Response et le corps de la réponse
Method OnAfterRequest(
	pFHIRService As HS.FHIRServer.API.Service,
	pFHIRRequest As HS.FHIRServer.API.Data.Request,
	pFHIRResponse As HS.FHIRServer.API.Data.Response)
{
	// OnAfterRequest est appelée après le traitement de chaque requête.
	if $ISOBJECT(..PythonClass) {
		set body = ##class(%SYS.Python).None()
		if pFHIRResponse.Json '= "" {
			set jsonLib = ##class(%SYS.Python).Import("json")
			set body = jsonLib.loads(pFHIRResponse.Json.%ToJSON())
		}
		do ..PythonClass."on_after_request"(pFHIRService, pFHIRRequest, pFHIRResponse, body)
	}
}
  • Et ainsi de suite...

Interactions en Python

La classeFHIR.Python.Interactions appelle les méthodes on_before_request, on_after_request, ... de la classe python.

Voici la classe python abstraite :

import abc
import iris

class Interaction(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def on_before_request(self, 
                          fhir_service:'iris.HS.FHIRServer.API.Service',
                          fhir_request:'iris.HS.FHIRServer.API.Data.Request',
                          body:dict,
                          timeout:int):
        """
        on_before_request is called before the request is sent to the server.
        param fhir_service: the fhir service object iris.HS.FHIRServer.API.Service
        param fhir_request: the fhir request object iris.FHIRServer.API.Data.Request
        param timeout: the timeout in seconds
        return: None
        """
        

    @abc.abstractmethod
    def on_after_request(self,
                         fhir_service:'iris.HS.FHIRServer.API.Service',
                         fhir_request:'iris.HS.FHIRServer.API.Data.Request',
                         fhir_response:'iris.HS.FHIRServer.API.Data.Response',
                         body:dict):
        """
        on_after_request is called after the request is sent to the server.
        param fhir_service: the fhir service object iris.HS.FHIRServer.API.Service
        param fhir_request: the fhir request object iris.FHIRServer.API.Data.Request
        param fhir_response: the fhir response object iris.FHIRServer.API.Data.Response
        return: None
        """
        

    @abc.abstractmethod
    def post_process_read(self,
                          fhir_object:dict) -> bool:
        """
        post_process_read is called after the read operation is done.
        param fhir_object: the fhir object
        return: True the resource should be returned to the client, False otherwise
        """
        

    @abc.abstractmethod
    def post_process_search(self,
                            rs:'iris.HS.FHIRServer.Util.SearchResult',
                            resource_type:str):
        """
        post_process_search is called after the search operation is done.
        param rs: the search result iris.HS.FHIRServer.Util.SearchResult
        param resource_type: the resource type
        return: None
        """

Mise en œuvre de la classe abstraite python

from FhirInteraction import Interaction

class CustomInteraction(Interaction):

    def on_before_request(self, fhir_service, fhir_request, body, timeout):
        #Extract the user and roles for this request
        #so consent can be evaluated.
        self.requesting_user = fhir_request.Username
        self.requesting_roles = fhir_request.Roles

    def on_after_request(self, fhir_service, fhir_request, fhir_response, body):
        #Clear the user and roles between requests.
        self.requesting_user = ""
        self.requesting_roles = ""

    def post_process_read(self, fhir_object):
        #Evaluate consent based on the resource and user/roles.
        #Returning 0 indicates this resource shouldn't be displayed - a 404 Not Found
        #will be returned to the user.
        return self.consent(fhir_object['resourceType'],
                        self.requesting_user,
                        self.requesting_roles)

    def post_process_search(self, rs, resource_type):
        #Iterate through each resource in the search set and evaluate
        #consent based on the resource and user/roles.
        #Each row marked as deleted and saved will be excluded from the Bundle.
        rs._SetIterator(0)
        while rs._Next():
            if not self.consent(rs.ResourceType,
                            self.requesting_user,
                            self.requesting_roles):
                #Mark the row as deleted and save it.
                rs.MarkAsDeleted()
                rs._SaveRow()

    def consent(self, resource_type, user, roles):
        #Example consent logic - only allow users with the role '%All' to see
        #Observation resources.
        if resource_type == 'Observation':
            if '%All' in roles:
                return True
            else:
                return False
        else:
            return True

Trop long, faisons un résumé

La classeFHIR.Python.Interactions est un wrapper pour appeler la classe python.

Les classes abstraites IRIS sont implémentées pour envelopper les classes abstraites python 🥳.

Cela nous aide à séparer le code python et le code ObjectScript et à bénéficier ainsi du meilleur des deux mondes.

0
0 69
Article Iryna Mykhailova · Fév 27, 2024 4m read

Salut les gars,

Il y a quelques jours, un client m'a contacté avec le souhait d'améliorer son application existante, qui utilise les services SOAP afin de partager la même autorisation avec sa nouvelle API d'application basée sur REST. Comme leur nouvelle application utilise OAuth2, le défi était clair : comment transmettre le token d'accès avec la requête SOAP au serveur.

Après avoir passé du temps sur Google, il s'est avéré que l'un des moyens possibles d'y parvenir consistait à ajouter un élément d'en-tête supplémentaire à l'enveloppe SOAP, puis à s'assurer que l'implémentation du WebService fait ce qui est nécessaire pour valider le jeton d'accèsю

0
0 133
InterSystems officiel Sylvain Guilbaud · Jan 22, 2024 2m read

Pour votre commodité, InterSystems publie les étapes d'installation typiques pour les systèmes d'exploitation pris en charge par InterSystems IRIS.

Pour Microsoft Windows, veuillez consulter la documentation.

Le programme d'installation d'IRIS détectera si un serveur Web est installé sur la même machine, ce qui vous donne la possibilité de configurer automatiquement le serveur Web.

Toutes les installations Apache nécessiteront une autorisation sudo (recommandé) ou root pour installer le serveur Web. Cette exigence prend en charge les meilleures pratiques recommandées.

0
0 89
Article Guillaume Rongier · Déc 11, 2023 10m read

Dans cette série d'articles, j'aimerais présenter et discuter de plusieurs approches possibles pour le développement de logiciels avec les technologies d'InterSystems et GitLab. J'aborderai des sujets tels que:

  • Git 101
  • Flux Git (processus de développement)
  • Installation de GitLab
  • Flux de travail GitLab
  • Diffusion continue
  • Installation et configuration de GitLab
  • GitLab CI/CD
  • Pourquoi des conteneurs?
  • Infrastructure de conteneurs
  • CD utilisant des conteneurs

Dans le premier article, nous avons évoqué les notions de base de Git, les raisons pour lesquelles une compréhension approfondie des concepts de Git est importante pour le développement de logiciels modernes et la manière dont Git peut être utilisé pour développer des logiciels.

Dans le deuxième article, nous avons évoqué le flux de travail GitLab - un processus complet du cycle de vie du logiciel ainsi que Diffusion continue.

Dans le troisième article, nous avons évoqué l'installation et la configuration de GitLab et la connexion de vos environnements à GitLab

Dans le quatrième article, nous avons écrit une configuration de CD.

Dans le cinquième article, nous avons parlé des conteneurs et de la manière dont ils peuvent être utilisés (et pour quelles raisons).

Dans le sixème article nous abordons des principaux composants dont vous aurez besoin pour exécuter un pipeline de diffusion continue avec des conteneurs et de la façon dont ils fonctionnent tous ensemble.

Dans cet article, nous allons créer une configuration de diffusion continue décrite dans les articles précédents.

0
0 88
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 Guillaume Rongier · Nov 23, 2023 9m read

Dans cette série d'articles, j'aimerais présenter et discuter de plusieurs approches possibles pour le développement de logiciels avec les technologies d'InterSystems et GitLab. J'aborderai des sujets tels que:

  • Git 101
  • Le flux Git (processus de développement)
  • Installation de GitLab
  • Flux de travail GitLab
  • Diffusion continue
  • Installation et configuration de GitLab
  • GitLab CI/CD

Dans l'article précédent, nous avons évoqué les notions de base de Git, les raisons pour lesquelles une compréhension approfondie des concepts de Git est importante pour le développement de logiciels modernes et la manière dont Git peut être utilisé pour développer des logiciels. Et bien que nous nous soyons concentrés sur la partie mise en œuvre du développement de logiciels, cette partie présente :

  • Le flux de travail GitLab est un processus complet du cycle de vie d'un logiciel, allant de l'idée au retour utilisateur
  • Diffusion continue est une approche d'ingénierie logicielle dans laquelle les équipes produisent des logiciels en cycles courts, garantissant que le logiciel peut être diffusé de manière fiable à tout moment. Elle vise à créer, tester et publier des logiciels plus rapidement et plus fréquemment.
0
0 157
Article Guillaume Rongier · Nov 20, 2023 7m read

Tout le monde dispose d'un environnement de test.

Certains ont la chance de disposer d'un environnement totalement séparé pour la production.

-- Inconnu

.

Dans cette série d'articles, j'aimerais présenter et discuter de plusieurs approches possibles pour le développement de logiciels avec les technologies d'InterSystems et GitLab. J'aborderai des sujets tels que:

  • Git 101
  • Git flow (processus de développement)
  • Installation de GitLab
  • Flux de travail GitLab WorkFlow
  • GitLab CI/CD
  • CI/CD avec des conteneurs

Cette première partie traite de la pierre angulaire du développement logiciel moderne - le système de contrôle de version Git et divers flux Git.

0
0 175
Article Iryna Mykhailova · Juil 19, 2022 9m read

Pour parler des différentes bases de données et des différents modèles de données qui existent, on doit premièrement comprendre ce qui est une base de données et comment les utiliser.

Une base de données est une collection organisée de données stockées et accessibles par voie électronique. Elle permet de stocker et de retrouver des données structurées, semi-structurées ou des données brutes souvent en rapport avec un thème ou une activité.

Au cœur de chaque base de données se trouve au moins un modèle utilisé pour décrire ses données. Et selon le modèle sur lequel elle est basée, elle peut avoir des caractéristiques un peu différentes et stocker différents types de données.

Pour inscrire, retrouver, modifier, trier, transformer ou imprimer les informations de la base de données on utilise un logiciel qui s’appelle système de gestion de base de données (SGBD, en anglais DBMS pour Database management system).

La taille, les capacités et les performances des bases de données et de leurs SGBD respectifs ont augmenté de plusieurs ordres de grandeur. Ces augmentations de performances ont été rendues possibles par les progrès technologiques dans différents domaines, tels que les domaines des processeurs, de la mémoire informatique, du stockage informatique et des réseaux informatiques. Le développement ultérieur de la technologie des bases de données peut être divisé en quatre générations basées sur le modèle ou la structure des données : navigation, relationnel, objet et post-relationnel.

4
0 642
Article Iryna Mykhailova · Août 7, 2023 3m read
   _________ ___ ____  
  |__  /  _ \_ _|  _ \
    / /| |_) | || |_) |
   / /_|  __/| ||  __/
  /____|_|  |___|_|    

À partir de la version 2021.1, InterSystems IRIS a commencé à fonctionner avec l'exécution python dans le noyau du moteur. Cependant, il n'y avait aucun moyen d'installer des paquets à partir de l'instance. Le principal attrait de Python est son énorme écosystème de paquets. C'est dans cette optique que je vous présente mon projet secondaire zpip, un wrapper pip qui peut être appelé depuis le terminal iris.

Qu'est-ce que zpip ?

zpip est un wrapper pour python pip qui permet aux développeurs d'ajouter rapidement des paquets à une instance via le terminal InterSystems IRIS.

Fonctionnalités

  • python pip wrapper pour InterSystems IRIS
  • Installation et désinstallation des paquets python
  • Ajout du mot-clé zpip à la langue lors de l'installation

Installation de zpip

%SYS> zpm "install zpip"

TODO: Liste des tâches à accomplir

  • API appelable avec retour des états

Utiliisation de zpip

Les commandes pip* sont toutes supportées, cependant, toute commande interactive nécessite l'utilisation de la version non-interactive de la commande. Par exemple, pour désinstaller un paquet, vous devrez utiliser -y dans la commande pour confirmer le processus.

Installation de paquets python avec zpip

// Installation de plusieurs paquets
// belles bibliothèques en demande
%SYS> zpip "install requests bs4"

... en action:

%SYS>zpip "install emoji"

Processing /home/irisowner/.cache/pip/wheels/ae/80/43/3b56e58669d65ea9ebf38b9574074ca248143b61f45e114a6b/emoji-2.1.0-py3-none-any.whl
Installing collected packages: emoji
Successfully installed emoji-2.1.0

%SYS>

Spécification d'un répertoire d'installation différent :

// Installation vers une autre cible de paquetage python
$SYS> zpip "install --target '/durable/iconfig/lib/python' emoji"

Désinstallation d'un paquet python

// Nécessite -y!
%SYS>zpip "uninstall -y emoji"
Found existing installation: emoji 2.1.0
Uninstalling emoji-2.1.0:
  Successfully uninstalled emoji-2.1.0

Autres commandes pip utiles

liste des paquets

// Liste des paquets
%SYS> zpip "list"
Paquet                      Version    
---------------------------- -----------
absl-py                      1.1.0      
argon2-cffi                  21.3.0     
argon2-cffi-bindings         21.2.0     
asttokens                    2.0.5      
astunparse                   1.6.3      
attrs                        21.4.0     
backcall                     0.2.0      
beautifulsoup4               4.11.1     
bleach                       5.0.0      
bs4                          0.0.1   
...

Limitations

  • ILes commandes interactives ne sont pas prises en charge
    • Utilisation de -y pour les désinstallations
  • La recherche peut ne pas fonctionner en fonction de la configuration du système
  • Utilisation de l'infrastructure pip du système d'exploitation sous-jacent, de sorte que votre installation dépend de la version pip du système d'exploitation.
0
0 65
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 Evgeny Shvarov · Mars 8, 2023 7m read

Il s'agit d'un modèle de base pour un environnement de développement permettant de travailler avec ObjectScript dans InterSystems IRIS. Il vous aide à éditer, compiler, commettre/pousser, déboguer et tester votre code ObjectScript. Il aide également à conditionner votre application en tant que module installable avec IPM. Le modèle est compatible avec Embedded Python.

Description

Ce référentiel fournit un environnement de développement prêt à l'emploi pour coder de manière productive avec ObjectScript d'InterSystems. Ce modèle:

0
0 64
Article Irène Mykhailova · Fév 3, 2023 2m read

Il n'y a pas si longtemps, GitHub a introduit la possibilité d'exécuter très rapidement VSCode dans le navigateur pour tout dépôt hébergé ici. Appuyez sur la touche . sur n'importe quel dépôt ou pull request, ou remplacez .com par .dev dans l'URL, pour aller directement à un environnement VS Code dans votre navigateur.

github dev

Ce VSCode est une version légère de la version Desktop mais fonctionne entièrement dans le Browser. Et en raison de cela, il a une limitation pour les extensions qui ont été autorisés à fonctionner de cette façon. Permettez-moi de vous présenter la nouvelle version 1.2.1 de l'extension VSCode-ObjectScript qui prend désormais en charge le fonctionnement en mode Browser.

Ce que vous devrez faire, c'est ouvrir n'importe quel référentiel dans ce nouvel environnement, aller dans les extensions, chercher et installer l'extension VSCode-ObjectScript. Une fois installée, elle sera disponible pour n'importe quel référentiel, que vous ouvrirez ensuite.

Il s'agit de la même extension mais elle n'a que des fonctionnalités de base pour le moment. Juste la syntaxe est mise en évidence. Il est certain qu'elle n'a pas accès à l'exécution d'IRIS, mais certaines fonctionnalités supplémentaires, qui pourraient être disponibles hors connexion, pourraient apparaître à l'avenir.

Sans GitHub

Il y a une autre option, si vous souhaitez exécuter VSCode dans le navigateur, mais sans être lié à un quelconque dépôt GitHub, et juste ouvrir une partie de votre dossier local. C'est https://vscode.dev. L'extension VSCode-ObjectScript y est également disponible.

0
0 83
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 Guillaume Rongier · Oct 24, 2022 10m read

Swift-FHIR-Iris

Application iOS pour exporter les données HealthKit vers InterSystems IRIS for Health (ou n'importe quel référentiel FHIR)

main

Table des matières

Objectif de cette démo

L'objectif est de créer une démonstration de bout en bout du protocole FHIR.

Ce que j'entends par de bout en bout, à partir d'une source d'information telle qu'un iPhone. Collectez vos données de santé au format Apple (HealthKit), transformez-les en FHIR, puis envoyez-les au référentiel IRIS for Health d'InterSystems.

Ces informations doivent être accessibles via une interface web.

TL;DR: iPhone -> InterSystems FHIR -> Page Web.

Comment lancer cette démo

Préalables

  • Pour la partie client (iOS)
    • Xcode 12
  • Pour le serveur et l'application Web
    • Docker

Installation de Xcode

Pas grand chose à dire ici, ouvrez l'AppStore, cherchez Xcode, installez.

Ouvrir le projet SwiftUi

Swift est la language de programmation d'Apple pour iOS, Mac, Apple TV et Apple Watch. Elle est le remplaçant d'objective-C.

Double-cliquez sur Swift-FHIR-Iris.xcodeproj.

Ouvrez le simulateur par un clic sur la flèche en haut à gauche.

xcode

Configuration du simulateur

Accéder à Santé

Cliquez sur Étapes

Ajouter des données

simulateur

Lancement du serveur FHIR d'InterSystems

Dans le dossier racine de ce git, exécutez la commande suivante :

docker-compose up -d

À la fin du processus de construction, vous serez en mesure de vous connecter au référentiel FHIR :

http://localhost:32783/fhir/portal/patientlist.html

portail

Ce portail a été réalisé par @diashenrique.

Avec quelques modifications pour gérer les pas d'activité d'Apple.

Jeu avec l'application iOS

L'application vous demandera d'abord d'accepter de partager certaines informations.

Cliquez sur autoriser

authoriser

Vous pouvez ensuite tester le serveur FHIR en cliquant sur "Sauvegarder et tester le serveur".

Les paramètres par défaut pointent vers la configuration docker.

En cas de succès, vous pouvez entrer les informations de votre patient.

Prénom, Nom de famille, Date de naissance, Genre.

Enregistrez le patient dans Fhir. Une fenêtre pop-up vous montrera votre ID Fhir unique.

savepatient

Consultez ce patient sur le portail :

Accédez à: http://localhost:32783/fhir/portal/patientlist.html

Nous pouvons voir ici, qu'il y a un nouveau patient "toto" avec 0 activités.

patient portal

Envoyez ses activités :

Retournez dans l'application iOS et cliquez sur Compteur de pas.

Ce panneau résume le nombre de pas de la semaine. Dans notre cas, il s'agit de 2 entrées.

Maintenant vous pouvez les envoyer à InterSystems IRIS FHIR par un clic sur envoi.

envoie ios

Consultez les nouvelles activités sur le portail :

Nous pouvons voir maintenant que Toto a deux nouvelles observations et activités.

portail d'activités

Vous pouvez éventuellement cliquer sur le bouton de graphique pour l'afficher comme un graphique.

graphiques de portail

Comment ça marche

iOS

La plupart de cette démo est construite sur SwiftUI.

https://developer.apple.com/xcode/swiftui/

Qui est le dernier framework pour iOS et co.

Comment vérifier l'autorisation pour les travaux sur les données de santé

Il se trouve dans la classe SwiftFhirIrisManager.

Cette classe est un élément singleton et elle sera transportée tout autour de l'application avec l'annotation @EnvironmentObject.

Plus d'informations ici : https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views

La méthode requestAuthorization :

    // Demander l'autorisation pour accéder à HealthKit.
    func requestAuthorization() {
        // Demande d'accès.
        /// - Tag: RequestAuthorization

        let writeDataTypes: Set<HKSampleType> = dataTypesToWrite()
        let readDataTypes: Set<HKObjectType> = dataTypesToRead()

        // demande d'accès
        healthStore.requestAuthorization(toShare: writeDataTypes, read: readDataTypes) { (succès, erreur) dans
            if !success {
                // Traitez l'erreur ici.
            } else {

                DispatchQueue.main.async {
                    self.authorizedHK = true
                }

            }
        }
    }

Où healthStore est l'objet de HKHealthStore().

Le HKHealthStore est comme la base de données de santé dans iOS.

dataTypesToWrite et dataTypesToRead sont les objets que nous souhaitons interroger dans la base de données.

L'autorisation doit avoir un but et cela est fait dans le fichier xml Info.plist en ajoutant :

    <key>NSHealthClinicalHealthRecordsShareUsageDescription</key>
    <string>Lisez les données pour IrisExporter</string>
    <key>NSHealthShareUsageDescription</key>
    <string>Envoyez les données à IRIS</string>
    <key>NSHealthUpdateUsageDescription</key>
    <string>Date d'inscription pour IrisExporter</string>

Comment se connecter à un référentiel FHIR ?

Pour cette partie, j'ai utilisé le paquet FHIR de Smart-On-FHIR : https://github.com/smart-on-fhir/Swift-FHIR.

La classe utilisée est le FHIROpenServer.

    private func test() {

        progress = true

        let url = URL(string: self.url)

        swiftIrisManager.fhirServer = FHIROpenServer(baseURL : url! , auth: nil)

        swiftIrisManager.fhirServer.getCapabilityStatement() { FHIRError in

            progress = false
            showingPopup = true

            if FHIRError == nil {
                showingSuccess = true
                textSuccess = "Connecté au référentiel fhir"
            } else {
                textError = FHIRError?.description ?? "Erreur inconnue"
                showingSuccess = false
            }

            return
        }

    }

Cela permet de créer un nouvel objet fhirServer dans le singleton swiftIrisManager.

Ensuite, nous utilisons la fonction getCapabilityStatement().

Si nous pouvons récupérer le capabilityStatement du serveur FHIR, cela signifie que nous nous sommes connectés avec succès au référentiel FHIR.

Ce référentiel n'est pas en HTTPS, par défaut apple interdit ce type de communication.

Pour permettre le support HTTP, le fichier xml Info.plist est édité comme suit :

    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSExceptionDomains</key>
        <dict>
            <key>localhost</key>
            <dict>
                <key>NSIncludesSubdomains</key>
                <true/>
                <key>NSExceptionAllowsInsecureHTTPLoads</key>
                <true/>
            </dict>
        </dict>
    </dict>

Comment enregistrer un patient dans le référentiel FHIR ?

Opération de base en vérifiant d'abord si le patient existe déjà dans le référentiel.

Patient.search(["family": "\(self.lastName)"]).perform(fhirServer)

Cela permet de rechercher les patients ayant le même nom de famille.

Ici, nous pouvons imaginer d'autres scénarios comme avec Oauth2 et un jeton JWT pour joindre le patient et son jeton. Mais pour cette démo, nous gardons les choses simples.

Ensuite, si le patient existe, nous le récupérons, sinon nous le créons :

    func createPatient(callback: @escaping (Patient?, Error?) -> Void) {
        // Créer une nouvelle ressource pour le patient
        let patient = Patient.createPatient(prénom: firstName, nom: lastName, date de naissance: birthDay, sex: gender)

        patient?.create(fhirServer, callback: { (erreur) dans
            callback(patient, erreur)
        })
    }

Comment extraire les données de HealthKit ?

Cela se fait en interrogeant le magasin Healthkit (HKHealthStore()).

Ici, nous faisons une requête pour les pas.

Préparez la requête avec le prédicat.

        //La semaine dernière
        let startDate = swiftFhirIrisManager.startDate
        //Now
        let endDate = swiftFhirIrisManager.endDate

        print("Collecte des séances d'entraînement entre \(startDate) et \(endDate)")

        let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: HKQueryOptions.strictEndDate)

Puis la requête elle-même avec son type de données (HKQuantityType.quantityType(forIdentifier : .stepCount)) et le prédicat.

func queryStepCount(){

        //La semaine dernière
        let startDate = swiftFhirIrisManager.startDate
        //Maintenant
        let endDate = swiftFhirIrisManager.endDate

        print("Collecte des séances d'entraînement entre \(startDate) et \(endDate)")

        let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: HKQueryOptions.strictEndDate)

        let query = HKSampleQuery(sampleType: HKQuantityType.quantityType(forIdentifier: .stepCount)!, predicate: predicate, limit: HKObjectQueryNoLimit, sortDescriptors: nil) { (requête, résultats, erreur) dans

            guard let results = results as? [HKQuantitySample] else {
                   return
            }

            process(results, type: .stepCount)

        }

        healthStore.execute(query)

    }

Comment transformer les données de HealthKit en FHIR ?

Pour cette partie, nous utilisons le paquet Microsoft HealthKitToFHIR

https://github.com/microsoft/healthkit-to-fhir

Il s'agit d'un paquet utile qui offre des facteurs pour transformer HKQuantitySample en FHIR Observation

     let observation = try! ObservationFactory().observation(from: item)
      let patientReference = try! Reference(json: ["référence" : "Patient/\(patientId)"])
      observation.category = try! [CodeableConcept(json: [
          "coding": [
            [
              "system": "http://terminology.hl7.org/CodeSystem/observation-category",
              "code": "activité",
              "display": "Activité"
            ]
          ]
      ])]
      observation.subject = patientReference
      observation.status = .final
      print(observation)
      observation.create(self.fhirServer,callback: { (erreur) dans
          if error != nil {
              completion(error)
          }
      })

Où élément est un HKQuantitySample, dans notre cas un type stepCount.

Le facteur fait le gros du travail en convertissant 'élément' et 'type' en FHIR codeableConcept et 'valeur' en FHIR valueQuantity.

La référence au patientId est faite manuellement en intégrant une référence json fhir.

let patientReference = try! Reference(json: ["référence" : "Patient/\(patientId)"])

On fait de même pour la catégorie :

      observation.category = try! [CodeableConcept(json: [
          "codage": [
            [
              "systèmem": "http://terminology.hl7.org/CodeSystem/observation-category",
              "code": "activité",
              "affichage": "Activité"
            ]
          ]
      ])]

Enfin, l'observation est créée dans le référentiel fhir :

      observation.create(self.fhirServer,callback: { (erreur) in
          if error != nil {
              completion(error)
          }
      })

Logiciel de gestion (FHIR)

Pas grand chose à dire, il est basé sur le modèle fhir de la communauté InterSystems :

https://openexchange.intersystems.com/package/iris-fhir-template

Interface utilisateur

Il est basé sur les travaux de Henrique et est un joli interface utilisateur pour les dépôts FHIR fait en jquery.

https://openexchange.intersystems.com/package/iris-fhir-portal

0
0 163
Article Lorenzo Scalese · Juin 8, 2022 11m read

En tant que développeur, vous avez probablement passé au moins un certain temps à écrire un code répétitif. Vous vous êtes peut-être même retrouvé à souhaiter pouvoir générer ce code de manière programmatique. Si vous êtes dans cette situation, cet article est pour vous !

Nous allons commencer par un exemple. Note : les exemples suivants utilisent l'interface %DynamicObject, qui nécessite Caché 2016.2 ou une version supérieure. Si vous n'êtes pas familier avec cette classe, consultez la documentation ici : Utiliser JSON dans Caché. C'est vraiment génial !

##Exemple

Vous avez une classe %Persistent que vous utilisez pour stocker des données. Maintenant, supposons que vous allez saisir des données au format JSON en utilisant l'interface %DynamicObject. Comment faire correspondre la structure %DynamicObject à votre classe ? Une solution consiste à écrire du code pour copier directement les valeurs :

Class Test.Generator Extends %Persistent
{
Property SomeProperty As %String;

Property OtherProperty As %String;

ClassMethod FromDynamicObject(dynobj As %DynamicObject) As Test.Generator
{
	set obj = ..%New()
	set obj.SomeProperty = dynobj.SomeProperty
	set obj.OtherProperty = dynobj.OtherProperty
	quit obj
}
}

Cependant, si les propriétés sont nombreuses ou si vous utilisez ce modèle pour plusieurs classes, cela devient fastidieux (et difficile à maintenir). C'est là que les Method Generators peuvent vous aider ! En termes simples, lorsqu'on utilise une Method Generator, au lieu d'écrire le code d'une méthode donnée, on écrit du code que le compilateur de la classe exécutera pour générer le code de la méthode. Cela vous semble-t-il gênant ? Non, pas du tout. Prenons un exemple :

Class Test.Generator Extends %Persistent
{
ClassMethod Test() As %String [ CodeMode = objectgenerator ]
{
	do %code.WriteLine(" write ""This is a method Generator!"",!")
	do %code.WriteLine(" quit ""Done!""")

	quit $$$OK
}
}

Nous utilisons le paramètre CodeMode = objectgenerator pour indiquer que la méthode courante est une Method Generator, et non une méthode classique. Comment fonctionne cette méthode ? Afin de déboguer les Method Generators, il est utile de regarder le code généré pour la classe. Dans notre cas, il s'agit d'une routine INT nommée Test.Generator.1.INT. Vous pouvez l'ouvrir dans Studio en tapant Ctrl+Shift+V, ou vous pouvez simplement ouvrir la routine depuis la boîte de dialogue "Open" de Studio, ou depuis l'Atelier.

Dans le code INT, vous pouvez trouver l'implémentation de cette méthode :

zTest() public {
 write "This is a method Generator!",!
 quit "Done!" }

Comme vous pouvez le voir, l'implémentation de la méthode contient simplement le texte qui est écrit dans l'objet %code. %code est un objet de type spécial de flux (%Stream.MethodGenerator). Le code écrit dans ce flux peut contenir n'importe quel code valide dans une routine MAC, y compris des macros, des directives de préprocesseur, et du SQL intégré. Il y a deux choses à garder à l'esprit quand on travaille avec des Method Generators :

  • La signature de la méthode s'applique à la méthode cible que vous allez générer. Le code du générateur doit toujours renvoyer un code d'état indiquant soit un succès, soit une erreur.

  • Le code écrit dans %code doit être un ObjectScript valide (les générateurs de méthodes avec d'autres modes de langage ne sont pas concernés par cet article). Cela signifie, entre autres, que les lignes contenant des commandes doivent commencer par un espace. Notez que les deux appels WriteLine() dans l'exemple commencent par un espace.

En plus de la variable %code (représentant la méthode générée), le compilateur rend les métadonnées de la classe courante disponibles dans les variables suivantes :

  • %class
  • %method
  • %compiledclass
  • %compiledmethod
  • %parameter

Les quatre premières variables sont des instances de %Dictionary.ClassDefinition, %Dictionary.MethodDefinition, %Dictionary.CompiledClass%Dictionary.CompiledMethod, respectivement. %parameter est un tableau souscrit de noms et de valeurs de paramètres définis dans la classe.

La principale différence (pour nos besoins) entre %class et %compiledclass est que %class ne contient que les métadonnées des membres de la classe (propriétés, méthodes, etc.) définis dans la classe courante. %compiledclass contiendra ces membres, mais aussi les métadonnées de tous les membres hérités. De plus, les informations de type référencées à partir de %class apparaîtront exactement comme spécifié dans le code de la classe, alors que les types dans %compiledclass (et %compiledmethod) seront étendus au nom complet de la classe. Par exemple, %String sera développé en %Library.String, et les noms de classes sans package spécifié seront développés en nom complet Package.Class. Vous pouvez consulter la référence de ces classes pour plus d'informations.

En utilisant ces informations, nous pouvons construire une Method Generator pour notre exemple %DynamicObject :

ClassMethod FromDynamicObject(dynobj As %DynamicObject) As Test.Generator [ CodeMode = objectgenerator ]
{
	do %code.WriteLine(" set obj = ..%New()")
	for i=1:1:%class.Properties.Count() {
		set prop = %class.Properties.GetAt(i)
		do %code.WriteLine(" if dynobj.%IsDefined("""_prop.Name_""") {")
		do %code.WriteLine("   set obj."_prop.Name_" = dynobj."_prop.Name)
		do %code.WriteLine(" }")
	}

	do %code.WriteLine(" quit obj")
	quit $$$OK
}

Le code suivant est ainsi créé :

zFromDynamicObject(dynobj) public {
 set obj = ..%New()
 if dynobj.%IsDefined("OtherProperty") {
   set obj.OtherProperty = dynobj.OtherProperty
 }
 if dynobj.%IsDefined("SomeProperty") {
   set obj.SomeProperty = dynobj.SomeProperty
 }
 quit obj }

Comme vous pouvez le voir, cela génère du code pour configurer chaque propriété définie dans cette classe. Notre implémentation exclut les propriétés héritées, mais nous pourrions facilement les inclure en utilisant %compiledclass.Properties au lieu de %class.Properties. Nous avons également ajouté une vérification pour voir si la propriété existe dans le %DynamicObject avant de tenter de la définir. Ce n'est pas strictement nécessaire, puisque la référence à une propriété qui n'existe pas dans un %DynamicObject n'entraînera pas d'erreur, mais c'est utile si l'une des propriétés de la classe définit une valeur par défaut. Si nous n'effectuons pas cette vérification, la valeur par défaut sera toujours surchargée par cette méthode.

Les Method Generators peuvent être très puissants lorsqu'ils sont combinés à l'héritage. Nous pouvons prendre le générateur de méthodes FromDynamicObject() et le placer dans une classe abstraite. Maintenant, si nous voulons écrire une nouvelle classe qui doit être capable d'être désérialisée à partir d'un %DynamicObject, tout ce que nous devons faire est d'étendre cette classe pour activer cette fonctionnalité. Le compilateur de classes exécutera le code de la Method Generator lors de la compilation de chaque sous-classe, créant ainsi une implémentation personnalisée pour cette classe.

Débogage des générateurs de méthodes

Débogage de base

L'utilisation de Method Generator permet d'ajouter un niveau d'indirection à votre programmation. Cela peut poser quelques problèmes lorsqu'on essaie de déboguer le code du générateur. Prenons un exemple. Considérons la méthode suivante :

Method PrintObject() As %Status [ CodeMode = objectgenerator ]
{
	if (%class.Properties.Count()=0)&&($get(%parameter("DISPLAYEMPTY"),0)) {
		do %code.WriteLine(" write ""{}"",!")
	} elseif %class.Properties.Count()=1 {
		set pname = %class.Properties.GetAt(1).Name
		do %code.WriteLine(" write ""{ "_pname_": ""_.."_pname_"_""}"",!")
	} elseif %class.Properties.Count()>1 {
		do %code.WriteLine(" write ""{"",!")
		for i=1:1:%class.Properties.Count() {
			set pname = %class.Properties.GetAt(i).Name
			do %code.WriteLine(" write """_pname_": ""_.."_pname_",!")
		}
		do %code.WriteLine(" write ""}""")
	}

	do %code.WriteLine(" quit $$$OK")
	quit $$$OK
}

Il s'agit d'une méthode simple conçue pour imprimer le contenu d'un objet. Elle affiche les objets dans un format différent selon le nombre de propriétés : un objet avec plusieurs propriétés sera imprimé sur plusieurs lignes, tandis qu'un objet avec zéro ou une propriété sera imprimé sur une ligne. De plus, l'objet contient un paramètre DISPLAYEMTPY, qui permet de supprimer ou non l'affichage des objets ayant zéro propriété. Cependant, il y a un problème avec le code. Pour une classe avec zéro propriété, l'objet n'est pas affiché correctement :

TEST>set obj=##class(Test.Generator).%New()

TEST>do obj.PrintObject()

TEST>

Nous nous attendons à ce que cela produise un objet vide "{}", et non un rien. Pour déboguer cela, nous pouvons regarder dans le code INT pour voir ce qui se passe. Cependant, en ouvrant le code INT, vous découvrez qu'il n'y a pas de définition pour zPrintObject() ! Ne me croyez pas sur parole, compilez le code et regardez par vous-même. Allez-y... Je vais attendre.

OK. Retour ? Qu'est-ce qui se passe ici ? Les lecteurs astucieux ont peut-être trouvé le problème initial : il y a une faute de frappe dans la première clause de l'instruction IF. La valeur par défaut du paramètre DISPLAYEMPTY devrait être 1 et non 0. Il devrait être le suivant : $get(%parameter("DISPLAYEMPTY"),1) not $get(%parameter("DISPLAYEMPTY"),0). Ceci explique le comportement. Mais pourquoi la méthode n'était-elle pas dans le code INT ? Il était encore exécutable. Nous n'avons pas eu d'erreur <METHOD DOES NOT EXIST> ; la méthode n'a simplement rien fait. Maintenant que nous voyons l'erreur, regardons ce que le code aurait été s'il avait été dans le code INT. Puisque nous n'avons satisfait à aucune des conditions de la construction if ... elseif ..., le code aurait été simplement comme suit :

zPrintObject() public {
	quit 1 }

Remarquez que ce code ne fonctionne pas réellement ; il renvoie simplement une valeur littérale. Il s'avère que le compilateur de classe Caché est assez intelligent. Dans certaines situations, il peut détecter que le code d'une méthode n'a pas besoin d'être exécuté, et peut optimiser le code INT de la méthode. Il s'agit d'une excellente optimisation, car la répartition du noyau vers le code INT peut impliquer une quantité considérable de surcharge, en particulier pour les méthodes simples.

Notez que ce comportement n'est pas spécifique aux Method Generators. Essayez de compiler la méthode suivante, et cherchez-la dans le code INT :

ClassMethod OptimizationTest() As %Integer
{
	quit 10
}

Il peut être très utile de vérifier le code INT pour déboguer le code de votre Method Generator. Cela vous permettra de savoir ce que le générateur a réellement produit. Cependant, vous devez être attentif au fait qu'il y a des cas où le code généré n'apparaîtra pas dans le code INT. Si cela se produit de manière inattendue, il y a probablement un bug dans le code du générateur qui l'empêche de générer un code significatif.

Utilisation du débogueur

Comme nous l'avons vu, s'il y a un problème avec le code généré, nous pouvons le voir en regardant le code INT. Nous pouvons également déboguer la méthode normalement en utilisant ZBREAK ou le débogueur de Studio. Vous vous demandez peut-être s'il existe un moyen de déboguer le code de la Method Generator elle-même. Bien sûr, vous pouvez toujours ajouter des instructions "write" à la Method Generator ou définir des globaux de débogage comme un homme des cavernes. Mais il doit bien y avoir un meilleur moyen, n'est-ce pas ?

La réponse est "Oui", mais pour comprendre la manière dont cela se passe, nous devons connaître le fonctionnement du compilateur de classes. En gros, lorsque le compilateur de classes compile une classe, il va d'abord analyser la définition de la classe et générer les métadonnées de la classe. Il s'agit essentiellement de générer les données pour les variables %class et %compiledclass dont nous avons parlé précédemment. Ensuite, il génère le code INT pour toutes les méthodes. Au cours de cette étape, il va créer une routine séparée pour contenir le code de génération de tous les Method Generators. Cette routine est nommée <classname>.G1.INT. Il exécute ensuite le code dans la routine *.G1 pour générer le code des méthodes, et les stocke dans la routine <classname>.1.INT avec le reste des méthodes de la classe. Il peut ensuite compiler cette routine et voilà ! Nous avons notre classe compilée ! Il s'agit bien sûr d'une simplification énorme d'un logiciel très complexe, mais cela suffira pour nos besoins.

Cette routine *.G1 semble intéressante. Jetons-y un coup d'œil !

	;Test.Generator3.G1
	;(C)InterSystems, method generator for class Test.Generator3.  Do NOT edit.
	Quit
	;
FromDynamicObject(%class,%code,%method,%compiledclass,%compiledmethod,%parameter) public {
	do %code.WriteLine(" set obj = ..%New()")
	for i=1:1:%class.Properties.Count() {
		set prop = %class.Properties.GetAt(i)
		do %code.WriteLine(" if dynobj.%IsDefined("""_prop.Name_""") {")
		do %code.WriteLine("   set obj."_prop.Name_" = dynobj."_prop.Name)
		do %code.WriteLine(" }")
	}
	do %code.WriteLine(" quit obj")
	quit 1
 Quit 1 }

Vous êtes peut-être habitué à modifier le code INT d'une classe et à ajouter du code de débogage. Normalement, c'est bien, même si c'est un peu primitif. Cependant, cela ne va pas fonctionner ici. Afin d'exécuter ce code, nous devons recompiler la classe. (C'est le compilateur de la classe qui l'appelle, après tout.) Mais recompiler la classe régénérera cette routine, effaçant toutes les modifications que nous avons apportées. Heureusement, nous pouvons utiliser ZBreak ou le débogueur de Studio pour parcourir ce code. Puisque nous connaissons maintenant le nom de la routine, l'utilisation de ZBreak est assez simple :

TEST>zbreak FromDynamicObject^Test.Generator.G1

TEST>do $system.OBJ.Compile("Test.Generator","ck")

La compilation a commencé le 14/11/2016 17:13:59 avec les qualificatifs 'ck'
Compiling class Test.Generator
FromDynamicObject(%class,%code,%method,%compiledclass,%compiledmethod,%parameter) publ
            ^
ic {
<BREAK>FromDynamicObject^Test.Generator.G1
TEST 21e1>write %class.Name
Test.Generator
TEST 21e1>

L'utilisation du débogueur de Studio est également simple. Vous pouvez définir un point de contrôle dans la routine *.G1.MAC, et configurer la cible de débogage pour qu'elle invoque $System.OBJ.Compile() sur la classe :

$System.OBJ.Compile("Test.Generator","ck")

Et maintenant vous vous lancez dans le débogage.

Conclusion

Cet article a été un bref aperçu des générateurs de méthodes. Pour de plus amples informations, veuillez consulter la documentation ci-dessous :

0
0 129
Article Irène Mykhailova · Juin 7, 2022 4m read

Pour chaque propriété, requête ou index défini, plusieurs méthodes correspondantes seraient automatiquement générées lors de la compilation d'une classe. Ces méthodes peuvent être très utiles. Dans cet article, je décrirai certaines d'entre elles.

Properties

Disons que vous avez défini une propriété nommée "Property". Les méthodes suivantes seraient automatiquement disponibles (la propriété indiquée en gras est une partie variable, égale au nom de la propriété) :

ClassMethod PropertyGetStored(id)

Pour les propriétés de type de données, cette méthode renvoie leur valeur logique, pour les propriétés d'objet, elle renvoie l'id. C'est une référence globale wrappée à la globale de données de la classe et le moyen le plus rapide de récupérer la valeur de la propriété singulière. Cette méthode n'est disponible que pour les propriétés stockées.

Method PropertyGet()

C'est un getter de propriété. Peut être redéfini.

Method PropertySet(val) As %Status

C'est un définisseur de propriété. Peut être redéfini.


Propriétés de l'objet

S'il s'agit d'une propriété objet, certaines méthodes supplémentaires, liées à l'accès aux ID et OID, deviennent disponibles :

Method PropertySetObjectId(id)

Cette méthode définit la valeur de la propriété par ID, il n'est donc pas nécessaire d'ouvrir un objet pour le définir comme valeur de la propriété.

Method PropertyGetObjectId()

Cette méthode renvoie l'ID de la valeur de la propriété.

Method PropertySetObject(oid)

Cette méthode définit la valeur de la propriété par OID.

Method PropertyGetObject()

Cette méthode renvoie l'OID de la valeur de la propriété.

Propriétés du type de données

Pour une propriété de type de données, plusieurs autres méthodes de conversion entre différents formats sont disponibles :

ClassMethod PropertyDisplayToLogical(val)
ClassMethod PropertyLogicalToDisplay(val)
ClassMethod PropertyOdbcToLogical(val)
ClassMethod PropertyLogicalToOdbc(val)
ClassMethod PropertyXSDToLogical(val)
ClassMethod PropertyLogicalToXSD(val)
ClassMethod PropertyIsValid(val) As %Status

Vérification de la validité de la valeur de la propriété

ClassMethod PropertyNormalize(val)

Renvoi de la valeur logique normalisée

Remarques

  • Les relations constituent des propriétés et peuvent être obtenues ou définies à l'aide des méthodes suivantes
  • L'entrée val est toujours une valeur logique, sauf pour les méthodes de conversion de format.

  • Index

    Pour un index nommé "Index", les méthodes suivantes seraient automatiquement disponibles

    ClassMethod IndexExists(val) As %Boolean

    Renvoi de 1 ou 0 selon l'existence d'un objet avec ce val, où val est une valeur logique de la propriété indexée.


    Index uniques

    Pour les index uniques, des méthodes supplémentaires sont disponibles :

    ClassMethod IndexExists(val, Output id) As %Boolean

    Renvoi de 1 ou 0 en fonction de l'existence d'un objet avec ce val, où val est une valeur logique de la propriété indexée. Renvoi également de l'identifiant de l'objet (s'il a été trouvé) comme second argument.

    ClassMethod IndexDelete(val, concurrency = -1) As %Status

    Suppression de l'entrée dont la valeur d'index est égale à val.

    ClassMethod IndexOpen(val, concurrency, sc As %Status)  

    Renvoi de l'objet existant dont l'indice est égal à val.

    Remarques:

    a) Étant donné qu'un index peut être basé sur plusieurs propriétés, la signature de la méthode serait modifiée pour avoir plusieurs valeurs en entrée, par exemple, considérez cet index :

    Index MyIndex On (Prop1, Prop2);

    La méthode IndexExists aurait alors la signature suivante :

    ClassMethod IndexExists(val1, val2) As %Boolean

    Où val1 correspond à la valeur de Prop1 et val2 correspond à la valeur de Prop2. Les autres méthodes suivent la même logique.

    b) Caché génère un index IDKEY qui indexe le champ ID (RowID). Il peut être redéfini par l'utilisateur et peut également contenir plusieurs propriétés. Par exemple, pour vérifier si une classe a une propriété définie, exécutez :

    Write ##class(%Dictionary.PropertyDefinition).IDKEYExists(class, property)

    c) Toutes les méthodes d'indexation vérifient la présence d'une valeur logique

    d) Documentation

    Requêtes

    En ce qui concerne une requête (qui peut être une simple requête SQL ou une requête de classe personnalisée, voici mon post à ce sujet) nommée "Query", la méthode Func est générée :

    ClassMethod QueryFunc(Arg1, Arg2) As %SQL.StatementResult

    qui renvoie un %SQL.StatementResult utilisé pour itérer sur la requête. Par exemple, la classe Sample.Person de l'espace de noms Samples possède une requête ByName acceptant un paramètre. Elle peut être appelée depuis le contexte objet au moyen de ce code :

    Set ResultSet=##class(Sample.Person).ByNameFunc("A")
    While ResultSet.%Next() { Write ResultSet.Name,! }

    En outre, une classe de démonstration sur GitHub illustre ces méthodes.

    0
    0 79
    Article Guillaume Rongier · Juin 3, 2022 13m read

    Class Query dans InterSystems IRIS (et Cache, Ensemble, HealthShare) est un outil utile qui sépare les requêtes SQL du code Object Script. En principe, cela fonctionne comme suit : supposons que vous souhaitiez utiliser la même requête SQL avec différents arguments à plusieurs endroits différents. Dans ce cas, vous pouvez éviter la duplication du code en déclarant le corps de la requête comme une Class Query, puis en appelant cette requête par son nom. Cette approche est également pratique pour les requêtes personnalisées, dans lesquelles la tâche consistant à obtenir la ligne suivante est définie par un développeur. Cela vous intéresse ? Alors lisez la suite !

    Class queries de base

    Plus simplement, les Class Queries de base vous permettent de représenter des requêtes SQL SELECT. L'optimiseur et le compilateur SQL les traitent comme des requêtes SQL standards, mais elles sont plus pratiques lorsqu'il s'agit de les exécuter à partir du contexte Caché Object Script. Ils sont déclarés en tant qu'éléments de requête Query dans les définitions de classe (similaires aux méthodes ou aux propriétés) de la manière suivante :

    • Type: %SQLQuery
    • Tous les arguments de votre requête SQL doivent être énumérés dans la liste des arguments
    • Type de requête: SELECT
    • Utiliser les deux-points pour accéder à chaque argument (similaire au SQL statique)
    • Définissez le paramètre ROWSPEC qui contient des informations sur les noms et les types de données des résultats de sortie ainsi que l'ordre des champs
    • (Facultatif) Définissez le paramètre CONTAINID qui correspond à l'ordre numérique si le champ contient l'ID. Si vous n'avez pas besoin de renvoyer l'ID, n'attribuez pas de valeur à CONTAINID
    • (Facultatif) Définissez le paramètre COMPILEMODE qui correspond au paramètre similaire en SQL statique et spécifie quand l'expression SQL doit être compilée. Lorsque ce paramètre est défini sur IMMEDIATE (par défaut), la requête sera compilée en même temps que la classe. Lorsque ce paramètre a la valeur DYNAMIC, la requête sera compilée avant sa première exécution (similaire au SQL dynamique)
    • (Facultatif) Définissez le paramètre SELECTMODE qui spécifie le format des résultats de la requête
    • Ajoutez la propriété SqlProc, si vous voulez appeler cette requête comme une procédure SQL.
    • Définissez la propriété SqlName, si vous souhaitez renommer la requête. Le nom par défaut d'une requête dans le contexte SQL est le suivant : PackageName.ClassName_QueryName
    • Caché Studio fournit l'assistant intégré pour la création de Class Query


    Exemple de définition de la classe Sample.Person avec la requête ByName qui renvoie tous les noms d'utilisateur qui commencent par une lettre spécifiée

    Class Sample.Person Extends %Persistent
    {
    Property Name As %String;
    Property DOB As %Date;
    Property SSN As %String;
    Query ByName(name As %String = "") As %SQLQuery
        (ROWSPEC="ID:%Integer,Name:%String,DOB:%Date,SSN:%String",
         CONTAINID = 1, SELECTMODE = "RUNTIME",
         COMPILEMODE = "IMMEDIATE") [ SqlName = SP_Sample_By_Name, SqlProc ]
    {
    SELECT ID, Name, DOB, SSN
    FROM Sample.Person
    WHERE (Name %STARTSWITH :name)
    ORDER BY Name
    }
    }

    Vous pouvez appeler cette requête depuis Caché Object Script de la manière suivante : 

    Set statement=##class(%SQL.Statement).%New()   
    Set status=statement.%PrepareClassQuery("Sample.Person","ByName")   
    If $$$ISERR(status) {
        Do $system.OBJ.DisplayError(status)
    }   
    Set resultset=statement.%Execute("A")   
    While resultset.%Next() {
        Write !, resultset.%Get("Name")   
    }

    Vous pouvez également obtenir un ensemble de résultats en utilisant la méthode générée automatiquement queryNameFunc :

    Set resultset = ##class(Sample.Person).ByNameFunc("A")    
    While resultset.%Next() {
        Write !, resultset.%Get("Name")   
    }
    

    Cette requête peut également être appelée à partir du SQLcontext de ces deux manières :

    Call Sample.SP_Sample_By_Name('A')
    Select * from Sample.SP_Sample_By_Name('A')

    Cette classe peut être trouvée dans l'espace de nom par défaut SAMPLES Caché. Et c'est tout pour les requêtes simples. Passons maintenant aux requêtes personnalisées

    Class queries personnalisées

    Bien que les Class Queries de base fonctionnent parfaitement dans la plupart des cas, il est parfois nécessaire d'exécuter un contrôle total sur le comportement des requêtes dans les applications, par exemple :

    • Des critères de sélection sophistiqués. Puisque dans les requêtes personnalisées vous implémentez une méthode Caché Object Script qui renvoie la ligne suivante de façon autonome, ces critères peuvent être aussi sophistiqués que vous le souhaitez.
    • Si les données sont accessibles uniquement via l'API dans un format que vous ne souhaitez pas utiliser
    • Si les données sont stockées dans des globales (sans classes)
    • Si vous avez besoin d'élever les droits afin d'accéder aux données
    • Si vous devez appeler une API externe afin d'accéder à des données
    • Si vous devez accéder au système de fichiers afin d'accéder aux données
    • Vous devez effectuer des opérations supplémentaires avant d'exécuter la requête (par exemple, établir une connexion, vérifier les autorisations, etc.)

    Alors, comment créer des requêtes de classes personnalisées ? Tout d'abord, vous devez définir 4 méthodes qui mettent en œuvre l'ensemble du flux de travail de votre requête, de l'initialisation à la destruction :

    • queryName — fournit des informations sur une requête (similaire aux requêtes de classe de base)
    • queryNameExecute — construit une requête
    • queryNameFetch — obtient le résultat de la ligne suivante d'une requête
    • queryNameClose — détruit une requête

    Analysons maintenant ces méthodes plus en détail.

    La méthode queryName

    La méthode queryName représente des informations sur une requête

    • Type: %Query
    • Laissez le corps vide
    • Définissez le paramètre ROWSPEC qui contient les informations sur les noms et les types de données des résultats de sortie ainsi que l'ordre des champs
    • (Facultatif) Définissez le paramètre CONTAINID qui correspond à l'ordre numérique si le champ contient l'ID. Si vous ne renvoyez pas d'ID, n'attribuez pas de valeur à CONTAINID

    Par exemple, créons la requête AllRecords (queryName = AllRecords, et la méthode est simplement appelée AllRecords) qui produira toutes les instances de la nouvelle classe persistante Utils.CustomQuery, une par une. Tout d'abord, créons une nouvelle classe persistante Utils.CustomQuery :

    Class Utils.CustomQuery Extends (%Persistent, %Populate){
    Property Prop1 As %String;
    Property Prop2 As %Integer;
    }

    Maintenant, écrivons la requête AllRecords :

    Query AllRecords() As %Query(CONTAINID = 1, ROWSPEC = "Id:%String,Prop1:%String,Prop2:%Integer") [ SqlName = AllRecords, SqlProc ]
    {
    }

    La méthode queryNameExecute
    La méthode queryNameExecute initialise complètement une requête. La signature de cette méthode est la suivante :

    ClassMethod queryNameExecute(ByRef qHandle As %Binary, args) As %Status

    où:

    • qHandle est utilisé pour la communication avec les autres méthodes de l'implémentation de la requête
    • Cette méthode doit mettre qHandle dans l'état qui sera ensuite transmis à la méthode queryNameFetch
    • qHandle peut être défini comme OREF, une variable ou une variable multidimensionnelle
    • Les args sont des paramètres supplémentaires transmis à la requête. Vous pouvez ajouter autant d'args que vous le souhaitez (ou ne pas les utiliser du tout)
    • La méthode doit retourner le statut d'initialisation de la requête

    Revenons à notre exemple. Vous pouvez itérer dans l'étendue de plusieurs façons (je décrirai plus loin les approches de travail de base pour les requêtes personnalisées), mais pour cet exemple, itérons dans la globale en utilisant la fonction $Order. Dans ce cas, qHandle stockera l'ID actuel, et puisque nous n'avons pas besoin d'arguments supplémentaires, l'argument arg n'est pas nécessaire. Le résultat est le suivant :

    ClassMethod AllRecordsExecute(ByRef qHandle As %Binary) As %Status {  
        Set qHandle = ""    Quit $$$OK
    }

    La méthode queryNameFetch
    La méthode queryNameFetch renvoie un seul résultat sous la forme $List. La signature de cette méthode est la suivante :

    ClassMethod queryNameFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = queryNameExecute ]

    where:

    • qHandle est utilisé pour la communication avec les autres méthodes de l'implémentation de la requête
    • Lorsque la requête est exécutée, les valeurs spécifiées par queryNameExecute ou par un appel précédent de queryNameFetch sont attribuées à qHandle.
    • Le rang sera défini soit par une valeur de %List, soit par une chaîne vide, si toutes les données ont été traitées
    • AtEnd doit être mis à 1, une fois que la fin des données est atteinte.
    • La méthode "Fetch" doit être positionnée après la méthode "Execute", mais cela n'est important que pour SQL statique, c'est-à-dire les curseurs à l'intérieur des requêtes.

    En général, les opérations suivantes sont effectuées dans le cadre de cette méthode :

    1. Vérifier si nous avons atteint la fin des données
    2. S'il reste encore des données : Créez une nouvelle %List et attribuez une valeur à la variable Row
    3. Sinon, mettez AtEnd à 1
    4. Préparer qHandle pour la prochaine récupération de résultat
    5. Retourner l'état

    Voici comment cela se présente dans notre exemple :

    ClassMethod AllRecordsFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status {
        #; itérer dans ^Utils.CustomQueryD    
        #; ecrire le prochain id dans qHandle et écriture de la valeur de la globale avec le nouvel id dans val
        Set qHandle = $Order(^Utils.CustomQueryD(qHandle),1,val)
        #; Vérifier s'il reste des données
           If qHandle = "" {
            Set AtEnd = 1
            Set Row = ""
            Quit $$$OK    
        }
        #; Si ce n'est pas le cas, créer %List
        #; val = $Lb("", Prop1, Prop2) voir définition de Storage
        #; Row =$lb(Id,Prop1, Prop2)  voir ROWSPEC pour la demande AllRecords
        Set Row = $Lb(qHandle, $Lg(val,2), $Lg(val,3))
        Quit $$$OK
    }

    La méthode queryNameClose
    La méthode queryNameClose met fin à la requête, une fois toutes les données obtenues. La signature de cette méthode est la suivante :

    ClassMethod queryNameClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = queryNameFetch ]

    où :

    • Caché exécute cette méthode après le dernier appel à la méthode queryNameFetch
    • En d'autres termes, il s'agit d'un destructeur de requête
    • Par conséquent, vous devez disposer de tous les curseurs SQL, des requêtes et des variables locales dans sa mise en œuvre
    • Les méthodes renvoient l'état actuel

    Dans notre exemple, nous devons supprimer la variable locale qHandle :

    ClassMethod AllRecordsClose(ByRef qHandle As %Binary) As %Status {
        Kill qHandle
        Quit $$$OK
      }

    Et voilà ! Une fois que vous aurez compilé la classe, vous serez en mesure d'utiliser la requête AllRecords à partir de %SQL.Statement - tout comme les requêtes de la classe de base.

    Approches de la logique d'itération pour les requêtes personnalisées

    Alors, quelles approches peuvent être utilisées pour les requêtes personnalisées ? En général, il existe 3 approches de base :

    Itération à travers une globale
    Cette approche est basée sur l'utilisation de $Order et de fonctions similaires pour l'itération à travers une globale. Elle peut être utilisée dans les cas suivants :

    • Les données sont stockées dans des globales (sans classes)
    • Vous voulez réduire le nombre de glorefs dans le code
    • Les résultats doivent/peuvent être triés par l'indice de la globale


    SQL statique
    L'approche est basée sur les curseurs et le SQL statique. Elle est utilisée pour :

    • Rendre le code int plus lisible
    • Faciliter le travail avec les curseurs
    • Accélération du processus de compilation (le SQL statique est inclus dans la requête de la classe et n'est donc compilé qu'une seule fois).

    Remarque:

    • Les curseurs générés à partir de requêtes du type %SQLQuery sont nommés automatiquement, par exemple Q14.
    • Tous les curseurs utilisés dans une classe doivent avoir des noms différents
    • Les messages d'erreur sont liés aux noms internes des curseurs qui comportent des caractères supplémentaires à la fin de leur nom. Par exemple, une erreur dans le curseur Q140 est en fait causée par le curseur Q14.
    • Utilisez PlaceAfter et assurez-vous que les curseurs sont utilisés dans la même routine int où ils ont été déclarés.
    • INTO doit être utilisé en conjonction avec FETCH, mais pas DECLARE.


    Exemple de SQL statique pour Utils.CustomQuery :

    Query AllStatic() As %Query(CONTAINID = 1, ROWSPEC = "Id:%String,Prop1:%String,Prop2:%Integer") [ SqlName = AllStatic, SqlProc ]
    {
    }
    
    ClassMethod AllStaticExecute(ByRef qHandle As %Binary) As %Status
    {
        &sql(DECLARE C CURSOR FOR
            SELECT Id, Prop1, Prop2
            FROM Utils.CustomQuery
         )
         &sql(OPEN C)
        Quit $$$OK
    }
    
    ClassMethod AllStaticFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = AllStaticExecute ]
    {
        #; INTO doit être associé à FETCH
        &sql(FETCH C INTO :Id, :Prop1, :Prop2)
        #; Vérifier si la fin des données est atteinte
        If (SQLCODE'=0) {
            Set AtEnd = 1
            Set Row = ""
            Quit $$$OK
        }
        Set Row = $Lb(Id, Prop1, Prop2)
        Quit $$$OK
    }
    
    ClassMethod AllStaticClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = AllStaticFetch ]
    {
        &sql(CLOSE C)
        Quit $$$OK
    }

    SQL dynamique
    L'approche est basée sur les requêtes d'autres classes et le SQL dynamique. Cette approche est raisonnable lorsqu'en plus d'une requête SQL proprement dite, vous devez également effectuer certaines opérations supplémentaires, par exemple exécuter une requête SQL dans plusieurs espaces de noms ou escalader les permissions avant d'exécuter la requête.

    Exemple de SQL dynamique pour Utils.CustomQuery :

    Query AllDynamic() As %Query(CONTAINID = 1, ROWSPEC = "Id:%String,Prop1:%String,Prop2:%Integer") [ SqlName = AllDynamic, SqlProc ]
    {
    }
    
    ClassMethod AllDynamicExecute(ByRef qHandle As %Binary) As %Status
    {
        Set qHandle = ##class(%SQL.Statement).%ExecDirect(,"SELECT * FROM Utils.CustomQuery")
        Quit $$$OK
    }
    
    ClassMethod AllDynamicFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status
    {
        If qHandle.%Next()=0 {
            Set AtEnd = 1
            Set Row = ""
            Quit $$$OK
        }
        Set Row = $Lb(qHandle.%Get("Id"), qHandle.%Get("Prop1"), qHandle.%Get("Prop2"))
        Quit $$$OK
    }
    
    ClassMethod AllDynamicClose(ByRef qHandle As %Binary) As %Status
    {
        Kill qHandle
        Quit $$$OK
    }

    Approche alternative : %SQL.CustomResultSet

    Vous pouvez également créer une requête en sous-classant la classe %SQL.CustomResultSet. Les avantages de cette approche sont les suivants :

    • Une légère augmentation de la vitesse
    • ROWSPEC est inutile, puisque toutes les métadonnées sont obtenues à partir de la définition de la classe
    • Respect des principes de conception orientée objet

    Pour créer une requête à partir de la sous-classe de la classe %SQL.CustomResultSet, assurez-vous d'effectuer les étapes suivantes :

    1. Définir les propriétés correspondant aux champs résultants
    2. Définir les propriétés privées où le contexte de la requête sera stocké
    3. Remplacer la méthode %OpenCursor (similaire à queryNameExecute) qui initie le contexte. En cas d'erreur, définissez également %SQLCODE et %Message
    4. Remplacer la méthode %Next (similaire à queryNameFetch) qui obtient le résultat suivant. Remplacer les propriétés. La méthode renvoie 0 si toutes les données ont été traitées et 1 s'il reste des données
    5. Remplacer la méthode %CloseCursor (similaire à queryNameClose) si nécessaire


    Exemple de %SQL.CustomResultSet pour Utils.CustomQuery :

    Class Utils.CustomQueryRS Extends %SQL.CustomResultSet
    {
    Property Id As %String;
    Property Prop1 As %String;
    Property Prop2 As %Integer;
    Method %OpenCursor() As %Library.Status
    {
        Set ..Id = ""
        Quit $$$OK
    }
    
    Method %Next(ByRef sc As %Library.Status) As %Library.Integer [ PlaceAfter = %Execute ]
    {
        Set sc = $$$OK
        Set ..Id = $Order(^Utils.CustomQueryD(..Id),1,val)
        Quit:..Id="" 0
        Set ..Prop1 = $Lg(val,2)
        Set ..Prop2 = $Lg(val,3)
        Quit $$$OK
    }
    }

    Vous pouvez l'appeler à partir de Caché Object Script code de la manière suivante :

    Set resultset= ##class(Utils.CustomQueryRS).%New()
           While resultset.%Next() {
            Write resultset.Id,!
     }

    Un autre exemple est disponible dans l'espace de noms SAMPLES - il s'agit de la classe Sample.CustomResultSet qui implémente une requête pour Samples.Person.

    Résumé

    Les requêtes personnalisées vous aideront à séparer les expressions SQL du code Caché Object Script et à mettre en œuvre un comportement sophistiqué qui peut être trop difficile pour le SQL pur.

    Références

    Class Queries

    Itération à travers une globale

    SQL statique

    Dynamic SQL

    %SQL.CustomResultSet

    Classe Utils.CustomQuery

    Classe Utils.CustomQueryRS

    L'auteur tient à remercier [Alexander Koblov] (https://community.intersystems.com/user/alexander-koblov) pour son aide à la composition de cet article.

    0
    0 150