Article Iryna Mykhailova · Fév 19, 2025 3m read

Pour l'un de nos clients, j'ai dû intégrer le point de terminaison AFAS imageconnector /imageconnector/{imageId}?format={format}. Ce point de terminaison renvoie un message json avec l'image comme propriété de chaîne codée en base64, en plus du type MIME de l'image :

/// Objet image
Class Mycustomer.Model.AfasGet.Image Extends (%SerialObject, %XML.Adaptor, %JSON.Adaptor)
{
/// données de fichier (encodées en base64)
Property Filedata As %String(%JSONFIELDNAME = "filedata");

/// MimeType par exemple "image/jpeg"
Property MimeType As %String(%JSONFIELDNAME = "mimetype");
}

Dans la classe Message, nous avons essayé de gérer cela comme :

Property Image As Mycustomer.Model.AfasGet.Image;

/// Réponse AFAS GetImage
/// get /imageconnector/{imageId}?format={format}
Method LoadFromResponse(httpResponse As %Net.HttpResponse, caller As %String = "") As %Status
{
	Set sc = $$$OK

	If $$$LOWER($Piece(httpResponse.ContentType,";",1))="application/json",httpResponse.StatusCode = "200" {
		set ..Image = ##class(Mycustomer.Model.AfasGet.Image).%New()
		set ..status = ..Image.%JSONImport(httpResponse.Data)
	}

	Return sc
}

Tout cela a bien fonctionné jusqu'à ce qu'à un moment donné, la taille des données du fichier devienne supérieure à $$$MaxStringLength (3641144), auquel cas une MAXSTRING exception a été levée.

L'étape logique suivante consistait à modifier le type de la propriété filedata en %Stream.GlobalCharacter :

/// Objet image
Class Mycustomer.Model.AfasGet.Image Extends (%SerialObject, %XML.Adaptor, %JSON.Adaptor)
{
/// Données de fichier (encodées en base64)
Property Filedata As %Stream.GlobalCharacter(%JSONFIELDNAME = "filedata");

/// Type Mime par exemple "image/jpeg"
Property MimeType As %String(%JSONFIELDNAME = "mimetype");
}

Mais cela n'a pas fonctionné, %JSONImport() générait toujours une erreur MAXSTRING.

J'ai ensuite essayé Python, mais comme je ne suis pas un expert en Python, j'ai finalement abandonné cette voie.

Grâce à la réponse de https://community.intersystems.com/user/steven-hobbs sur https://community.intersystems.com/post/maxstring-trying-read-string-json-object#comment-250216, j'ai alors appris qu'il est possible et simple de récupérer les propriétés de la chaîne json dans un flux en utilisant image.%Get("filedata", , "stream")):

Method LoadFromResponse(httpResponse As %Net.HttpResponse, caller As %String = "") As %Status
{
	Set sc = $$$OK

	If $$$LOWER($Piece(httpResponse.ContentType,";",1))="application/json",httpResponse.StatusCode = "200" {
		set ..Image = ##class(Mycustomer.Model.AfasGet.Image).%New()
		set image = {}.%FromJSON(httpResponse.Data)
		set ..Image.MimeType = image.mimetype
		set ..Image.Filedata = ##class(%Stream.GlobalCharacter).%New()
		do ..Image.Filedata.CopyFrom(image.%Get("filedata", , "stream"))
	}

	Return sc
}

Je ne sais toujours pas comment je pourrais instruire la classe %JSON.Adaptor et utiliser la logique intégrée pour mapper vers un flux. Si quelqu'un sait comment faire cela, faites-le moi savoir !

0
0 37
Article Iryna Mykhailova · Fév 17, 2025 2m read

Les utilisateurs de CCR peuvent désormais profiter d'une syntaxe améliorée pour remplacer les jetons prédéfinis par des liens URL actifs dans les champs de texte liés à la phase. En plus du jeton <env> existant qui se met automatiquement à jour pour refléter l'environnement de l'enregistrement CCR concerné, CCR introduit désormais trois nouveaux mots-clés : <smp> , <smpPrefix>  et <homepage>.

0
0 38
Article Iryna Mykhailova · Fév 3, 2025 2m read

Bonjour ! J'ai étendu mon référentiel de démonstration, andreas5588/demo-dbs-iris, pour faciliter le test des fonctionnalités FOREIGN SERVER et FOREIGN TABLE dans IRIS.

Pour y parvenir, j'ai créé un espace de noms appelé FEDERATION. L'idée est la suivante :

  1. Configurez des connexions JDBC pour chaque espace de noms.
  2. Créez un FOREIGN SERVER dans l'espace de noms FEDERATION pour chaque connexion.
  3. Définissez une FOREIGN TABLE au moins pour une table basée sur chaque serveur étranger.

Le script :  demo-dbs-iris/src/sql/02_create_foreign_server.sql

0
0 30
Article Iryna Mykhailova · Jan 27, 2025 1m read

Dans votre production d'interopérabilité, vous pouvez toujours avoir une Business Operation qui est un client HTTP, qui utilise OAuth 2.0 pour l'authentification, mais vous avez du personnaliser l'opération pour cette méthodologie d'authentification. Depuis la version v2024.3, qui a été récemment publiée, il existe une nouvelle fonctionnalité, fournissant de nouveaux paramètres, pour gérer cela plus facilement.

Dans votre Business Operation qui utilise l'outbound adaptateur HTTP, vous trouverez de nouveaux paramètres, sous le groupe OAuth.

0
0 42
Article Iryna Mykhailova · Jan 20, 2025 3m read

Bonjour, chers collègues développeurs d'InterSystems IRIS !

On me demande souvent, notamment en ce qui concerne les bonus techniques que nous encourageons pour chaque concours Open Exchange, pourquoi nous donnons constamment des bonus pour les tests de qualité Docker, IPM et ObjectScript.

En fait, il est très facile de répondre à cette question.

7 Life Hacks Guaranteed To Make Your Life Easier - everymum

0
0 31
Annonce Iryna Mykhailova · Jan 17, 2025

Depuis quelques mois, nous avons automatisé l'analyse du code source des projets sur Github, afin que toute personne possédant un projet open source puisse faire analyser son code sans frais.

Il vous suffit de créer le fichier ".github/workflows/objectscript-quality.yml" dans votre projet avec le contenu suivant :

0
0 46
Article Iryna Mykhailova · Jan 15, 2025 1m read

Lorsque vous déployez du code à partir d'un dépôt, la suppression de classe (fichier) peut ne pas être reflétée par votre système CI/CD.
Voici une simple ligne de commande pour supprimer automatiquement toutes les classes d'un package spécifié qui n'ont pas été importées. Elle peut être facilement ajustée pour une variété de tâches annexes :

set packages = "USER.*,MyCustomPackage.*"set dir = "C:\InterSystems\src\"set sc = $SYSTEM.OBJ.LoadDir(dir,"ck", .err, 1, .loaded)
set sc = $SYSTEM.OBJ.Delete(packages _ ",'" _ $LTS($LI($LFS(loaded_",",".cls,"), 1, *-1), ",'"),, .err2)

La première commande compile les classes et renvoie également une liste des classes chargées. La deuxième commande supprime toutes les classes des packages spécifiés, à l'exception des classes chargées juste avant.

0
0 43
Article Iryna Mykhailova · Jan 13, 2025 1m read

Si vous souhaitez savoir quelle est la version exacte de votre image Docker (et depuis le dernier schéma de balisage d'image, vous ne pouvez pas simplement vous fier à la balise d'image ; et en supposant que vous ne souhaitiez pas l'exécuter réellement juste pour le savoir), vous pouvez exécuter cette commande docker :

0
0 73
Article Iryna Mykhailova · Jan 10, 2025 7m read


 

Traçage des données gnSSLocation de mon véhicule Rivian R1S à travers le Michigan avec InterSystems Cloud Document et Databricks

Si vous cherchez un cas d'utilisation pour une Document Database, j'ai compris que mon cas préféré, très simple, est la possibilité d'interroger un paquet de JSON, juste à côté de mes autres données avec sql sans vraiment faire grand-chose. C'est un rêve réalisé à partir de la puissante plateforme de données multi-modèles d'InterSystems, et montré ici dans un simple carnet pour visualiser mes données de localisation géographique que mon Rivian R1S émet pour DeezWatts ( Une aventure de données Rivian ).

Voici donc l'approche en 2 étapes, à savoir l'ingestion à et la visualisation de InterSystems Cloud Document, à l'aide du pilote document JDBC.

Déploiement de documents dans InterSystems Cloud

Pour commencer, j'ai lancé un petit déploiement de Cloud Document sur le portail InterSystems Cloud Services Portal, avec un listener activé.

J'ai téléchargé le certificat ssl, et j'ai récupéré les drivers pour JDBC ainsi que le pilote de document qui l'accompagne.

Ingestion

Pour l'ingestion, j'ai voulu comprendre comment extraire un document JSON du système de fichiers et le faire persister en tant que collection dans la base de données de documents via le listener, pour cela j'ai écrit une application Java autonome. Cette approche était plus utilitaire, car tout ce qui était amusant se passait dans le carnet une fois que les données y étaient placées.
 

 
RivianDocDB.java

Ce qui précède est assez proche de JAVA trash, mais cela fonctionne, nous pouvons voir la collection dans le navigateur de collection lors du déploiement.

Databricks

Cela demande un peu d'installation de Databricks, mais cela vaut la peine de travailler avec pyspark la partie amusante.

J'ai ajouté les deux pilotes InterSystems au cluster, et j'ai placé le certificat dans le script d'initialisation du cluster import_cloudsql_certficiate.sh afin qu'il soit ajouté au keystore.

Pour être complet, le cluster utilise Databricks 16, Spark 3.5.0 et Scala 2.12

Visualization

Nous devrions donc être prêts à exécuter une tâche PySpark et à tracer l'endroit où mon fouet a été dans le sous-ensemble de données que je traîne.

Nous utilisons l'ensemble de données de base géo geopandas and geodatasets pour une approche directe de la représentation graphique.

import geopandas as gpd
import geodatasets
from shapely.geometry import Polygon

Il faut un peu de temps pour s'y habituer, mais voici la requête adressée à InterSystems Cloud Document en utilisant la syntaxe des chemins JSON et JSON_TABLE.

dbtablequery = f"(SELECT TOP 1000 lat,longitude FROM JSON_TABLE(deezwatts2 FORMAT COLLECTION, '$' COLUMNS (lat VARCHAR(20) path '$.whip2.data.vehicleState.gnssLocation.latitude', longitude VARCHAR(20) path '$.whip2.data.vehicleState.gnssLocation.longitude' ))) AS temp_table;"

 

J'ai réussi à trouver un site qui facilite grandement la création du chemin json path @ jsonpath.com.

Ensuite, nous établissons la connexion avec le déploiement de la base de données documentaire IRIS Document Database et nous lisons les données dans un cadre de données.

# Read data from InterSystems Lecture des données de la base de données documentaire InterSystems Document Database via la requête ci-dessus
df = (spark.read.format("jdbc") \
  .option("url", "jdbc:IRIS://k8s-05868f04-a88b7ecb-5c5e41660d-404345a22ba1370c.elb.us-east-1.amazonaws.com:443/USER") \
  .option("jars", "/Volumes/cloudsql/iris/irisvolume/intersystems-document-1.0.1.jar") \
  .option("driver", "com.intersystems.jdbc.IRISDriver") \
  .option("dbtable", dbtablequery) \
  .option("sql", "SELECT * FROM temp_table;") \
  .option("user", "SQLAdmin") \
  .option("password", "REDACTED") \
  .option("connection security level","10") \
  .option("sslConnection","true") \
  .load())


Ensuite, nous prenons une carte disponible à partir de jeux de données géographiques, le jeu sdoh est parfait pour une utilisation générique des États-Unis.
 

# La carte sdoh est fantastique grâce aux boîtes de délimitation
michigan = gpd.read_file(geodatasets.get_path("geoda.us_sdoh"))

gdf = gpd.GeoDataFrame( df.toPandas(), geometry=gpd.points_from_xy(df.toPandas()['longitude'].astype(float), df.toPandas()['lat'].astype(float)), crs=michigan.crs #"EPSG:4326" )

Maintenant, la partie la plus intéressante, nous voulons zoomer sur l'endroit où nous voulons placer les points de géolocalisation de l'endroit où le R1S a roulé, pour cela nous avons besoin d'une boîte de délimitation pour l'état du Michigan.

J'ai utilisé un outil très astucieux de Keene pour dessiner la boîte de délimitation de la géo clôture et cela me donne le tableau de coordonnées!

Maintenant le tableau de coordonnées de la boîte de délimitation est en notre possession, il nous faut l'insérer dans un objet Polygone.

polygon = Polygon([
      (
        -87.286377,
        45.9664245
      ),
      (
        -81.6503906,
        45.8134865
      ),
      (
        -82.3864746,
        42.1063737
      ),
      (
        -84.7814941,
        41.3520721
      ),
      (
        -87.253418,
        42.5045029
      ),
      (
        -87.5610352,
        45.8823607
      )
    ])

 

Maintenant, traçons la piste du Rivian R1S! Il s'agit d'environ 10 000 enregistrements (j'ai utilisé l'instruction top ci-dessus pour limiter les résultats)
 

ax = michigan.clip(polygon).plot(color="lightblue", alpha=0.5,linewidth=0.8, edgecolor='gray')
ax.axis('off')
ax.annotate("Data: Rivian R1S Telemetry Data via InterSystems Document Database", xy=(0.01, .085), xycoords='figure fraction', fontsize=14, color='#555555')

gdf.plot(ax=ax, color="red", markersize=1.50, alpha=0.5, figsize=(200,200))

Et voilà, nous l'avons... Détroit, Traverse City, Silver Lake Sand Dunes, Holland, Mullet Lake, Interlachen... Le Michigan à l'état pur, à la Rivian.


 

0
0 31
Article Iryna Mykhailova · Déc 13, 2024 3m read

Comme beaucoup d'autres se retrouvent probablement, nous étions obligés de faire un mappage de données en direct dans notre moteur d'interface, ce que nous ne voulions vraiment pas faire, mais nous n'avions pas de bon choix alternatif. Nous voulons uniquement conserver les mappages aussi longtemps que nécessaire, puis purger les lignes expirées en fonction d'une valeur TTL. Nous avions en fait 4 cas d'utilisation pour cela nous-mêmes avant de créer cela. Cas d'utilisation :

0
0 38
Article Iryna Mykhailova · Déc 5, 2024 3m read

Salutations chers membres de la communauté !

J'ai récemment déployé une image IRIS for Health sur un Docker avec une image Webgateway préconfigurée et je suis tombé sur le problème des configurations SSL qui nous permettent de nous connecter à l'instance IRIS en utilisant HTTPS et en passant par notre Webgateway.

Jusqu'à présent, j'avais toujours déployé IRIS for Health avec une licence communautaire, sur laquelle le serveur Web privé était toujours installé, je n'avais donc besoin que de configurer la connexion Webgateway avec l'instance IRIS déployée :

0
0 58
Article Iryna Mykhailova · Déc 3, 2024 2m read

L'audit est une fonctionnalité essentielle pour garantir la sécurité de votre serveur. Depuis un certain temps déjà, nous avons inclus la possibilité d'auditer les instructions SQL exécutées sur le serveur.

Avec la version 2024.3 déjà disponible, nous proposons des options plus précises pour définir ces événements à auditer.

0
0 30
Article Iryna Mykhailova · Nov 28, 2024 1m read

La possibilité de renvoyer facilement des messages a toujours été une caractéristique importante de nos capacités d'interopérabilité.

Avec la sortie de la version 2024.3, nous avons rendu cela encore plus facile !

0
0 43
Article Iryna Mykhailova · Nov 18, 2024 1m read

InterSystems IRIS for Health v2024.3 est déjà disponible en tant qu'aperçu pour les développeurs depuis un certain temps, et je voulais souligner la nouvelle prise en charge liée à la recherche FHIR qui a été introduite.

Il existe deux modificateurs dont la prise en charge a été ajoutée :

0
0 31
Article Iryna Mykhailova · Nov 8, 2024 2m read

Résumé : si vous concaténez des noms de fichiers dans /STDOUT et /STDERR dans un appel $zf(-100), mettez-les entre guillemets.

J'ai rencontré une erreur <ILLEGAL VALUE> qui m'a d'abord déconcerté. Cela faisait partie d'un test unitaire qui fonctionnait parfaitement bien sous Windows, mais lorsque CI s'exécutait sur Docker, il échouait :

Set outFile = ##class(%Library.File).TempFilename()
Set outDir = ##class(%Library.File).NormalizeDirectory(##class(%Library.File).TempFilename()_"dir-out")
Do##class(%Library.File).CreateDirectoryChain(outDir)
Do$$$AssertEquals($zf(-100,"/STDOUT="_outFile_"/STDERR="_outFile,"tar","-xvf",tempDir_".tgz","-C",outDir)

Le problème, qui semble douloureusement évident une fois que vous connaissez la réponse, est que sous Linux outFile contient des barres obliques, donc elles sont interprétées comme des indicateurs de mot-clé pour $zf(-100) et bien sûr ne sont pas valides. L'erreur <ILLEGAL VALUE>
est en fait utile ici, et la solution est de mettre les noms de fichiers entre guillemets :

Set outFile = ##class(%Library.File).TempFilename()
Set outDir = ##class(%Library.File).NormalizeDirectory(##class(%Library.File).TempFilename()_"dir-out")
Do##class(%Library.File).CreateDirectoryChain(outDir)
Do$$$AssertEquals($zf(-100,"/STDOUT="""_outFile_"""/STDERR="""_outFile_"""","tar","-xvf",tempDir_".tgz","-C",outDir)
0
0 36
Article Iryna Mykhailova · Oct 28, 2024 2m read

Les fonctions d'agrégation définies par l'utilisateur sont prises en charge dans IRIS depuis 2021.1.0. J'avais souhaité une étoile pour celle-ci il y a des années avant de trouver un moyen secret et astucieux de remplacer MAX et MIN dans un type de données personnalisé, mais je n'ai pas eu l'occasion d'en essayer un jusqu'à aujourd'hui. J'ai pensé que c'était une expérience/un exemple intéressant - la question de savoir comment obtenir une médiane dans IRIS SQL s'est déjà posée une fois - donc je la partage ici sans trop de commentaires supplémentaires.

0
0 37
Article Iryna Mykhailova · Oct 15, 2024 8m read

Problèmes de chaînes

J'utilise Python pour accéder aux bases de données IRIS avec JDBC (ou ODBC). Je veux récupérer les données dans pandas dataframe pour manipuler les données et créer des graphiques à partir de celles-ci. Lors de l'utilisation de JDBC, j'ai rencontré un problème avec la gestion des chaînes. Cet article est destiné à aider les personnes qui ont les mêmes problèmes. S'il existe un moyen plus simple de résoudre ce problème, faites-le moi savoir dans les commentaires !

J'utilise OSX, donc je ne sais pas à quel point mon problème est unique. J'utilise Jupyter Notebooks, mais le code serait généralement le même si vous utilisiez n'importe quel autre programme ou cadre en Python.

Le problème JDBC

Lorsque je récupère des données de la base de données, les descriptions de colonnes et toutes les données de type chaîne sont renvoyées en tant que données de type java.lang.String. Si vous imprimez une chaîne de données, elle se présentera sous la forme suivante : "(p,a,i,n,i,n,t,h,e,r,e,a,r)" au lieu de l'attendu "painintherear".

Ceci est probablement dû au fait que les chaînes de caractères de type java.lang.String se présentent sous la forme d'un itérable ou d'un tableau lorsqu'elles sont récupérées à l'aide de JDBC. Cela peut arriver si le pont Python-Java que vous utilisez (par exemple, JayDeBeApi, JDBC) ne convertit pas automatiquement java.lang.String en une chaîne Python en une seule étape.

La représentation de la chaîne str de Python, en revanche, dispose de la chaîne entière en tant qu'unité unique. Lorsque Python récupère une chaîne normale (par exemple via ODBC), elle n'est pas divisée en caractères individuels.

La solution JDBC

Pour résoudre ce problème, vous devez vous assurer que le type java.lang.String est correctement converti en type str de Python. Vous pouvez explicitement gérer cette conversion lors du traitement des données récupérées afin qu'elles ne soient pas interprétées comme un itérable ou une liste de caractères.

Il existe de nombreuses façons de manipuler les chaînes de caractères ; c'est ce que j'ai fait.

import pandas as pd

import pyodbc

import jaydebeapi
import jpype

def my_function(jdbc_used)

    # Un autre code pour créer la connexion se trouve ici

    cursor.execute(query_string)

    if jdbc_used:
        # Récupération des résultats, conversion des données java.lang.String en str Python
        # (java.lang.String est retourné comme suit : "(p,a,i,n,i,n,t,h,e,r,e,a,r)" Conversion en type str "painintherear"
        results = []
        for row in cursor.fetchall():

            converted_row = [str(item) if isinstance(item, jpype.java.lang.String) else item for item in row]

            results.append(converted_row)


        # Obtention des noms des colonnes et vérification qu'il s'agit bien de chaînes Python

        column_names = [str(col[0]) for col in cursor.description]


        # Création du cadre de données

        df = pd.DataFrame.from_records(results, columns=column_names)


        # Vérification des résultats

        print(df.head().to_string())


    else:

        # Je testais aussi ODBC
        # Dans le cas d'ensembles de résultats très volumineux, obtenez les résultats par blocs en utilisant cursor.fetchmany() ou fetchall()
        results = cursor.fetchall()
        # Obtention des noms des colonnes
        column_names = [column[0] for column in cursor.description]
        # Création du cadre de données
        df = pd.DataFrame.from_records(results, columns=column_names)

    # Faites des choses avec votre cadre de données

Le problème ODBC

Lors d'une connexion ODBC, les chaînes de caractères ne sont pas renvoyées ou sont S.O.

Si vous vous connectez à une base de données qui contient des données Unicode (par exemple, des noms dans des langues différentes) ou si votre application doit stocker ou récupérer des caractères qui ne sont pas ASCII, vous devez vous assurer que les données restent correctement encodées lorsqu'elles sont transmises entre la base de données et votre application Python.

La solution ODBC

Ce code garantit que les données de type chaîne sont encodées et décodées en utilisant UTF-8 lors de l'envoi et de la récupération de données dans la base de données. C'est particulièrement important lorsqu'il s'agit de caractères non ASCII ou d'assurer la compatibilité avec les données Unicode.

def create_connection(connection_string, password):
    connection = None

try:
        # print(f"Connecting to {connection_string}")
        connection = pyodbc.connect(connection_string + ";PWD=" + password)

        # Veiller à ce que les chaînes de caractères soient lues correctement
        connection.setdecoding(pyodbc.SQL_CHAR, encoding="utf8")
        connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8")
        connection.setencoding(encoding="utf8")

    except pyodbc.Error as e:
        print(f"The error '{e}' occurred")

    return connection

connection.setdecoding(pyodbc.SQL_CHAR, encoding="utf8")

Désigné ci-dessus indique à pyodbc comment décoder les données de caractères de la base de données lors de la récupération des types SQL_CHAR (typiquement, les champs de caractères de longueur fixe).

connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8")

Désigné ci-dessus définit le décodage pour SQL_WCHAR, les types de caractères larges (c'est-à-dire les chaînes Unicode, telles que NVARCHAR ou NCHAR dans SQL Server).

connection.setencoding(encoding="utf8")

Désigné ci-dessus garantit que toutes les chaînes ou données de caractères envoyées de Python à la base de données seront encodées en UTF-8, cela signifie que Python traduira son type str interne (qui est Unicode) en octets UTF-8 lors de la communication avec la base de données.


La mise en place de l'ensemble

Installation de JDBC

Installation de JAVA-utiliser dmg

https://www.oracle.com/middleeast/java/technologies/downloads/#jdk23-mac

Mise à jour du shell pour définir la version par défaut

$ /usr/libexec/java_home -V
Matching Java Virtual Machines (2):
    23 (arm64) "Oracle Corporation" - "Java SE 23" /Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home
    1.8.421.09 (arm64) "Oracle Corporation" - "Java" /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
/Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home
$ echo $SHELL
/opt/homebrew/bin/bash
$ vi ~/.bash_profile

Ajoutez JAVA_HOME à votre chemin

export JAVA_HOME=$(/usr/libexec/java_home -v 23)
export PATH=$JAVA_HOME/bin:$PATH

Obtention du pilote JDBC

https://intersystems-community.github.io/iris-driver-distribution/

Placement du fichier jar quelque part... Je l'ai placé dans $HOME

$ ls $HOME/*.jar
/Users/myname/intersystems-jdbc-3.8.4.jar

Exemple de code

Cela suppose que vous avez configuré ODBC (un exemple à suivre un autre jour, le chien a mangé mes notes...).

Remarque : il s'agit d'un piratage de mon propre code réel. Notez les noms des variables.

import os

import datetime
from datetime import date, time, datetime, timedelta

import pandas as pd
import pyodbc

import jaydebeapi
import jpype

def jdbc_create_connection(jdbc_url, jdbc_username, jdbc_password):

    # Chemin d'accès au pilote JDBC
    jdbc_driver_path = '/Users/yourname/intersystems-jdbc-3.8.4.jar'

    # Il faut s'assurer que JAVA_HOME est défini 
    os.environ['JAVA_HOME']='/Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home'
    os.environ['CLASSPATH'] = jdbc_driver_path

    # Démarrage de la JVM (si elle n'est pas déjà en cours d'exécution)
    if not jpype.isJVMStarted():
        jpype.startJVM(jpype.getDefaultJVMPath(), classpath=[jdbc_driver_path])

    # Connexion aux bases de données:
    connection = None

    try:
        connection = jaydebeapi.connect("com.intersystems.jdbc.IRISDriver",
                                  jdbc_url,
                                  [jdbc_username, jdbc_password],
                                  jdbc_driver_path)
        print("Connection successful")
    except Exception as e:
        print(f"An error occurred: {e}")

    return connection


def odbc_create_connection(connection_string):
    connection = None

    try:
        # print(f"Connecting to {connection_string}")
        connection = pyodbc.connect(connection_string)

        # Veiller à ce que les chaînes de caractères soient lues correctement
        connection.setdecoding(pyodbc.SQL_CHAR, encoding="utf8")
        connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8")
        connection.setencoding(encoding="utf8")

    except pyodbc.Error as e:
        print(f"The error '{e}' occurred")

    return connection

# Paramètres

odbc_driver = "InterSystems ODBC"
odbc_host = "your_host"
odbc_port = "51773"
odbc_namespace = "your_namespace"
odbc_username = "username"
odbc_password = "password"

jdbc_host = "your_host"
jdbc_port = "51773"
jdbc_namespace = "your_namespace"
jdbc_username = "username"
jdbc_password = "password"

# Création d'une connexion et des graphiques

jdbc_used = True

if jdbc_used:
    print("Using JDBC")
    jdbc_url = f"jdbc:IRIS://{jdbc_host}:{jdbc_port}/{jdbc_namespace}?useUnicode=true&characterEncoding=UTF-8"
    connection = jdbc_create_connection(jdbc_url, jdbc_username, jdbc_password)
else:
    print("Using ODBC")
    connection_string = f"Driver={odbc_driver};Host={odbc_host};Port={odbc_port};Database={odbc_namespace};UID={odbc_username};PWD={odbc_password}"
    connection = odbc_create_connection(connection_string)


if connection is None:
    print("Unable to connect to IRIS")
    exit()

cursor = connection.cursor()

site = "SAMPLE"
table_name = "your.TableNAME"

desired_columns = [
    "RunDate",
    "ActiveUsersCount",
    "EpisodeCountEmergency",
    "EpisodeCountInpatient",
    "EpisodeCountOutpatient",
    "EpisodeCountTotal",
    "AppointmentCount",
    "PrintCountTotal",
    "site",
]

# Construction de la partie de la requête relative à la sélection des colonnes
column_selection = ", ".join(desired_columns)

query_string = f"SELECT {column_selection} FROM {table_name} WHERE Site = '{site}'"

print(query_string)
cursor.execute(query_string)

if jdbc_used:
    # Récupération des résultats
    results = []
    for row in cursor.fetchall():
        converted_row = [str(item) if isinstance(item, jpype.java.lang.String) else item for item in row]
        results.append(converted_row)

    #  Il faut récupérer les noms des colonnes et s'assurer qu'il s'agit bien de chaînes Python (java.lang.String is returned "(p,a,i,n,i,n,t,h,e,a,r,s,e)" 
    column_names = [str(col[0]) for col in cursor.description]

    # Création du cadre de données
    df = pd.DataFrame.from_records(results, columns=column_names)
    print(df.head().to_string())
else:
    # Dans le cas d'ensembles de résultats très volumineux, obtenez les résultats par blocs en utilisant cursor.fetchmany() ou fetchall()
    results = cursor.fetchall()
    # Obtention des noms des colonnes
    column_names = [column[0] for column in cursor.description]
    # Création du cadre de données
    df = pd.DataFrame.from_records(results, columns=column_names)

    print(df.head().to_string())

# # Construction des graphiques pour un site
# cf.build_7_day_rolling_average_chart(site, cursor, jdbc_used)

cursor.close()
connection.close()

# Arrêt de la JVM (si vous l'avez démarrée)
# jpype.shutdownJVM()
0
0 74
Article Iryna Mykhailova · Oct 9, 2024 4m read

L'accès à un stockage cloud Azure pour charger/télécharger des blobs est assez simple à l'aide des méthodes API de classe %Net.Cloud.Storage.Client désignées ou des adaptateurs entrants/sortants EnsLib.CloudStorage.*.

Notez que vous devez avoir le serveur de %JavaServer External Language opérationnel pour utiliser l'API ou les adaptateurs de stockage cloud, car ils utilisent tous deux le framework PEX à l'aide du serveur Java.

Voici un bref résumé :

L'accès à Azure Blob Storage s'effectue à l'aide d'une chaîne de connexion qui ressemble à celle-ci :

0
0 91
Article Iryna Mykhailova · Sept 21, 2024 4m read

Selon le rapport OWASP Top Ten de 2021, un document de référence reconnu dans le domaine de la sécurité des applications web, les injections SQL arrivent en troisième position des risques les plus critiques. Ce rapport, disponible sur OWASP Top 10: Injection, souligne la gravité de cette menace et la nécessité de mettre en place des mesures de protection efficaces.

Une injection SQL se produit lorsqu'un attaquant malveillant parvient à insérer du code SQL non autorisé dans une requête envoyée à une base de données. Ce code, dissimulé au sein des entrées utilisateur, peut alors être exécuté par la base de données, provoquant des actions indésirables comme le vol de données confidentielles, la modification ou la suppression d'informations sensibles, ou encore la perturbation du fonctionnement de l'application.

0
0 119
Article Iryna Mykhailova · Sept 16, 2024 5m read

Dans la section précédente, nous avons exploré le processus d'installation et initié l'écriture de l'IRIS en Python natif. Nous allons maintenant examiner la traversée globale et nous intéresser aux objets de classe IRIS. 

get : cette fonction est utilisée pour obtenir des valeurs à partir du nœud de traversée.

deftraversal_firstlevel_subscript():"""
    ^mygbl(235)="test66,62" and ^mygbl(912)="test118,78"
    """for  i in irispy.node('^mygbl'):
        print(i, gbl_node.get(i,''))
0
0 41
Article Iryna Mykhailova · Sept 9, 2024 4m read

Bonjour la communauté

J'ai déjà expérimenté Embedded Python dans IRIS ; cependant, je n'ai pas encore eu l'occasion d'implémenter IRIS en utilisant Native Python. Dans cet article, je souhaite décrire les étapes que j'ai suivies pour commencer à apprendre et à implémenter IRIS dans la source Python. Je tiens également à remercier @Guillaume Rongier et @Luis Angel Pérez Ramos pour leur aide dans la résolution des problèmes que j'ai rencontrés lors de ma récente installation PIP d'IRIS en Python, ce qui lui a finalement permis de fonctionner correctement.

Commençons par écrire IRIS en Python.

0
0 50
Article Iryna Mykhailova · Sept 4, 2024 2m read

Bonjour à tous,

Cet article a pour but de vous guider dans le processus de configuration et d'utilisation de la fonctionnalité d'exécution flexible de Python pour Embedded Python. Avant la version 2024.2, le programme d'installation d'Intersystems IRIS incluait une version préinstallée de Python. Vous pouvez trouver les bibliothèques Python et les fichiers d'application situés dans le répertoire \lib\python de votre dossier d'installation IRIS (par exemple, C:\InterSystems\IRIS20242\lib\python).

0
0 37
Article Iryna Mykhailova · Sept 2, 2024 2m read

Disons que je veux désinstaller IKO - tout ce que j'ai à faire est :

> helm uninstall intersystems

Ce qui se passe dans les coulisses, c'est que helm désinstallera ce qui a été installé lorsque vous avez exécuté :

> helm install intersystems <relative/path/to/iris-operator>

Dans un certain sens, c'est symétrique à ce que nous avons fait lorsque nous avons exécuté l'installation, mais avec une image différente.

Vous remarquerez que lorsque vous installez, il sait de quelle image il doit s'agir :

0
0 47
Article Iryna Mykhailova · Août 28, 2024 2m read

Récemment, je me suis retrouvé dans une situation où un utilisateur avait des rôles qui lui accordaient des rôles supplémentaires, etc.

Comme je ne comprenais pas d'où venait une autorisation particulière, j'ai écrit ce code qui obtient un ensemble initial de rôles et les décompresse de manière récursive, en tenant compte des éventuelles répétitions.

0
0 31
Article Iryna Mykhailova · Août 16, 2024 1m read

Il existe de nombreux articles communautaires intéressants concernant la « recherche vectorielle sur IRIS » et des exemples dans OpenExchange. Chaque fois que je les vois, je suis ravi de savoir que tant de développeurs essaient déjà les vecteurs sur IRIS !

Mais si vous n'avez pas encore essayé la « recherche vectorielle sur IRIS », donnez-moi une minute 😄 Je crée une classe IRIS - et avec une seule classe IRIS, vous pouvez voir comment vous placez les données vectorielles dans votre base de données IRIS et comment vous les comparez dans votre application.

0
0 41
Article Iryna Mykhailova · Juil 31, 2024 2m read

Je me suis retrouvé dans la situation peu confortable de travailler avec un système Linux sur lequel quelqu'un avait accidentellement désactivé l'accès utilisateur au shell Linux. HealthConnect était en cours d'exécution, assurant la maintenance de centaines d'interfaces. Pour résoudre le problème d'accès, cependant, nous devions arrêter l'hôte pour l'application d'un correctif.

Sans le shell, la commande iris n'est pas disponible pour contrôler l'instance, nous étions donc confrontés au risque d'arrêter le serveur de manière inélégante. Nous voulions éviter cela si possible...

0
0 68
Article Iryna Mykhailova · Juil 17, 2024 4m read

Parmi les points problématiques de la maintenance des interfaces HL7 figure la nécessité d'effectuer un test de régression fiable lors du déploiement dans de nouveaux environnements et après les mises à jour. La classe %UnitTest permet de créer des tests unitaires et de les intégrer au code de l'interface. Les données de test peuvent également être enregistrées au sein de la classe de test unitaire, ce qui permet de réaliser rapidement et facilement des tests de fumée et des tests de régression.

##Ressources:

##Scénario: Une classe de test unitaire sera créée pour chaque flux entrant en fonction des exigences en matière de profilage, de routage et de mappage des données.

  • Exigences relatives aux échantillons:*

Functional Requirements

Pour tester chaque scénario, nous devons lancer un échantillon pour chaque type d'événement et confirmer les règles de routage. Il nous faut également confirmer les exigences spécifiques en matière de mappage et trouver des exemples de données pour chaque scénario.

##Production HL7

L'objectif de ce UnitTest-RuleSet est de tester un pipeline HL7 : Service métierRègles de routageTransformation.

Dans cet exemple créé pour la production illustrée ci-dessous, le flux de processus ADT passe par plusieurs sauts supplémentaires. Une classe étendant UnitTest.RuleSet.HL7 a été modifiée pour permettre de traiter des flux de processus plus complexes.

Flux de processus: FromHSROUTER.TESTAdtFromTESTAdt.ReorgSegmentProcessRouteTESTAdtTEST.ADTTransformHS.Gateway.HL7.InboundProcess

HL7 Production

##Création d'une Classe de Test Unitaire

Créez une classe TestAdtUnitTest qui étend UnitTest.RuleSet.HL7. (UnitTest.RuleSet.Example dans le référentiel UnitTest-RuleSet contient un exemple d'une telle classe.

*Remarque: Dans cet exemple, tout a été déplacé sous HS.Local dans HSCUSTOM pour faciliter les tests. *

Signature:Class HS.Local.EG.UnitTest.TestAdtUnitTest Extends HS.Local.Util.UnitTest.RuleSet.HL7

##Aperçu de la structure des classes

La classe de test unitaire est organisée de la manière suivante:

Unit Test Class Layout

##Paramètres de configuration

Ces paramètres de classe sont utilisés pour configurer les tests.

// Espace de noms où la production est située
Parameter Namespace = "EGTEST";

// Répertoire de base pour les tests unitaires

Parameter TestDirectory = "/tmp/unittest";

// nom du sous-répertoire pour les tests unitaires

Parameter TestSuite = "TEST-HL7ADT";

/// Remplacement pour un schéma différent
Parameter HL7Schema = "2.5.1:ADT_A01";

/// Remplacement par le nom du service existant en production
Parameter SourceBusinessServiceName = "FromHSROUTER.TESTAdt";

/// Remplacer par le nom du moteur de routage de processus métier existant en production
Parameter TargetConfigName = "FromTESTAdt.ReorgSegmentProcess";

/// Nom du processus de routage primaire en production
Parameter PrimaryRoutingProcessName = "RouteTESTAdt";

/// Nom du processus de routage secondaire en production
Parameter SecondaryRoutingProcessName = "TEST.ADTTransform";

Remarque: Les processus de routage primaire et secondaire sont référencés dans les différentes méthodes de test afin de tester la sortie et les résultats de deux processus de routage enchaînés.

##Création d'exemples de messages d'entrée

Pour créer une classe de test unitaire réutilisable, il faut enregistrer des échantillons anonymes pour chaque type d'événement et tout message supplémentaire nécessaire à la réalisation des scénarios de test dans la classe de test unitaire, dans des blocs XDATA au bas du fichier.

  • Exemple du bloc XDATA:*

Le nom *SourceMessageA01 * est utilisé pour référencer le bloc XDATA spécifique.

XData SourceMessageA01 { <![CDATA[ MSH|^~\&|Epic|TEST||TEST|20230911060119|RUBBLE|ADT^A01|249509431|P|2.2 EVN|A01|20230911060119||ADT_EVENT|RUBBLE^MATTHEW^BARNEY^D^^^^^TEST^^^^^UH|20230911060000|PHL^PHL^ADTEDI ZVN||LS23450^LS-23450^^^^RTL PID|1|000163387^^^MRN^MRN|000163387^^^MRN^MRN||FLINTSTONE^ANNA^WILMA||19690812|F|MURRAY^ANNA~FLINTSTONE^ANNA^R~FLINTSTONE^ANNA|B|100 BEDROCK WAY^^NORTH CHARLESTON^SC^29420-8707^US^P^^CHARLESTON|CHAR|(555)609-0969^P^PH^^^555^6090969~^NET^Internet^ANNAC1@YAHOO.COM~(555)609-0969^P^CP^^^555^6090969||ENG|M|CHR|1197112023|260-61-5801|||1|||||Non Veteran|||N ZPD||MYCH||AC|||N||N PD1|||TEST HOLLINGS CANCER CENTER^^10003|1134107873^LINK^MICHAEL^J^^^^^EPIC^^^^PNPI ROL|1|UP|GENERAL|1134107873^LINK^MICHAEL^J^^^^^EPIC^^^^PNPI|20211115 NK1|1|GABLE^BETTY|PARENT||(555)763-5651^^PH^^^555^7635651||Emergency Contact 1 NK1|2|FLINTSTONE^FRED|Spouse|100 Bedrock way^^REMBERT^SC^29128^US|(888)222-2222^^PH^^^888^2222222|(888)222-3333^^PH^^^888^2223333|Emergency Contact 2 PV1|1|O|R1OR^RTOR^07^RT^R^^^^TEST RT OR|EL|||1386757342^HALSTEAD^LUCINDA^A.^^^^^EPIC^^^^PNPI|1386757342^HALSTEAD^LUCINDA^A.^^^^^EPIC^^^^PNPI||OTO||||PHYS|||1386757342^HALSTEAD^LUCINDA^A.^^^^^EPIC^^^^PNPI|SO||BCBS|||||||||||||||||||||ADMCONF|||20230911060000 PV2||PRV||||||20230911||||HOSP ENC|||||||||N|N||||||||||N ZPV||||||||||||20230911060000 OBX|1|NM|PRIMARYCSN|1|1197112023||||||F AL1|1|DA|900525^FISH CONTAINING PRODUCTS^DAM|3|Anaphylaxis|20210823 AL1|2|DA|568^PEANUT^HIC|3|Anaphylaxis|20221209 AL1|3|DA|12753^TREE NUT^HIC|3|Anaphylaxis|20221209 AL1|4|DA|1193^TREE NUTS^DAM|3|Anaphylaxis|20130524 AL1|5|DA|1554^HYDROCODONE^HIC||Other|20210728 AL1|6|DA|3102^POLLEN EXTRACTS^HIC||Other|20201204 AL1|7|DA|11754^SHELLFISH DERIVED^HIC||Other|20210728 DG1|1|I10|Q85.02^Neurofibromatosis, type 2^I10|Neurofibromatosis, type 2||ADMISSION DIAGNOSIS (CODED) DG1|2|I10|D33.3^Benign neoplasm of cranial nerves^I10|Benign neoplasm of cranial nerves||ADMISSION DIAGNOSIS (CODED) DG1|3|I10|J38.01^Paralysis of vocal cords and larynx, unilateral^I10|Paralysis of vocal cords and larynx, unilateral||ADMISSION DIAGNOSIS (CODED) DG1|4||^NF2 (neurofibromatosis 2) [Q85.02]|NF2 (neurofibromatosis 2) [Q85.02]||ADMISSION DIAGNOSIS (TEXT) DG1|5||^Acoustic neuroma [D33.3]|Acoustic neuroma [D33.3]||ADMISSION DIAGNOSIS (TEXT) DG1|6||^Unilateral complete paralysis of vocal cord [J38.01]|Unilateral complete paralysis of vocal cord [J38.01]||ADMISSION DIAGNOSIS (TEXT) GT1|1|780223|FLINTSTONE^ANNA^WILMA^^^^L||100 BEDROCK WAY^^NORTH CHARLESTON^SC^29420-8707^US^^^CHARLESTON|(555)609-0969^P^PH^^^555^6090969~(555)763-5651^P^CP^^^555^7635651||19690812|F|P/F|SL|248-61-5801|||||^^^^^US|||Full ZG1||||1 IN1|1|BL90^BCBS/STATE EMP^PLANID||BCBS STATE|ATTN CLAIMS PROCESSING^PO BOX 100605^COLUMBIA^SC^29260-0605||(800)444-4311^^^^^800^4444311|002038404||||20140101||NPR||FLINTSTONE^THOMAS^^V|Sp|19661227|3310 DUBIN RD^^NORTH CHARLESTON^SC^29420^US|||1|||||||||||||1087807|ZCS49984141|||||||M|^^^^^US|||BOTH IN3|1|||2||20230911|20230911|RUBBLE^MATTHEW^BARNEY^D|||NOT|||||(800)999-0000^^^^^800^9990000~(888)444-5555^^^^^888^4445555 ZIN|||||||FLINTSTONE^THOMAS^^V|||||16871492 ]]> }

Exemple de code permettant d'extraire des données d'un bloc XDATA pour les utiliser dans le cadre de tests :

set xdata=##class(%Dictionary.CompiledXData).%OpenId(..%ClassName(1)_"||"_XDataName,0)

Voici un exemple de code de ** GetMessage**, une méthode d'assistance utilisée pour lire et renvoyer les données d'un bloc XDATA en fonction du nom du bloc.

ClassMethod GetMessage(XDataName As %String) As EnsLib.HL7.Message { #dim SourceMessage as EnsLib.HL7.Message set xdata=##class(%Dictionary.CompiledXData).%OpenId(..%ClassName(1)_"||"XDataName,0) quit:'$IsObject(xdata) $$$NULLOREF set lines="" while 'xdata.Data.AtEnd { set line=$ZSTRIP(xdata.Data.ReadLine(),"<w") continue:line="" continue:$Extract(line,1)="<" // ignorer les balises XML ouvrantes ou fermantes et commencer la balise CData continue:$Extract(line,1)="]" // ignorer ]]> closing CDATA set lines=lines($S($L(lines)=0:"",1:$C(..#NewLine)))_line } set SourceMessage=##class(EnsLib.HL7.Message).ImportFromString(lines,.tSC) quit:$$$ISERR(tSC) $$$NULLOREF set SourceMessage.DocType=..#HL7Schema set tSC=SourceMessage.PokeDocType(..#HL7Schema) quit SourceMessage }

##Création de méthodes de test

La classe de test unitaire contient également une méthode de test qui configure chaque test de manière programmatique, injecte le message dans la production et vérifie que le message transformé qui en résulte correspond à ce qui est attendu.

L'exemple de test contient les méthodes de test suivantes :

  • TestMessageA01
  • TestMessageA02
  • TestMessageA03
  • TestMessageA04
  • TestMessageA05
  • TestMessageA06
  • TestMessageA08
  • TestMessageA28
  • TestMessageA31
  • TestCorrectAssigningAuthorityForA01
  • TestEncoutnerNumberPresent
  • TestPD1LocationMapped

Exemple de méthode: TestMessageA01 Méthode permettant de vérifier que les messages A01 sont traités sans erreur et routés vers la transformation correcte.

Method TestMessageA01() { Set ReturnA01 = ..#SecondaryRoutingProcessName_":HS.Local.EG.ProfSvcs.Router.Base.ADT.TransformA01" #dim message as EnsLib.HL7.Message

    // Télécharger un nouveau message HL7 par UnitTest
set ..HL7Message=..GetMessage("SourceMessageA01")
quit:'$IsObject(..HL7Message) $$$ERROR(5001,"Failed to correlate Xdata for Source Message")

set routingProcess = ..#PrimaryRoutingProcessName

set message=..HL7Message.%ConstructClone(1)
do message.PokeDocType(message.DocType)
do message.SetValueAt("SYSA","MSH:3.1")
set expectSuccess=1
set expectReturn="send:"_ReturnA01
set expectReason="rule#8"
do ..SendMessageToRouter(message,"TestMessageA01",routingProcess, expectSuccess, expectReturn, expectReason)

}

Remarque: SendMessageToRouter() est une méthode mise en œuvre dans le paquet UnitTest-RuleSet.

Exemple de méthode: TestEncounterNumberPresent

La méthode ci-dessous vérifie la présence de valeurs spécifiques dans le message après la transformation.

Method TestEncounterNumberPresent()
{
	#dim message as EnsLib.HL7.Message

    // Télécharger un nouveau message HL7 par UnitTest
set ..HL7Message=..GetMessage("SourceMessageA01")
quit:'$IsObject(..HL7Message) $$$ERROR(5001,"Failed to correlate Xdata for Source Message")

// source du processus de transformation
set routingProcess = ..#SecondaryRoutingProcessName
set message=..HL7Message.%ConstructClone(1)
do message.PokeDocType(message.DocType)
do message.SetValueAt("SYSA","MSH:3.1")
// Vérifier que la sortie PV1:19 n'est pas vide
set expectSuccess=1
set expectElement="PV1:19"
set expectReturnVal="1197112023"
do ..SendMessageReturnOutput(message,"TestMessageA01", routingProcess, expectSuccess, expectElement, expectReturnVal)
 }

Remarque: SendMessageReturnOutput() est une méthode modifiée pour renvoyer le message résultatif pour évaluation.

##Comment exécuter le UnitTest

Les mises en œuvre de la classe %UnitTest dépend de la globale ^UnitTestRoot pour l'emplacement des dossiers de travail des tests unitaires.

Il y a deux paramètres utilisés pour définir les dossiers:

 // Répertoire de base pour les tests unitaires
 Parameter TestDirectory = "/tmp/unittest";

 // nom du sous-répertoire pour les tests unitaires
 Parameter TestSuite = "TEST-HL7ADT";

Dans ce cas, la classe s'attend à ce que le dossier : /tmp/unittest/TEST-HL7ADT soit présent dans l'environnement où la classe de test unitaire est exécutée.

En outre, la mise en œuvre personnalisée définit la globale ^UnitTestRoot et passe à l'espace de noms spécifique dans le paramètre d'espace de noms Namespace.

Pour le lancement depuis le terminal:

HSCUSTOM> do ##class(HS.Local.EG.UnitTest.TestAdtUnitTest).Debug()

Les résultats du lancement seront envoyés à la console. Si l'un des tests échoue, tout le test est considéré comme ayant échoué. Outre le terminal, on peut également consulter les résultats à partir du portail de gestion. Pour ouvrir l'URL, utilisez le lien figurant dans le résultat.

##Extrait du résultat de la Console

TestMessageA31 passed TestPD1LocationMapped() begins ... 14:50:20.104:...t.TestAdtUnitTest: TestPD1Location Sent LogMessage:SessionId was 130 LogMessage:Source Config Name name is :TEST.ADTTransform LogMessage:Message Body Id was 94 LogMessage:Expect Success is 0 LogMessage:Testing for value PV1:39 LogMessage:Found value AssertNotEquals:Expect No Match:TestPD1Location:ReturnValue= (failed) <<==== FAILED TEST-HL7ADT:HS.Local.EG.UnitTest.TestAdtUnitTest:TestPD1LocationMapped LogMessage:Duration of execution: .033106 sec. TestPD1LocationMapped failed HS.Local.EG.UnitTest.TestAdtUnitTest failed Skipping deleting classes TEST-HL7ADT failed

Use the following URL to view the result: http://192.168.1.229:52773/csp/sys/%25UnitTest.Portal.Indices.cls?Index=3&$NAMESPACE=EGTEST Some tests FAILED in suites: TEST-HL7ADT

##Affichage des résultats dans le portail de gestion

Utilisez l'URL affichée sur le terminal et copiez-la dans un navigateur pour voir les résultats. En cliquant sur le nom du test, vous obtiendrez des détails supplémentaires.

Unit Test in Management Portal

En cliquant sur le lien de chaque test, vous obtiendrez plus d'informations sur le test :

UnitTest Results

##Conclusion La création de classes de tests unitaires pour chaque interface HL7 et leur intégration dans le code de l'interface permettent de documenter le processus de test et les échantillons de données, ainsi que de tester rapidement la régression de toute mise à jour ou modification des données ou de leur mise en œuvre.

Lors du déploiement, la classe de test unitaire sert de test de fumée autonome pour confirmer que l'interface est complète et fonctionnelle.

##Considérations supplémentaires

  • Le cadre UnitTest_RuleSet peut être adapté aux tests unitaires du CCDA. Les tests de routage sont relativement semblables à l'exemple HL7. Pour répondre aux exigences de mappage, il faut mettre en œuvre la classe de test unitaire pour gérer XSLT et XPaths.

  • Les responsables de l'assurance qualité peuvent recueillir des exigences et des échantillons de données pour configurer la classe de test unitaire avant le début de la mise en œuvre.

  • Les tests unitaires pour toutes les interfaces peuvent être regroupés dans le gestionnaire de tests unitaires (Unit Test Manager), de sorte que l'ensemble des tests peut être exécuté en une seule fois pour valider la configuration et le code existants.

0
0 50