#Portail de gestion

0 Abonnés · 13 Publications

Le portail de gestion InterSystems est une application web qui vous permet d'effectuer des tâches d'administration et de gestion du système pour la plateforme de données InterSystems.

Documentation sur le portail de gestion d'InterSystems.

Article Lorenzo Scalese · Oct 6, 2025 5m read

Commençons par une question simple et motivante : au cours des 14 derniers jours, quelles sont les erreurs les plus courantes dans le Journal des erreurs d'application?

Répondre à cette question via le portail de gestion ou le terminal est un processus manuel fastidieux. Nous devrions pouvoir simplement utiliser SQL. Heureusement, quelques requêtes de classe sont disponibles  pour vous aider dans la classe SYS.ApplicationError de l'espace de noms %SYS. Vous pouvez répondre à cette question pour une seule date à l'aide d'une commande telle que:

select"Error message",count(*)
from SYS.ApplicationError_ErrorList('CCR','12/16/2024')
groupby"Error message"orderby2desc

Malheureusement, la structure des requêtes de classe est soumise aux mêmes contraintes structurelles générales que les pages du portail de gestion ; la requête ErrorList nécessite un espace de noms et une date. Il existe sûrement une meilleure approche que de faire 14 appels conjoints à cette requête de classe pour différentes dates, n'est-ce pas ? D'une certaine manière, c'est un véritable problème. S'il existe une bonne façon de procéder avec du SQL classique et que je l'ai simplement manquée, merci de me le faire savoir!

Logiquement, il convient de rédiger notre propre requête de classe personnalisée. Cela implique d'ajouter un membre de classe Query (par exemple <QueryName>) et d'implémenter des méthodes nommées <QueryName>Execute, <QueryName>Fetch et <QueryName>Close. De manière générale, la méthode Execute configure le contexte de la requête de classe et effectue toutes les tâches initiales, en conservant l'état dans qHandle. La méthode Fetch récupère une seule ligne et indique si toutes les lignes ont été trouvées ou non. Enfin, la méthode Close effectue le nettoyage final. Par exemple, si l'implémentation des méthodes Execute/Fetch utilise une variable globale privée au processus, la méthode Close peut la supprimer.

N'oubliez pas d'ajouter un indicateur [ SqlProc ] magique au membre Query afin qu'il puisse être appelé en tant que TVF (fonction table) à partir d'autres requêtes SQL!

Ci-dessous, vous trouverez un exemple complet fonctionnel:

/// Requêtes utilitaires pour aider à accéder au journal des erreurs de l'application à partir de SQLClass AppS.Util.ApplicationErrorLog
{

/// Renvoi de toutes les erreurs d'application (toutes dates confondues) à partir du journal des erreurs d'application Query All() As%Query(ROWSPEC = "Date:%Date,ErrorNumber:%Integer,ErrorMessage:%String,Username:%String") [ SqlProc ] { }

/// Récupèration d'une liste de dates comportant des erreurs et la stocke dans qHandleClassMethod AllExecute(ByRef qHandle As%Binary) As%Status { Set ns = $NamespaceNew$NamespaceSet$Namespace = "%SYS"Set stmt = ##class(%SQL.Statement).%New() Set stmt.%SelectMode = 0Set result = ##class(%SQL.Statement).%ExecDirect(stmt,"select %DLIST(""Date"") ""Dates"" from SYS.ApplicationError_DateList(?)",ns) $$$ThrowSQLIfError(result.%SQLCODE,result.%Message) If 'result.%Next(.sc) { Return sc } Set qHandle("list") = result.%Get("Dates") Set qHandle("pointer") = 0Quit$$$OK }

/// Récupèreation de la ligne suivante, en passant à la date suivante si nécessaireClassMethod AllFetch(ByRef qHandle As%Binary, ByRef Row As%List, ByRef AtEnd As%Integer = 0) As%Status [ PlaceAfter = AllExecute ] { Set sc = $$$OKSet ns = $NamespaceNew$NamespaceSet$Namespace = "%SYS"If$Get(qHandle("dateResult")) = "" { // Passage à la date suivanteSet pointer = qHandle("pointer") If '$ListNext(qHandle("list"),pointer,oneDate) { Set AtEnd = 1Quit$$$OK } Set qHandle("pointer") = pointer Set qHandle("currentDate") = oneDate Set qHandle("dateResult") = ##class(%SQL.Statement).%ExecDirect(,"select * from SYS.ApplicationError_ErrorList(?,?)",ns,oneDate) $$$ThrowSQLIfError(qHandle("dateResult").%SQLCODE,qHandle("dateResult").%Message) } If qHandle("dateResult").%Next(.sc) { // Si nous avons une ligne pour la date actuelle, ajoutons-laSet Row = $ListBuild(qHandle("currentDate"),qHandle("dateResult").%GetData(1),qHandle("dateResult").%GetData(2),qHandle("dateResult").%GetData(6)) } ElseIf$$$ISOK(sc) { // Sinon, il faut vider le jeu de résultats et appeler AllFetch pour avancerSet qHandle("dateResult") = ""Set$Namespace = ns Set sc = ..AllFetch(.qHandle,.Row,.AtEnd) } Quit sc }

ClassMethod AllClose(ByRef qHandle As%Binary) As%Status [ PlaceAfter = AllExecute ] { New$NamespaceSet$Namespace = "%SYS"// Il semble parfois nécessaire pour que %OnClose s'exécute correctementKill qHandle("dateResult") Quit$$$OK }

}

Dans cet exemple, nous commençons dans un espace de noms utilisateur, mais toutes les requêtes s'exécutent en réalité dans %SYS. Execute obtient une liste des dates d'erreur pour l'espace de noms actuel et la stocke dans qHandle. Fetch passe à la date suivante lorsque cela est approprié, puis renvoie l'erreur suivante pour la date actuelle. Et Close s'assure que la requête de classe sort de la portée dans %SYS, car j'obtenais parfois des erreurs si ce n'était pas le cas. C'était un peu surprenant, mais cela semble logique, car la requête de classe que nous appelons n'existe que dans %SYS.

La réutilisabilité des fonctions table offre de nombreuses possibilités. Par exemple, nous pouvons en ajouter une autre dans la même classe:

/// Obtenir le nombre d'erreurs survenues au cours des derniers <var>Days</var> jours
Query ErrorCounts(Days As%Integer) As%SQLQuery(ROWSPEC = "Occurrences:%Integer,ErrorMessage:%String") [ SqlProc ]
{
    SELECT COUNT(*) AS Occurrences, ErrorMessage
    FROM AppS_Util.ApplicationErrorLog_All()
    WHERE DATEDIFF(D,"Date",$h) <= :Days
    GROUP BY ErrorMessage
    ORDER BY Occurrences DESC
}

Et maintenant, pour obtenir les erreurs d'application les plus courantes au cours des 14 derniers jours, il suffit de:

call AppS_Util.ApplicationErrorLog_ErrorCounts(14)

Maintenant, il ne nous reste plus qu'à les corriger! 😅

0
0 18
Article Guillaume Rongier · Sept 4, 2025 6m read

img

Les modules, quel sujet ! Nous n'avons pas de notion équivalente en ObjectScript, mais c'est un concept fondamental en Python. Découvrons-le ensemble.

Qu'est-ce qu'un module?

Je considère les modules comme une couche intermédiaire entre les classes et les packages. Voici un exemple.

Un mauvais exemple :

# MyClass.py
class MyClass:
    def my_method(self):
        print("Hello from MyClass!")

Lorsque vous voulez utiliser cette classe dans un autre script, vous devez faire comme suit:

# class_usage.py
from MyClass import MyClass # weird, right?

my_instance = MyClass()
my_instance.my_method()

En quoi cet exemple est-il mauvais?

Tout d'abord, parce que les noms de fichiers doivent être écrits en snake_case conformément à la norme PEP 8, et qu'ils doivent donc être nommés my_class.py. Ensuite, parce que vous importez une classe à partir d'un fichier qui porte le même nom que la classe. Ce n'est pas une bonne pratique en Python.

Je sais que cela peut être déroutant, surtout si vous venez d'ObjectScript où les classes sont définies dans des fichiers portant le même nom que celui de la classe.

Notions avancées

Un module est un fichier Python

Donc, nous venons de voir que les modules peuvent être des fichiers Python, mais sans l'extension .py.

Mais attendez, cela signifie-t-il qu'un script python est également un module? Oui, tout à fait!

C'est pourquoi il faut être prudent lorsque vous importez un script, car celui-ci exécutera le code qui y est contenu. Consultez l'article Introduction à Python pour en savoir plus.

Un module est un dossier contenant un fichier __init__.py

Comment ça, un dossier peut être un module? Oui, c'est possible!

Un dossier peut être un module s'il contient un fichier __init__.py. Ce fichier peut être vide ou contenir le code d'initialisation du module.

Voici un exemple:

src/python/article/
└── my_folder_module/
    ├── __init__.py
    ├── my_sub_module.py
    └── another_sub_module.py
# my_folder_module/my_sub_module.py
class MySubModule:
    def my_method(self):
        print("Hello from MySubModule!")
# my_folder_module/another_sub_module.py
class AnotherSubModule:
    def another_method(self):
        print("Hello from AnotherSubModule!")
# my_folder_module/__init__.py
# Ce fichier peut être vide ou contenir le code d'initialisation du module.

Dans ce cas, my_folder_module est un module, et vous pouvez l'importer comme suit:

from my_folder_module import my_sub_module, another_sub_module

Ou si vous définissez un fichier __init__.py avec le contenu suivant:

# my_folder_module/__init__.py
from .my_sub_module import MySubModule
from .another_sub_module import AnotherSubModule

Vous pouvez l'importer de la manière suivante:

from my_folder_module import MySubModule, AnotherSubModule

Vous voyez la subtilité ? Vous pouvez importer les classes directement depuis le module sans spécifier le sous-module, car le fichier __init__.py est exécuté lorsque vous importez le module, et ce fichier peut définir les éléments disponibles dans l'espace de noms du module.

sys.path

Lorsque vous importez un module, Python le recherche dans les répertoires spécifiés dans le fichier sys.path. Il s'agit d'une liste de chaînes de caractères qui spécifie le chemin de recherche des modules.

Vous pouvez afficher le sys.path actuel en exécutant le code suivant:

import sys
print(sys.path)

Par défaut, le répertoire actuel ainsi que d'autres répertoires variés en fonction de votre installation Python sont inclus.

Vous pouvez également ajouter des répertoires à sys.path lors de l'exécution, ce qui est utile lorsque vous souhaitez importer des modules à partir d'un emplacement spécifique. Par exemple:

import sys
sys.path.append('/path/to/your/module')
from your_module import YourClass

Pour cette raison, dans l'article précédent, nous avons ajouté le chemin d'accès au module avant de l'importer:

Set sys = ##class(%SYS.Python).Import("sys")
do sys.path.append("/irisdev/app/src/python/article")
set my_module = ##class(%SYS.Python).Import("my_module")

sys.path et autres répertoires

Quels sont les autres répertoires dans sys.path? Il s'agit généralement des répertoires suivants:

  • Le répertoire contenant le script d'entrée (ou le répertoire actuel si aucun script n'est spécifié).
  • Les répertoires de la bibliothèque standard, qui contiennent les modules intégrés fournis avec Python.
  • Les répertoires site-packages contenant les packages tiers installés.

site-packages

Voici comment fonctionne site-packages. Lorsque vous installez un package à l'aide de pip, il est installé dans le répertoire site-packages, qui est automatiquement inclus dans sys.path. Cela vous permet d'importer le package sans avoir à spécifier son emplacement.

🤨🔍 Mais comment et où le répertoire site-packages est-il défini, et par qui?

Le répertoire site-packages est créé lors de l'installation de Python et se trouve généralement dans le répertoire lib de votre installation Python. Son emplacement exact dépend de votre système opérationnel et de la manière dont Python a été installé.

Par exemple, sur une installation Linux classique, le répertoire site-packages peut être accessible à l'emplacement suivant:

/usr/local/lib/python3.x/site-packages

Sous Windows, il peut se trouver à l'emplacement suivant:

C:\Python3x\Lib\site-packages

Lorsque vous installez un package à l'aide de pip, il est installé dans le répertoire site-packages, qui est automatiquement inclus dans sys.path. Cela vous permet d'importer le package sans avoir à spécifier son emplacement.

import site
print(site.getsitepackages())

🤨🔍 A quel moment et à quel endroit l'interpréteur Python lit-il le fichier site.py?

Le fichier site.py (qui se trouve dans le répertoire standard de la bibliothèque) est exécuté automatiquement au lancement de l'interpréteur Python. Il est responsable de la configuration du répertoire site-packages et de son ajout à sys.path. Ce fichier se trouve dans le répertoire standard de la bibliothèque de votre installation Python.

sys.path dans IRIS

Dans IRIS, nous avons également un fichier site.py, qui se trouve dans <installation_directory>/lib/python/iris_site.py. Ce fichier est exécuté lorsque vous démarrez ou importez un script/module dans IRIS, et il configure le sys.path pour vous.

En résumé, le fichier iris_site.py effectue les opérations suivantes:

  • il préserve le répertoire site-packages par défaut
  • il ajoute le répertoire <installation_directory>/lib/python/ à sys.path
    • c'est là que les modules Python IRIS sont situés, veuillez ne pas y placer vos modules
  • il ajoute le répertoire <installation_directory>/mgr/python/ à sys.path
    • c'est là que vous pouvez placer vos modules Python personnalisés
  • il ajoute la chaîne de configuration PythonPath à sys.path

Conclusion

Un module peut être:

  • un fichier Python (avec ou sans l'extension .py)
  • un dossier contenant un fichier __init__.py
  • un script Python (qui est également un module)
  • si vous ne réussissez pas à importer un module, vérifiez s'il se trouve dans la liste sys.path
0
0 21
Article Sylvain Guilbaud · Juil 8, 2025 3m read

Si vous migrez d'Oracle vers InterSystems IRIS, comme beaucoup de mes clients, vous risquez de rencontrer des modèles SQL spécifiques à Oracle nécessitant une conversion.

Prenons l'exemple suivant:

SELECT (TO_DATE('2023-05-12','YYYY-MM-DD') - LEVEL + 1) AS gap_date
FROM dual
CONNECT BY LEVEL <= (TO_DATE('2023-05-12','YYYY-MM-DD') - TO_DATE('2023-05-02','YYYY-MM-DD') + 1);

Dans Oracle:

  • LEVEL est une pseudo-colonne utilisée dans les requêtes hiérarchiques (CONNECT BY). Elle commence à 1 et s'incrémente de 1.
  • CONNECT BY LEVEL <= (...) détermine le nombre de lignes à générer.
  • La différence entre les deux dates plus un donne 11, donc la requête génère 11 lignes, en comptant à rebours à partir du 12 mai 2023 jusqu'au 2 mai 2023.

Répartition du résultat:

LEVEL = 1  → 2023-05-12
LEVEL = 2  → 2023-05-11
...
LEVEL = 11 → 2023-05-02

La question est maintenant de savoir comment obtenir ce résultat dans InterSystems IRIS, qui ne prend pas en charge CONNECT BY?

Une solution consiste à implémenter une requête de type SQL à l'aide d'ObjectScript qui imite ce comportement. Vous trouverez ci-dessous un exemple de définition CREATE QUERY qui accepte une date de début STARTDATE et un nombre de jours DAYS, et renvoie la liste des dates par ordre descendant.


✅ InterSystems IRIS: mise en œuvre d'une requête de l'intervalle de date

CREATE QUERY GET_GAP_DATE(IN STARTDATE DATE, IN DAYS INT)
  RESULTS (GAP_DATE DATE)
  PROCEDURE
  LANGUAGE OBJECTSCRIPT

Execute(INOUT QHandle BINARY(255), IN STARTDATE DATE, IN DAYS INT) { SET QHandle("start") = STARTDATE SET QHandle("days") = DAYS SET QHandle("level") = 1 RETURN $$$OK }

Fetch(INOUT QHandle BINARY(255), INOUT Row %List, INOUT AtEnd INT) { IF (QHandle("level") > QHandle("days")) { SET Row = "" SET AtEnd = 1 } ELSE { SET Row = $ListBuild(QHandle("start") - QHandle("level") + 1) SET QHandle("level") = QHandle("level") + 1 } RETURN $$$OK }

Close(INOUT QHandle BINARY(255)) { KILL QHandle QUIT $$$OK }

Vous pouvez exécuter la commande CREATE QUERY Vous pouvez exécuter la commande CREATE QUERY ci-dessus dans le portail IRIS System Management, ou via un outil tel que DBeaver ou un éditeur Python/Jupyter Notebook utilisant JDBC/ODBC.


🧪 Exemple d'utilisation:

Pour générer le même résultat que la requête Oracle ci-dessus, utilisez:

SELECT * FROM GET_GAP_DATE(
  TO_DATE('2023-05-12', 'YYYY-MM-DD'),
  TO_DATE('2023-05-12', 'YYYY-MM-DD') - TO_DATE('2023-05-02', 'YYYY-MM-DD') + 1
);

Cela donnera le résultat suivant:

GAP_DATE
----------
2023-05-12
2023-05-11
...
2023-05-02
(11 rows)

🔁 Utilisation avancée: Jointure avec d'autres tables

Vous pouvez également utiliser cette requête comme sous-requête ou dans des jointures:

SELECT * 
FROM GET_GAP_DATE(TO_DATE('2023-05-12', 'YYYY-MM-DD'), 11) 
CROSS JOIN dual;

Cela vous permet d'intégrer des plages de dates dans des flux SQL plus importants.


J'espère que cela sera utile à tous ceux qui sont confrontés à des scénarios de migration d'Oracle vers IRIS ! Si vous avez mis au point des solutions alternatives ou si vous proposez des améliorations, n'hésitez pas à me faire part de vos commentaires.

0
0 31
Annonce Irène Mykhailova · Juin 16, 2025

Bonjour à tous,

L'équipe Certification d'InterSystems Learning Services développe actuellement deux nouveaux examens de certification HealthShare Unified Care Record. Nous sollicitons les commentaires de notre communauté afin de nous aider à évaluer et à définir leur contenu. Veuillez noter que ces examens remplaceront l'examen de spécialiste technique HealthShare Unified Care Record, que nous prévoyons de supprimer en janvier 2026. Les certifications obtenues dans cette technologie avant la suppression de l'examen resteront valables cinq ans à compter de la date d'obtention.

0
0 22
InterSystems officiel Adeline Icard · Juin 10, 2025

InterSystems a publié de nouvelles mises à jour afin de corriger un défaut affectant les versions antérieures les plus récentes (2025.1.0, 2024.1.4, 2023.1.6 et 2022.1.7), pour les gammes de produits prises en charge suivantes :

  • InterSystems IRIS
  • InterSystems IRIS for Health
  • HealthShare Health Connect

Ce problème pouvait entraîner des erreurs <PROTECT> inattendues ou des anomalies d'accès lors de l'utilisation de fonctionnalités telles que :

0
0 33
Article Sylvain Guilbaud · Mai 9, 2025 9m read

L'indication de requête parallèle augmente les performances de certaines requêtes sur les systèmes multiprocesseurs par le biais du traitement parallèle. L'optimiseur SQL détermine les cas où cela est bénéfique. Sur les systèmes à un seul processeur, cette indication n'a aucun effet.

Le traitement parallèle peut être géré par:

  1.  Définition de l'option auto parallel pour l'ensemble du système.
    
  2. L'utilisation du mot-clé %PARALLEL dans la clause FROM de certaines requêtes.
    

Le mot clé %PARALLEL est ignoré lorsqu'il est appliqué aux requêtes suivantes:

  1. Les requêtes INSERT, UPDATE et DELETE (cette fonctionnalité ne s'applique qu'aux requêtes SELECT)
  2. Les requêtes impliquant des fonctions ou des variables spécifiques au processus
  3. Une sous-requête corrélée à une requête englobante.
  4. Une sous-requête contenant des prédicats complexes, tels que les prédicats FOR SOME et FOR SOME %ELEMENT.

En plus des raisons mentionnées précédemment, voici quelques raisons pour lesquelles le traitement parallèle des requêtes peut être ignoré:

  • Certaines requêtes complexes ne bénéficient pas d'un traitement parallèle, même si elles semblent en bénéficier au départ.
  • Certaines configurations et paramètres de base de données ne supportent pas le traitement %PARALLEL.
  • Les dépendances et les relations au sein de la structure des données peuvent empêcher une parallélisation efficace.

Dans des scénarios suivants, %PARALLEL n'effectuera pas de traitement parallèle:

  1.  La requête inclut à la fois les clauses TOP et ORDER BY, en optimisant le temps le plus rapide pour atteindre la première ligne.
    
  2.  La requête fait référence à une vue et renvoie un identifiant de vue.
    
  3.  La requête utilise des formats de stockage personnalisés ou des tables GLOBAL TEMPORARY ainsi que des tables avec un stockage de référence global étendu.
    
  4.  La requête accède à une table avec une sécurité au niveau de la ligne.
    
  5.  Les données sont stockées dans une base de données distante.
    
  6.  La collation NLS au niveau du processus ne correspond pas à la collation NLS de tous les globaux impliqués.
    

Pour plus de détails sur les options, les considérations et les restrictions, reportez-vous à Configure Parallel Query Processing (Interystems Documentation) et Specify Optimization Hints in Queries (Configuration du traitement parallèle des requêtes (Documentation Interystems) et Spécifier les conseils d'optimisation dans les requêtes). Ce sujet a été récemment exploré dans le cadre d'une discussion au sein de la Communauté de développeurs d'InterSystems (DC), qui a inspiré cet article sur IRIS, Cache et Ensemble.

InterSystems IRIS supporte le traitement parallèle à la fois pour le SQL intégré, le SQL dynamique et le SQL dans les QueryMethods. Lorsque le mot-clé %PARALLEL est utilisé dans la clause FROM d'une requête pour suggérer un traitement parallèle. L'optimiseur SQL déterminera si la requête peut bénéficier d'un traitement parallèle et l'appliquera le cas échéant.

Pour utiliser efficacement le traitement %PARALLEL dans InterSystems IRIS, plusieurs paramètres et restrictions doivent être pris en compte, tant au niveau du système qu'au niveau de la requête, afin d'en tirer tous les avantages.

Dans le cas où vous essayez d'obtenir le traitement %PARALLEL à l'aide du traitement parallèle des requêtes à l'échelle du système et que le mode adaptatif est désactivé, vous pouvez activer le traitement parallèle des requêtes à l'échelle du système via le Portail de gestion ou $SYSTEM.SQL.Util.SetOption() Exemple

USER>w ##class(%SYSTEM.SQL.Util).GetOption("AutoParallel")
0
USER>d ##class(%SYSTEM.SQL.Util).SetOption("AutoParallel",1,.oldParVal)
 
USER>w ##class(%SYSTEM.SQL.Util).GetOption("AutoParallel")
1
USER>zw oldParVal
oldParVal=0

Autres aspects importants à prendre en compte lors de la mise en œuvre de la fonctionnalité %PARALLEL.

  •   Lorsque le [AdaptiveMode](https://docs.intersystems.com/iris20242/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_AdaptiveMode) est activé, le traitement parallèle automatique est appliqué à toutes les requêtes SELECT, en les accompagnant de la mention %PARALLEL. Cependant, toutes les requêtes ne peuvent pas utiliser le traitement parallèle, car l'optimiseur SQL peut en décider autrement.
    
  •   Lorsque nous essayons d'utiliser cette fonctionnalité %PARALLEL, nous devons également prendre en compte le paramètre  [AutoParallelThreshold](https://docs.intersystems.com/iris20242/csp/docbook/Doc.View.cls?KEY=RACS_AutoParallelThreshold) (la valeur par défaut est 3200) et ce paramètre n'est pas utile dans le cas où [AutoParallel](https://docs.intersystems.com/iris20242/csp/docbook/DocBook.UI.Page.cls?KEY=RACS_AutoParallel) est désactivé.
    
  •   Le paramètre AutoParallelThreshold détermine si une requête est exécutée en parallèle, les valeurs les plus élevées réduisant les chances de traitement en parallèle. La valeur par défaut est 3200, elle peut être ajustée via $SYSTEM.SQL.Util.SetOption("AutoParallelThreshold",n,.oldval).
    
  •   Dans les environnements partagés, le traitement parallèle est utilisé pour toutes les requêtes, quel que soit le seuil, lorsque le mode adaptatif AdaptiveMode est activé.
    
  •   Lorsque le mode AdaptiveMode est activé (défini à 1) et que la fonctionnalité AutoParallel est désactivée, le Mode adaptatif remplace le paramètre AutoParallel et active le traitement parallèle.
    

Exemple: Exemple de classe avec 100 000 enregistrements remplis

 Class SQLClass.MyTest Extends (%Persistent, %Populate)
  {
    
    Property Name As %String(MAXLEN = 255);
    
    Property Age As %Integer(MAXVAL = 100, MINVAL = 1);
    
    Property Address As %String(MAXLEN = 255);
    
    Property City As %String(MAXLEN = 255);
    
    Property State As %String(MAXLEN = 255);
    
    Property Zip As %String(MAXLEN = 255);
    
    Property Country As %String(MAXLEN = 255);
    
    Property Comment As %String(MAXLEN = 255);
    
    Property Hobby As %String(MAXLEN = 255);
    
    Property JobTitle As %String(MAXLEN = 255);
    
    Property Company As %String(MAXLEN = 255);
    
    Property PhoneNumber As %String(MAXLEN = 255);
    
    Property Email As %String(MAXLEN = 255);
    
    Property Gender As %String(MAXLEN = 1);
    
    Property Ethnicity As %String(MAXLEN = 255);
    
    Property Race As %String(MAXLEN = 255);
    
    Property Religion As %String(MAXLEN = 255);
    
    Property MaritalStatus As %String(MAXLEN = 255);
    
    Property Children As %Integer(MAXVAL = 10, MINVAL = 0);
    
    Property Income As %Integer(MAXVAL = 100000, MINVAL = 0);
    
    Property Occupation As %String(MAXLEN = 255);
    
    Property Education As %String(MAXLEN = 255);
    
    Property HomePhone As %String(MAXLEN = 255);
    
    Property MobilePhone As %String(MAXLEN = 255);
    
    Property WorkPhone As %String(MAXLEN = 255);
    
    Property WorkEmail As %String(MAXLEN = 255);
    
    Property HomeEmail As %String(MAXLEN = 255);
    
    Property HomeAddress As %String(MAXLEN = 255);
    
    Property HomeCity As %String(MAXLEN = 255);
    
    Property HomeState As %String(MAXLEN = 255);
    
    Property HomeZip As %String(MAXLEN = 255);
    
    Property HomeCountry As %String(MAXLEN = 255);
    
    Property WorkAddress As %String(MAXLEN = 255);
    
    Property WorkCity As %String(MAXLEN = 255);
    
    Property WorkState As %String(MAXLEN = 255);
    
    Property WorkZip As %String(MAXLEN = 255);
    
    Property WorkCountry As %String(MAXLEN = 255);
    
    Property WorkPhoneNumber As %String(MAXLEN = 255);
    
    Property WorkMobilePhone As %String(MAXLEN = 255);
    
    Property WorkFax As %String(MAXLEN = 255);
    
    Property WorkWebsite As %String(MAXLEN = 255);
    
    Property WorkComments As %String(MAXLEN = 255);
    
    
    Index IdxAge On Age;
}

Test n° 1Exemple d'exécution sans % PARALLEL (pour afficher 10 000 enregistrements en SMP)

select * from SQLClass.MyTest where age>40
  • 3.2069 secondes
  • 10404 références globales
  • 3325407 commandes exécutées

Exemple d'exécution avec %PARALLEL(pour afficher 10 000 enregistrements dans SMP)

select * from %PARALLEL SQLClass.MyTest where age>40
  • 2.8681 secondes
  • 10404 références globales
  • 3325407 commandes exécutées

Test n° 2 :Exemple d'exécution sans % PARALLEL (pour afficher 1 enregistrement en SMP)

select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from SQLClass.MyTest where age>10
  • 0.4037 secondes
  • 46559 références globales
  • 1459936 commandes exécutées

Exemple d'exécution avec %PARALLEL (pour afficher 1 enregistrement en SMP)

select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from %PARALLEL SQLClass.MyTest where age>10
  • 0.0845 secondes
  • 46560 références globales
  • 1460418 commandes exécutées

Exemple avec SQL intégré

ClassMethod embeddedSQL() As %Status
{
    // w ##Class(SQLClass.MyTest).embeddedSQL()
    Set sc = $$$OK
    DO ClearBuffers^|"%SYS"|GLOBUFF()
    set stime=$p($zts,",",2)
    &sql(select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from SQLClass.MyTest where age>10)
    w:'SQLCODE "Without %Parallel : ",($p($zts,",",2)-stime),!
    DO ClearBuffers^|"%SYS"|GLOBUFF()
    set stime=$p($zts,",",2)
    &sql(select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from %PARALLEL SQLClass.MyTest where age>10)
    w:'SQLCODE "With %Parallel : ",($p($zts,",",2)-stime),!
    Return sc
}

Résultats (SQL intégré) : USER> D ##Class(SQLClass.MyTest).embeddedSQL() 5466 blocs supprimés Sans %Parallel : .355737 5217 blocs supprimés Avec %Parallel : .3407056

USER>

Exemple avec SQL dynamique

ClassMethod dynamicSQL() As %Status
{
     // w ##Class(SQLClass.MyTest).dynamicSQL()
    Set sc = $$$OK
    DO ClearBuffers^|"%SYS"|GLOBUFF()
    set stime=$p($zts,",",2), recCnt=0
    Set rs=##class(%ResultSet).%New()
    Set sc=rs.Prepare("select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from SQLClass.MyTest where age>10")
    Set sc=rs.Execute()
    While(rs.Next()) {
	 	w "COUNT(Children) : ",rs.GetData(1),"; MAX(Children) : ",rs.GetData(2),"; MIN(Children) : ",rs.GetData(3),"; AVG(Children) : ",rs.GetData(4),!
    }
    w "Without %Parallel : ",($p($zts,",",2)-stime),!!!
    DO ClearBuffers^|"%SYS"|GLOBUFF()
    set stime=$p($zts,",",2), recCnt=0
    Set sc=rs.Prepare("select COUNT(Children),MAX(Children),MIN(Children),AVG(Children) from SQLClass.MyTest where age>10")
    Set sc=rs.Execute()
    While(rs.Next()) {
	 	w "COUNT(Children) : ",rs.GetData(1),"; MAX(Children) : ",rs.GetData(2),"; MIN(Children) : ",rs.GetData(3),"; AVG(Children) : ",rs.GetData(4),!
	}
    w "With %Parallel : ",($p($zts,",",2)-stime),!
    Return sc
}

Résultats (SQL dynamique): USER>d ##Class(SQLClass.MyTest).dynamicSQL() 22163 blocs supprimés NOMBRE(Enfants) : 89908; MAX(Enfants) : 10; MIN(Enfants) : 0; AVG(Enfants) : 5.021989144458780086 Sans %Parallel : .4036913

5721 blocs supprimés NOMBRE(Enfants) : 89908; MAX(Enfants) : 10; MIN(Enfants) : 0; AVG(Enfants) : 5.021989144458780086 Avec %Parallel : .3693442

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

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

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

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

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

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

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

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

 

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

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

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

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

 

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

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

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

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

 

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

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

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

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

Bonus! 

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

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

0
0 36
Article Lorenzo Scalese · Août 8, 2024 3m read

Parfois, nous devons convertir le message FHIR en HL7 V2, par exemple pour enregistrer un patient dans le système PACS.
Dans cet article, les étapes à suivre pour obtenir les résultats souhaités en utilisant la production du serveur IRIS FHIR seront expliquées.

Voici les étapes à suivre:

  1. Assurez-vous que la production du serveur FHIR est démarrée.
  2. Enregistrez le service métier avec le point de terminaison FHIRServer.
  3. Définissez les processus métier pour convertir les messages FHIR en SDA, puis convertissez SDA en HL7 v2.
  4. Publiez la ressource JSON sur le point de terminaison FHIRServer et obtenez la réponse HL7 V2.

Examinons les étapes en détail.
 

Étape 1. Assurez-vous que la production du serveur FHIR est démarrée

Ouvrez la page de production et assurez-vous que la Production est démarrée. À l'étape suivante, nous devons nous assurer que le service commercial HS.FHIRServer.Interop.Service est enregistré auprès de FHIRServer


Step 2. Étape 2. Enregistrez le service métier avec le point de terminaison FHIRServer.

Depuis le portail de gestion, cliquez sur l'onglet Health (Santé)

Cliquez ensuite sur Configuration FHIR dans la liste, puis cliquez sur Server Configuration (configuration du serveur)

Sélectionnez le point de terminaison et assurez-vous que HS.FHIRServer.Interop.Service (service commercial défini dans la production) est défini sous le nom Service Config Name.

Étape 3. Définissez les processus métier pour convertir les messages FHIR en SDA, puis convertissez SDA en HL7 v2.

Définissez le rpocessus métier (le processus Solution.BP.Process est déjà défini dans l'application).
Veuillez noter que nous appliquons ici une condition selon laquelle le point de terminaison doit contenir une augmentation hl7 pour procéder à la conversion, sinon il est considéré comme une demande FHIR normale.


Transmettez ensuite le message au processus FHIR_SDA qui le convertira en SDA.
FHIR_SDA est dérivé d'une classe Solution.BP.Process définie par l'utilisateur.
Après conversion FHIR_SDA, le processus transmet le message au processus SDA_HL7
Le processus SDA_HL7 est dérivé d'une classe Solution.BP.SDATransformProcess définied  par l'utilisateur qui convertit le message SDA en message HL7 V2.


Définissez les processus de production comme suit:


Étape 4. Publiez la ressource JSON sur le point de terminaison FHIRServer et obtenez la réponse HL7 V2.

Depuis Postman, appelez le point de terminaison FHIRServer à l'aide de la méthode Post.
Notre point de terminaison FHIRServer est ci-dessous:
http://localhost:32783/csp/fhirserver/fhir/r4/hl7

REMARQUE:Notre point de terminaison FHIRServer est http://localhost:32783/csp/fhirserver/fhir/r4/, mais nous passons hl7 comme argument pour détecter à partir du processus métier de production qu'il ne s'agit pas d'une demande Post FHIR normale mais d'une demande de transformation du message FHIR.

Il s'agit de la même fonctionnalité que celle que nous pouvons utiliser à partir de nos applications Web. 
Sélectionnez la ressource patient, puis cliquez sur la ressource dans la liste, sélectionnez l'onglet HL7 FHIR ou Détails de la ressource, et cliquez sur le bouton "Transformer FHIR en HL7 V2"

L'application obtiendra le message de transformation HL7 V2 à l'aide de la production du serveur FHIR.



Transformation de HL7 V2 à FHIR

Sélectionnez "HL7 to FHIR" dans le menu et entrez les données HL7 V2. Cliquez sur le bouton de conversion pour transformer le message HL7 en message FHIR

La transformation de HL7 en FHIR utilise également la production pour convertir le message HL7 V2 en message FHIR.

Le service métier HL7_Http_Service envoie le message HL7 au processus HL7_SDA, puis HL7_SDA envoie les données SDA au processus SDA_FHIR, qui les convertit finalement en FHIR

Pour plus de détails et une révision du code, veuillez visiter la page d'application de l'échange ouvert iris-fhir-lab .
Merci!

0
0 69
Article Pierre LaFay · Mars 2, 2024 3m read

Bonjour à la communauté,

C'est mon premier message dans la communauté des développeurs - j'apprécierais tout retour d'information !

A des fins de tests ou de démonstrations, vous pouvez vouloir envoyer des emails à partir de votre production d'interopérabilité. Dans ce billet, je vais vous aider à connecter une production IRIS d'InterSystems à Gmail afin que vous puissiez l'utiliser pour envoyer des alertes par email.

Traduit avec DeepL.com (version gratuite)

1. Créer un nouveau compte Gmail

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

Le défi du Lo-Code

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Nous pouvons appeler l'UserList pour vérifier

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

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

Étape 4 – Exigences d'interopérabilité

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Étape 5 – En savoir plus: Analyse

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

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

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

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

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

Conclusion

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

1
0 99
Article Pierre LaFay · Avr 9, 2024 4m read

Introduction

Il existe une option d'assistant de procédure de liaison dans le portail de gestion (Système > SQL > Assistants > Procédure de liaison) avec laquelle j'ai eu des problèmes de fiabilité, j'ai donc décidé d'utiliser cette solution à la place.

Problème

0
0 77
Article Irène Mykhailova · Avr 22, 2022 2m read

Comment rechercher une globale contenant un string spécifique ?

Vous pouvez afficher les globales dans le Management Portal et effectuer une recherche avec Ctrl + F, mais les grandes globales peuvent prendre du temps et être difficiles à afficher.

Bien sûr, vous pouvez effectuer une boucle en utilisant les fonctions $ORDER et $QUERY pour trouver la chaîne.

Mais il existe un moyen plus simple et plus pratique.

Il s'agit d'une méthode qui utilise la fonction de recherche globale de chaîne de caractères qui peut être effectuée dans le Management Portal.

Cela peut être facilement fait dans [System Explorer]> [Globals] : Rechercher dans le Management Portal.

Par exemple, pour rechercher dans le global ^%ISCLOG une variable contenant l'erreur "Erreur CSP de nettoyage après la page" :

 ↓


Si vous voulez vraiment le faire par programmation en utilisant les fonctions $ORDER et $QUERY :

* S'il y a plusieurs indices, utilisez la fonction $QUERY pour boucler dans plusieurs couches.

set glb="^%ISCLOG"
set glb=$query(@glb@(""))
for {
  if glb="" quit
  
  if @glb [ "CSP error cleaning up after page" {
     write glb,"=",@glb,! ;; <= you can take values with @glb
  }
  set glb=$query(@glb)
}


Le résultat de l'exécution est le suivant (la routine ci-dessus est enregistrée et exécutée par test.mac).

%SYS>Do ^test
^%ISCLOG("Data",5960)=
                        CSPServerUError cleaning up after page, HALTing ...
^%ISCLOG("Data",5960,0,"$ZE")=CSP error cleaning up after page, HALTing
^%ISCLOG("Data",10046)=
                         CSPServerUError cleaning up after page, HALTing...
^%ISCLOG("Data",10046,0,"$ZE")=CSP error cleaning up after page, HALTing
^%ISCLOG("Data",13398)=
                         CSPServerUError cleaning up after page, HALTing ...
^%ISCLOG("Data",13398,0,"$ZE")=CSP error cleaning up after page, HALTing
 
%SYS>
0
0 102