#ObjectScript

0 Abonnés · 105 Publications

InterSystems ObjectScript est un langage de script permettant d'opérer avec des données en utilisant n'importe quel modèle de données de la plateforme de données InterSystems (objets, relationnel, clé-valeur, document, globales) et de développer une logique métier pour les applications côté serveur sur la plateforme de données InterSystems.

Documentation.

Article Sylvain Guilbaud · Jan 29, 2024 13m read

Nous avons un délicieux dataset avec des recettes écrites par plusieurs utilisateurs de Reddit, mais la plupart des informations sont du texte libre comme le titre ou la description d'un article. Voyons comment nous pouvons très facilement charger l'ensemble de données, extraire certaines fonctionnalités et l'analyser à l'aide des fonctionnalités du grand modèle de langage OpenAI contenu dans Embedded Python et le framework Langchain.

Chargement de l'ensemble de données

Tout d’abord, nous devons charger l’ensemble de données ou pouvons-nous simplement nous y connecter ?

Il existe différentes manières d'y parvenir : par exemple CSV Record Mapper vous pouvez utiliser dans une production d'interopérabilité ou même de belles applications OpenExchange comme csvgen.

Nous utiliserons Foreign Tables. Une fonctionnalité très utile pour projeter des données physiquement stockées ailleurs vers IRIS SQL. Nous pouvons l'utiliser pour avoir une toute première vue des fichiers de l'ensemble de données.

Nous créons un Foreign Server:

CREATE FOREIGN SERVER dataset FOREIGN DATA WRAPPER CSV HOST '/app/data/'

Et puis une table étrangère qui se connecte au fichier CSV:

CREATE FOREIGN TABLE dataset.Recipes (
  CREATEDDATE DATE,
  NUMCOMMENTS INTEGER,
  TITLE VARCHAR,
  USERNAME VARCHAR,
  COMMENT VARCHAR,
  NUMCHAR INTEGER
) SERVER dataset FILE 'Recipes.csv' USING
{
  "from": {
    "file": {
       "skip": 1
    }
  }
}

Et voilà, nous pouvons immédiatement exécuter des requêtes SQL sur dataset.Recipes: image

## De quelles données avons-nous besoin ? L’ensemble de données est intéressant et nous avons faim. Cependant, si nous voulons décider d'une recette à cuisiner, nous aurons besoin de plus d'informations que nous pourrons utiliser pour analyser.

Nous allons travailler avec deux classes persistantes (tables):

  • yummy.data.Recipe: une classe contenant le titre et la description de la recette et quelques autres propriétés que nous souhaitons extraire et analyser (par exemple Score, Difficulty, Ingredients, CuisineType, PreparationTime)
  • yummy.data.RecipeHistory: une classe simple pour enregistrer que faisons-nous avec la recette

Nous pouvons maintenant charger nos tables yummy.data* avec le contenu de l'ensemble de données:

do ##class(yummy.Utils).LoadDataset()

Cela a l'air bien, mais nous devons encore découvrir comment générer des données pour les champs Score, Difficulty, Ingredients, PreparationTime et CuisineType. ## Analyser les recettes Nous souhaitons traiter le titre et la description de chaque recette et :

  • Extraire des informations telles que Difficulté, Ingrédients, Type de Cuisine, etc.
  • Construire notre propre score en fonction de nos critères afin que nous puissions décider de ce que nous voulons cuisiner.

Nous allons utiliser ce qui suit :

  • yummy.analysis.Analysis - une structure d'analyse générique que nous pouvons réutiliser au cas où nous souhaiterions construire plus d'analyse.
  • yummy.analysis.SimpleOpenAI - une analyse qui utilise le modèle Embedded Python + Langchain Framework + OpenAI LLM.

LLM (large language models) sont vraiment un excellent outil pour traiter le langage naturel.

LangChainest prêt à fonctionner en Python, nous pouvons donc l'utiliser directement dans InterSystems IRIS en utilisant Embedded Python.

La classe complète SimpleOpenAI ressemble à ceci:

/// Analyse OpenAI simple pour les recettes
Class yummy.analysis.SimpleOpenAI Extends Analysis
{

Property CuisineType As %String;

Property PreparationTime As %Integer;

Property Difficulty As %String;

Property Ingredients As %String;

/// Run
/// Vous pouvez essayer ceci depuis un terminal :
/// set a = ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8))
/// do a.Run()
/// zwrite a
Method Run()
{
    try {
        do ..RunPythonAnalysis()

        set reasons = ""

        // mes types de cuisine préférés
        if "spanish,french,portuguese,italian,korean,japanese"[..CuisineType {
            set ..Score = ..Score + 2
            set reasons = reasons_$lb("It seems to be a "_..CuisineType_" recipe!")
        }

        // je ne veux pas passer toute la journée à cuisiner :)
        if (+..PreparationTime < 120) {
            set ..Score = ..Score + 1
            set reasons = reasons_$lb("You don't need too much time to prepare it") 
        }
        
        // bonus pour les ingrédients préférés !
        set favIngredients = $listbuild("kimchi", "truffle", "squid")
        for i=1:1:$listlength(favIngredients) {
            set favIngred = $listget(favIngredients, i)
            if ..Ingredients[favIngred {
                set ..Score = ..Score + 1
                set reasons = reasons_$lb("Favourite ingredient found: "_favIngred)
            }
        }

        set ..Reason = $listtostring(reasons, ". ")

    } catch ex {
        throw ex
    }
}

/// Mettre à jour la recette avec les résultats de l'analyse
Method UpdateRecipe()
{
    try {
        // appeler d'abord l'implémentation de la classe parent
        do ##super()

        // ajouter des résultats d'analyse spécifiques à OpenAI
        set ..Recipe.Ingredients = ..Ingredients
        set ..Recipe.PreparationTime = ..PreparationTime
        set ..Recipe.Difficulty = ..Difficulty
        set ..Recipe.CuisineType = ..CuisineType

    } catch ex {
        throw ex
    }
}

/// Exécuter une analyse à l'aide de Embedded Python + Langchain
/// do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8)).RunPythonAnalysis(1)
Method RunPythonAnalysis(debug As %Boolean = 0) [ Language = python ]
{
    # load OpenAI APIKEY from env
    import os
    from dotenv import load_dotenv, find_dotenv
    _ = load_dotenv('/app/.env')

    # account for deprecation of LLM model
    import datetime
    current_date = datetime.datetime.now().date()
    # date after which the model should be set to "gpt-3.5-turbo"
    target_date = datetime.date(2024, 6, 12)
    # set the model depending on the current date
    if current_date > target_date:
        llm_model = "gpt-3.5-turbo"
    else:
        llm_model = "gpt-3.5-turbo-0301"

    from langchain.chat_models import ChatOpenAI
    from langchain.prompts import ChatPromptTemplate
    from langchain.chains import LLMChain

    from langchain.output_parsers import ResponseSchema
    from langchain.output_parsers import StructuredOutputParser

    # init llm model
    llm = ChatOpenAI(temperature=0.0, model=llm_model)

    # prepare the responses we need
    cuisine_type_schema = ResponseSchema(
        name="cuisine_type",
        description="What is the cuisine type for the recipe? \
                     Answer in 1 word max in lowercase"
    )
    preparation_time_schema = ResponseSchema(
        name="preparation_time",
        description="How much time in minutes do I need to prepare the recipe?\
                     Anwer with an integer number, or null if unknown",
        type="integer",
    )
    difficulty_schema = ResponseSchema(
        name="difficulty",
        description="How difficult is this recipe?\
                     Answer with one of these values: easy, normal, hard, very-hard"
    )
    ingredients_schema = ResponseSchema(
        name="ingredients",
        description="Give me a comma separated list of ingredients in lowercase or empty if unknown"
    )
    response_schemas = [cuisine_type_schema, preparation_time_schema, difficulty_schema, ingredients_schema]

    # get format instructions from responses
    output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
    format_instructions = output_parser.get_format_instructions()
    
    analysis_template = """\
    Interprete and evaluate a recipe which title is: {title}
    and the description is: {description}
    
    {format_instructions}
    """
    prompt = ChatPromptTemplate.from_template(template=analysis_template)

    messages = prompt.format_messages(title=self.Recipe.Title, description=self.Recipe.Description, format_instructions=format_instructions)
    response = llm(messages)

    if debug:
        print("======ACTUAL PROMPT")
        print(messages[0].content)
        print("======RESPONSE")
        print(response.content)

    # populate analysis with results
    output_dict = output_parser.parse(response.content)
    self.CuisineType = output_dict['cuisine_type']
    self.Difficulty = output_dict['difficulty']
    self.Ingredients = output_dict['ingredients']
    if type(output_dict['preparation_time']) == int:
        self.PreparationTime = output_dict['preparation_time']

    return 1
}

}

La méthode RunPythonAnalysis c'est ici qu'entre en jeu OpenAI :). Vous pouvez le lancer directement depuis votre terminal pour une recette donnée :

do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12)).RunPythonAnalysis(1)

Nous obtiendrons un résultat comme celui-ci :

USER>do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12)).RunPythonAnalysis(1)
======ACTUAL PROMPT
                    Interprete and evaluate a recipe which title is: Folded Sushi - Alaska Roll
                    and the description is: Craving for some sushi but don't have a sushi roller? Try this easy version instead. It's super easy yet equally delicious!
[Video Recipe](https://www.youtube.com/watch?v=1LJPS1lOHSM)
# Ingredients
Serving Size:  \~5 sandwiches      
* 1 cup of sushi rice
* 3/4 cups + 2 1/2 tbsp of water
* A small piece of konbu (kelp)
* 2 tbsp of rice vinegar
* 1 tbsp of sugar
* 1 tsp of salt
* 2 avocado
* 6 imitation crab sticks
* 2 tbsp of Japanese mayo
* 1/2 lb of salmon  
# Recette     
* Place 1 cup of sushi rice into a mixing bowl and wash the rice at least 2 times or until the water becomes clear. Then transfer the rice into the rice cooker and add a small piece of kelp along with 3/4 cups plus 2 1/2 tbsp of water. Cook according to your rice cookers instruction.
* Combine 2 tbsp rice vinegar, 1 tbsp sugar, and 1 tsp salt in a medium bowl. Mix until everything is well combined.
* After the rice is cooked, remove the kelp and immediately scoop all the rice into the medium bowl with the vinegar and mix it well using the rice spatula. Make sure to use the cut motion to mix the rice to avoid mashing them. After thats done, cover it with a kitchen towel and let it cool down to room temperature.
* Cut the top of 1 avocado, then slice into the center of the avocado and rotate it along your knife. Then take each half of the avocado and twist. Afterward, take the side with the pit and carefully chop into the pit and twist to remove it. Then, using your hand, remove the peel. Repeat these steps with the other avocado. Dont forget to clean up your work station to give yourself more space. Then, place each half of the avocado facing down and thinly slice them. Once theyre sliced, slowly spread them out. Once thats done, set it aside.
* Remove the wrapper from each crab stick. Then, using your hand, peel the crab sticks vertically to get strings of crab sticks. Once all the crab sticks are peeled, rotate them sideways and chop them into small pieces, then place them in a bowl along with 2 tbsp of Japanese mayo and mix until everything is well mixed.
* Place a sharp knife at an angle and thinly slice against the grain. The thickness of the cut depends on your preference. Just make sure that all the pieces are similar in thickness.
* Grab a piece of seaweed wrap. Using a kitchen scissor, start cutting at the halfway point of seaweed wrap and cut until youre a little bit past the center of the piece. Rotate the piece vertically and start building. Dip your hand in some water to help with the sushi rice. Take a handful of sushi rice and spread it around the upper left hand quadrant of the seaweed wrap. Then carefully place a couple slices of salmon on the top right quadrant. Then place a couple slices of avocado on the bottom right quadrant. And finish it off with a couple of tsp of crab salad on the bottom left quadrant. Then, fold the top right quadrant into the bottom right quadrant, then continue by folding it into the bottom left quadrant. Well finish off the folding by folding the top left quadrant onto the rest of the sandwich. Afterward, place a piece of plastic wrap on top, cut it half, add a couple pieces of ginger and wasabi, and there you have it.

                    
Le résultat doit être un extrait de code de démarque formaté selon le schéma suivant, incluant les caractères "```json" and "```" :
json
{
        "cuisine_type": string  // Quel est le type de cuisine de la recette ? Réponse en 1 mot maximum en minuscule
        "preparation_time": integer  // De combien de temps en minutes ai-je besoin pour préparer la recette ? Répondez avec un nombre entier, ou nul si inconnu
        "difficulty": string  // À quel point cette recette est-elle difficile ? Répondez avec l'une de ces valeurs : facile, normal, difficile, très difficile
        "ingredients": string  // Donnez-moi une liste d'ingrédients séparés par des virgules en minuscules ou vide si inconnu
}

                    
======RESPONSE
json
{
        "cuisine_type": "japanese",
        "preparation_time": 30,
        "difficulty": "easy",
        "ingredients": "sushi rice, water, konbu, rice vinegar, sugar, salt, avocado, imitation crab sticks, japanese mayo, salmon"
}

Ça à l'air bon. Il semble que notre invite OpenAI soit capable de renvoyer des informations utiles. Exécutons toute la classe d'analyse depuis le terminal :

set a = ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12))
do a.Run()
zwrite a
USER>zwrite a
a=37@yummy.analysis.SimpleOpenAI  ; <OREF>
+----------------- general information ---------------
|      oref value: 37
|      class name: yummy.analysis.SimpleOpenAI
| reference count: 2
+----------------- attribute values ------------------
|        CuisineType = "japanese"
|         Difficulty = "easy"
|        Ingredients = "sushi rice, water, konbu, rice vinegar, sugar, salt, avocado, imitation crab sticks, japanese mayo, salmon"
|    PreparationTime = 30
|             Reason = "It seems to be a japanese recipe!. You don't need too much time to prepare it"
|              Score = 3
+----------------- swizzled references ---------------
|           i%Recipe = ""
|           r%Recipe = "30@yummy.data.Recipe"
+-----------------------------------------------------

## Analyser toutes les recettes ! Naturellement, vous souhaitez exécuter l’analyse sur toutes les recettes que nous avons chargées.

Vous pouvez analyser une gamme d’identifiants de recettes de cette façon :

USER>do ##class(yummy.Utils).AnalyzeRange(1,10)
> Recipe 1 (1.755185s)
> Recipe 2 (2.559526s)
> Recipe 3 (1.556895s)
> Recipe 4 (1.720246s)
> Recipe 5 (1.689123s)
> Recipe 6 (2.404745s)
> Recipe 7 (1.538208s)
> Recipe 8 (1.33001s)
> Recipe 9 (1.49972s)
> Recipe 10 (1.425612s)

Après cela, regardez à nouveau votre tableau de recettes et vérifiez les résultats.

select * from yummy_data.Recipe

image

Je pense que je pourrais essayer la pizza à la courge poivrée ou le kimchi coréen au tofu et au porc :). De toute façon, je devrai vérifier à la maison :)

Notes finales

Vous pouvez trouver l'exemple complet sur https://github.com/isc-afuentes/recipe-inspector

Avec cet exemple simple, nous avons appris à utiliser les techniques LLM pour ajouter des fonctionnalités ou analyser certaines parties de vos données dans InterSystems IRIS.

Avec ce point de départ, vous pourriez penser à :

  • Utiliser InterSystems BI pour explorer et parcourir vos données à l'aide de cubes et de tableaux de bord.
  • Créer une application Web et fournir une interface utilisateur (par exemple Angular) pour cela, vous pouvez exploiter des packages tels que RESTForms2 pour générer automatiquement des API REST pour vos classes persistantes.
  • Et pourquoi garder en base l'information indiquant les recettes que vous aimez et celles que vous n'aimez pas, puis d'essayer de déterminer si une nouvelle recette vous plaira ? Vous pourriez essayer une approche IntegratedML, ou même une approche LLM fournissant des exemples de données et construisant un cas d'utilisation RAG (Retrieval Augmented Generation).

Quelles autres choses pourriez-vous essayer ? Laissez-moi savoir ce que vous pensez!

0
0 208
Article Pierre LaFay · Jan 20, 2024 9m read

Avec l'avènement d'Embedded Python, une myriade de cas d'utilisation sont désormais possibles depuis IRIS, directement en utilisant les librairies Python pour des opérations plus complexes. L'une de ces opérations consiste à utiliser des outils de traitement du langage naturel tels que la comparaison de similarités textuelles.

Configuration de Python intégré pour utiliser la librairie de transformateurs de phrases

0
0 170
Article Pierre LaFay · Jan 13, 2024 3m read

Bienvenue à tous!

Dans ce court article, je voudrais présenter un exemple d'utilisation auquel beaucoup d'entre vous qui travaillent avec IRIS comme backend pour vos applications Web ont sûrement été confrontés à plus d'une occasion : comment envoyer un fichier à votre serveur depuis le frontend. .

0
0 54
Article Pierre LaFay · Jan 13, 2024 3m read

ZPM est un gestionnaire de packages conçu pour un déploiement pratique d'applications et de modules sur la plateforme IRIS.

Les développeurs de modules, pour que leur module soit installé à l'aide de ZPM, doivent suivre une série d'étapes simples.

  • Écrire le code du module
  • Créez un fichier module.xml qui contient la méta description du module
  • A l'aide du registre de test, publiez le module, vérifiez qu'il est publié
  • Installez le module à partir du registre de test
  • Publier le module. Pour publier dans le registre public pm.community.intersystems.com, vous devez publier le module dans https://openexchange.intersystems.com, en spécifiant l'URL github de votre package et cochez la case « Publier dans le gestionnaire de packages ».

La création manuelle d'un fichier module.xml peut être fastidieuse, donc la commande generate sera désormais créée dans zpm (à partir de la version 0.2.3).

La commande generate sert à créer module.xml pour votre projet.

Mode d'emploi:##

Exécuter zpm dans le terminal Et puis entrer « generate »

USER>zpm
zpm: USER>generate /temp/zzz

En argument (dans ce cas /temp/zzz), spécifiez le chemin d'accès au répertoire contenant votre projet. Le fichier module.xml sera créé dans ce répertoire.

Répondre ensuite aux questions : zpm: USER>generate /temp/zzz

Enter module name: my-module
Enter module version: 1.0.0 => 1.0.1
Enter module description: module description
Enter module keywords: test,zpm,docker
Enter module source folder: src => 

Existing Web Applications:
    /csp/user
    /registry
    Enter a comma separated list of web applications or * for all: /csp/user
    Enter path to csp files for /csp/user:  web
Dependencies:
    Enter module:version or empty string to continue: sslclient:*  
    Enter module:version or empty string to continue: 
zpm: USER>
  • module source folder – chemin relatif vers votre code (classes, routines), généralement src. Toutes les classes et routines de ce dossier sont chargées dans l'espace de noms actuel.
  • Si votre module inclut des applications Web, indiquez quelles applications Web de l'espace de noms actuel doivent être ajoutées à module.xml
  • Si votre module contient des dépendances, précisez le module et sa version. Utilisez * pour la dernière version.

Si vous devez ajouter des informations sur l'auteur et la licence à module.xml, utilisez le modificateur -author (-a).

zpm: USER>generate -author /temp/zzz

La commande generate prend également en charge une option différente : utilisez le modificateur -template (-t). Le module.xml est créé avec les données fictives, que vous devez modifier manuellement.

zpm: USER>generate -template /temp/zzz

Cette vidéo montre l'usage de generate.

1
1 60
Question Pierre LaFay · Jan 12, 2024

Bonjour a tous,

Dans le cadre d'un projet, j'ai mis en place à l'aide de quelques classes objectscript une solution de tests d'integration.

Son principe est basique, les tests sont rassemblés dans des suites elles mêmes incluses dans un fichier par classe ou ensemble de classes testé.

Mon outil m'affiche à l'écran le bon passage des tests et utilise des séquences d'échappement terminal pour mettre en exergue les échecs (écriture en rouge) .

Mon souci est maintenant que j'aimerai obtenir les résultat dans un fichier tcommunicable à mon client.

4
0 46
Article Pierre LaFay · Jan 6, 2024 23m read

Jusqu'à présent, nous avons expliqué comment utiliser ObjectScript pour gérer les utilisateurs, les rôles, les ressources et les applications. Il existe quelques autres classes dans ce package qui fonctionnent de manière similaire à celles mentionnées ci-dessus. Cependant, ces quatre classes sont celles que chacun devra utiliser pour gérer la sécurité de ses applications. Supposons que vous souhaitiez créer votre propre portail de gestion de la sécurité pour ce package. Il y aurait des problèmes spécifiques à prendre en compte pour une API. Étant donné que les classes utilisent des méthodes

1
0 64
Question Antoine.Dh · Déc 13, 2023

Bonjour,

Je cherche à faire un systeme de pagination en SQL et je suis tombé sur un article ici https://community.intersystems.com/post/scrollable-resultset-pagination-sample#comment-166186

J'ai essayé d'appliquer la solution, mais j'ai un bug assez particulier et je me demandais si quelqu'un pouvait m'éclairer. Sur mon environnement de dev local aucun soucis, par contre pour la même requete en environnement de preprod j'ai cette erreur:

<UNDEFINED>newvar+3^%qaqcasl *%classname

la requete que j'effectue:

SELECT * FROM (SELECT _DATA.ID, ROW_NUMBER() _RN, COUNT(*) _COUNT FROM XX.srsshiptype _DATA ) WHERE _RN BETWEEN 1 and 1000

La seule piste que j'ai serait peut-etre la une difference au niveau de la version d'IRIS, je suis en 2023.1 conteneur Ubuntu en local, et 2022.1 Windows sur l'environnement de preprod.

Mais comme la solution date de 2021 je me dit qu'elle devrait fonctionner en 2022.1

2
0 93
Annonce Irène Mykhailova · Nov 29, 2023

Bonjour la communauté,

Nous savons que vous attendez ce moment avec impatience depuis une année (et nous avons même reçu des messages demandant si cela se produira 😉)... Eh bien, le moment est maintenant là !

Rejoignez l'Advent of Code 2023 avec InterSystems et participez à notre concours ObjectScript pour avoir une chance de gagner des prix fabuleux !


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

Salut les devs,

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

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

2
1 147
Question Laurent Jouron · Oct 24, 2023

Est-il possible d'utiliser Django avec InterSystems Iris, dont les classes sont implémentées en ObjectScript ?

Class EpErp.ARTICLES Extends (%Persistent, %JSON.Adaptor, %XML.Adaptor) [ ClassType = persistent, SqlTableName = ARTICLES ]

{

Parameter IDPROPERTY = "IDARTICLES";

Property IDARTICLES As %BigInt [ SqlColumnNumber = 2, SqlFieldName = IDARTICLES ];

Property dhDateCreation As %TimeStamp [ SqlColumnNumber = 3, SqlFieldName = dhDateCreation ];

Property sCode As %String(MAXLEN = 30) [ SqlColumnNumber = 4, SqlFieldName = sCode ];

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

Salut les développeurs,

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

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

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

0
0 99
Article Iryna Mykhailova · Août 28, 2023 3m read

Bonjour les développeurs, Je suis actuellement en train de faire une démo sur la construction d'une interface utilisateur en front-end faisant de l'analyse de données et de mettre en place un test de performance avec de gros objets de données, donc l'utilisation de "Populate Utility" pourrait m'aider à générer automatiquement des échantillons de données avec lesquels je pourrais jouer.

Dans ce post, j'aimerais partager mon expérience de l'utilisation de Populate Utility, y compris l'utilisation du paramètre POPSPEC.

  1. Pour commencer, j'ai créé 2 classes persistantes pour soutenir l'utilitaire "Populate Utility" ( Extends (%Persistent, %Populate)) : popPatient qui a pour but de remplir les informations sur les patients, popSign pour simuler les données collectées à partir d'un capteur de rythme cardiaque sur le patient.   

2.1 Pour que cette démo soit plus proche de la réalité, j'aimerais ajouter la plage de valeurs des variables pour certaines propriétés en utilisant MAXVAL et MINVAL. Par exemple, vous ne pouvez pas vous attendre à ce que l'âge d'un patient soit de 1000 ans.

Faire la même chose pour le rythme cardiaque

2.2 Si nous devons utiliser une méthode de génération automatique personnalisée, il nous faut cette fois utiliser POPSPEC pour définir les valeurs générées,par exemple Nous avons des classes prédéfinies qui peuvent être référencées directement et qui génèrent des numéros de téléphone américains, mais dans mon cas, je veux générer un format qui corresponde au numéro de téléphone australien. Je souhaite également enregistrer l'heure des rythmes cardiaques collectées et créer une liste pour y placer toutes les valeurs que je souhaite générer. Tout ce qui précède doit utiliser POPSPEC pour personnaliser la génération de données à partir d'une méthode définie par l'utilisateur.

Dans ce cas, j'ai écrit deux méthodes de classe simples pour prédéfinir le format du numéro de téléphone et récupérer l'horodatage actuel en tant qu'heure de collecte du rythme cardiaque. Ensuite, j'ai ajouté le paramètre POPSPEC à la propriété correspondante

  1. Exécution de la méthode et démarrage de l'alimentation des données

Vous pouvez simplement saisir la commande suivante dans Terminal pour alimenter les données, en remplaçant number par le nombre de valeurs à alimenter.

"do ##class(Demo.popPatient).Populate( number  )"

"do ##class(Demo.popSign).Populate( number )"

Ou vous pouvez placer ces deux commandes définies dans une classMethod comme ceci, puis exécuter "do ##class(Demo.RunPopulate). StartPop ('temps pour le patient', temps pour les signes')

4.Voici un exemple de génération de 10 patients et de 50 signes de rythme cardiaque collectés

J'espère que cette explication simple pourra vous aider, Bon codage !

0
0 50
Article Iryna Mykhailova · Juil 7, 2023 2m read

Excusez si cela est évident pour les programmeurs Python, mais pour ceux qui viennent d'ObjectScript, cela peut être une astuce utile.

Lorsqu'on développe avec des commandes python.

Le test des fonctionnalités sont en cours via le shell :

$SYSTEM.Python.Shell()
 
Python 3.9.5 (default, Mar 14 2023, 06:58:44) [MSC v.1927 64 bit (AMD64)] on win32
Type quit() or Ctrl-D to exit this shell.
>>>

Lorsque Python évalue une expression dans le shell, il imprime le résultat de l'expression sur le terminal.

>>> 1 + 2

3

Il est assez facile d'évaluer et d'afficher accidentellement des valeurs

0
0 83
Article Guillaume Rongier · Juil 3, 2023 5m read

Les méthodes écrites en ObjectScript peuvent utiliser des arguments de type " passage par référence " pour renvoyer des informations à l'appelant. Python ne supporte pas les arguments de type " passage par référence ", donc Python intégré dans IRIS ne les supporte pas non plus. Voilà, c'est la fin de cet article, j'espère qu'il vous a plu 😉 Mais attendez, et le Rock & Roll Classic ?

En fait, puisque les valeurs retournées dans les arguments de méthode peuvent être utiles, cet article montre plusieurs façons de procéder, entre ObjectScript et Python intégré. Commençons donc par l'exemple le plus simple : l'appel d'une méthode ObjectScript qui dispose déjà d'un argument Python de type " passage par référence ". Voici la méthode :

ClassMethod Sum(a as %Integer, b as %Integer, Output sum as %Integer)
{
	set sum = a + b
}

Il est facile de l'appeler à partir d'ObjectScript. Il vous suffit de ne pas oublier de mettre un "." avant le troisième argument.

USER>set x = 4, y = 5
USER>do ##class(Simple.Demo).Sum(x, y, .z)
USER>write z
9

Mais vous n'êtes pas ici pour cela. Et si vous l'appeliez depuis Python ? Il faut créer un objet de référence spécial en utilisant iris.ref() et le fournir comme troisième argument. Vous ne devez pas placer un "." avant le troisième argument. (Notez que je démarre le shell Python au moyen de :py, un alias pour la fonction do $system.Python.Shell(), une fonction qui mérite un article de la communauté des développeurs pour elle-même).

USER>:py

Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Type quit() or Ctrl-D to exit this shell.
>>> x = 4
>>> y = 5
>>> z = iris.ref(0)
>>> z
<iris.ref object at 0xffff164ca3b0>

>>> iris.cls("Simple.Demo").Sum(x, y, z)
>>> z.value
9

Vous trouverez ici de la documentation sur iris.ref().

Ensuite, examinons quelques façons de permettre à une méthode Python intégré de renvoyer des valeurs dans ses arguments. Pour ces exemples, nous utiliserons des structures JSON qui peuvent être passées dans la méthode et modifiées. Après le retour de la méthode, puisque ces structures sont des références d'objet, toute modification est visible pour l'appelant. Pour JSON, Python supporte les structures dict et list, tandis qu'IRIS supporte les structures %DynamicObject et %DynamicArray. **Important : **il n'y a aucune conversion automatique des structures JSON d'ObjectScript vers les structures JSON de Python (ou vice versa) lorsqu'elles sont passées autour.

Ces options permettent plusieurs combinaisons possibles :

  • À partir de Python, créer un dict ou une liste Python et les passer dans la méthode.
  • À partir d'ObjectScript, créer un dict ou une liste Python et les passer dans la méthode.
  • À partir de Python, créer un %DynamicObject/%DynamicArray IRIS et le passer dans la méthode.
  • À partir d'ObjectScript, créer un %DynamicObject/%DynamicArray IRIS et le passer dans la méthode.

C'est parti pour le rock & roll !

Beatles() est une méthode Python qui renvoie une dict et une liste

/// modifier et renvoyer un dict et une liste
ClassMethod Beatles(membersdict As %SYS.Python, memberslist As %SYS.Python) [ Language = python ]
{
	membersdict['member1'] = 'John'
	memberslist.append('John')
	membersdict['member2'] = 'Paul'
	memberslist.append('Paul')
	membersdict['member3'] = 'George'
	memberslist.append('George')
	membersdict['member4'] = 'Ringo'
	memberslist.append('Ringo')
}

Voici un exemple d'appel à partir de Python :

USER>:py

Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Tapez quit() ou Ctrl-D pour quitter ce shell.
>>> d = {}
>>> l = []
>>> iris.cls("Simple.Demo").Beatles(d, l)
>>> d
{'member1': 'John', 'member2': 'Paul', 'member3': 'George', 'member4': 'Ringo'}
>>> l
['John', 'Paul', 'George', 'Ringo']

Voici un exemple d'appel à partir d'ObjectScript, en passant un dict vide et une liste vide créée en appelant des méthodes de Builtins() :

USER>set d = ##class(%SYS.Python).Builtins().dict()        
USER>set l = ##class(%SYS.Python).Builtins().list()
USER>do ##class(Simple.Demo).Beatles(d, l)
USER>zwrite d
d=7@%SYS.Python  ; {'member1': 'John', 'member2': 'Paul', 'member3': 'George', 'member4': 'Ringo'}  ; <oref>
USER>zwrite l
l=2@%SYS.Python  ; ['John', 'Paul', 'George', 'Ringo']  ; <oref>

Voici quelques documents sur l'utilisation des dicts et des lists à partir d'ObjectScript.

Who() est une méthode Python qui renvoie un %DynamicObject et un %DynamicArray.

/// modifier et renvoyer un %DynamicObject et un %DynamicArray
ClassMethod Who(membersObject As %DynamicObject, membersArray As %DynamicArray) [ Language = python ]
{
	import iris

	membersObject._Set('member1','Roger')
	membersArray._Push('Roger')
	membersObject._Set('member2','Pete')
	membersArray._Push('Pete')
	membersObject._Set('member3','John')
	membersArray._Push('John')
	membersObject._Set('member4','Keith')
	membersArray._Push('Keith')
}

Voici un exemple d'appel à partir de Python, en passant un %DynamicObject vide et un %DynamicArray vide créés en utilisant iris.cls() pour appeler _New(), et en affichant leur contenu à l'aide du %JSON.Formatter :

USER>:py

Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux
Tapez quit() ou Ctrl-D pour quitter ce shell.
>>> f = iris.cls("%JSON.Formatter")._New()
>>> o = iris.cls("%DynamicObject")._New()
>>> a = iris.cls("%DynamicArray")._New()
>>> o
<iris.%Library.DynamicObject object at 0xffff165c0210>
>>> a
<iris.%Library.DynamicArray object at 0xffff165c0360>
>>> f.Format(o)
{
}
>>> f.Format(a)
[
]
>>> iris.cls("Simple.Demo").Who(o, a)
>>> f.Format(o)
{
  "member1":"Roger",
  "member2":"Pete",
  "member3":"John",
  "member4":"Keith"
}
>>> f.Format(a)
[
  "Roger",
  "Pete",
  "John",
  "Keith"
]

Voici un exemple d'appel à partir d' ObjectScript:

USER>set o = {}, a = []
USER>do ##class(Simple.Demo).Who(o, a)
USER>zwrite o
o={"member1":"Roger","member2":"Pete","member3":"John","member4":"Keith"}  ; <dynamic object="">
USER>zwrite a
a=["Roger","Pete","John","Keith"]  ; <dynamic array="">

Voici quelques documents sur les objets/tableaux dynamiques.

Puisque je vois que certains parmi vous agitent les lumières de leurs téléphones portables d'avant en arrière en demandant un rappel, je vais vous dire une dernière chose : le passage d'un %DynamicObject dans une méthode Python est utile pour une autre raison, qui n'est pas liée au passage par référence. Cela vous permet d'appeler une méthode Python qui accepte des arguments nommés. Lisez tout cela ici !

0
0 57
Article Guillaume Rongier · Mai 15, 2023 4m read

Salut à tous !  

Dans cet article, j'aimerais passer en revue les extensions de VS Code que j'utilise moi-même pour travailler avec InterSystems et qui rendent mon travail beaucoup plus pratique. Je suis certain que cet article sera utile à ceux qui commencent tout juste à apprendre les technologies d'InterSystems. Cependant, j'espère également que cet article pourra être utile aux développeurs expérimentés ayant de nombreuses années d'expérience et qu'il leur ouvrira de nouvelles possibilités lors de l'utilisation de VS Code pour le développement.

Je recommande à tous ceux qui travaillent avec InterSystems d'avoir ces extensions installées et, dans cet article, je montrerai comment utiliser certaines d'entre elles.

Vous pouvez en savoir plus sur la fonctionnalité et l'utilisation de chaque extension dans la section Extensions de VS Code, où vous pouvez également télécharger, mettre à jour et désinstaller des extensions :

 Après l'installation, des icônes d'extension apparaissent sur le côté ou en bas de l'éditeur de code.

Extensions obligatoires

Je crois qu'il est logique de commencer notre voyage avec ces extensions de base, sans lesquelles il est impossible de travailler avec InterSystems dans VS Code.

  • L'extension du gestionnaire de serveur d'InterSystems pour VS Code permet de spécifier les connexions au serveur.
  • L'extension ObjectScript d'InterSystems pour VS Code permet l'écriture et la compilation des fichiers de code.
  • L'extension du serveur de langage d'InterSystems pour VS Code permet la mise en œuvre d'un serveur de langage pour ObjectScript, offrant des fonctions de coloration, de complétion de code, d'évaluation de la qualité et bien plus encore.
  • Conjointement, ces extensions offrent aux développeurs un moyen rationalisé de créer, tester et déployer des applications complètes basées sur InterSystems.

    Extensions supplémentaires

    Outre les extensions de base nécessaires, VS Code propose un grand nombre d'autres extensions. Vous pouvez écrire du code sans elles, mais leur utilisation rend le développement plus efficace lorsque vous utilisez n'importe quelle pile technologique, y compris les technologies d'InterSystems. J'en décrirai quelques-unes qui me semblent incontournables.

  • L'extension Docker rend la gestion des projets dockerisés un peu plus facile. Vous pouvez générer automatiquement un fichier Docker pour les projets, exécuter des images et gérer les conteneurs actifs.  
  • Le pilote SQLTools for InterSystems IRIS et SqlTools - sont deux extensions très pratiques qui fonctionnent conjointement. En les utilisant, vous pouvez créer et exécuter les requêtes SQL de la base de données dans VS Code sans avoir à aller dans le portail de gestion et à effectuer des requêtes sql pour interagir avec le contenu des tableaux qui s'y trouvent.
  •  

    Aujourd'hui, il est difficile d'imaginer développer un projet sans utiliser le contrôle de version. Le plus souvent, il s'agit de Git, et le code Visual Studio a un support minimal dès sa sortie de la boîte. Mais si cela ne vous suffit pas, jetez un coup d'œil aux deux extensions suivantes : 

  • Git Graph - montre les branches et leur statut de manière schématique. Ceci est utile dans les situations où vous avez besoin de comprendre rapidement l'état des branches, par exemple lorsqu'elles fusionnent.
  • Git Lens - permet de voir l'historique des modifications apportées à la ligne surlignée et à son auteur.
  •  Il est indispensable au travail d'équipe !

  • EditorConfig - une extension pour améliorer l'apparence du code, elle nécessite l'écriture d'un fichier .editorconfig, dans lequel vous pouvez spécifier tous les paramètres de formatage du code. Il est important de noter que, par défaut, cette fonctionnalité peut être mise en œuvre par l'extension du serveur de langage InterSystems pour VS Code. Pour appliquer le formatage de code ObjectScript standard dans VS Code, vous devez utiliser une combinaison de touches : Windows - [Shift + Alt + F], Mac - [Shift + Option + F], Ubuntu - [Ctrl + Shift + I]. Mais en utilisant le fichier .editorconfig, vous pouvez spécifier votre propre formatage de code pour différents fichiers au sein du projet.
  • Dans cet article, je n'ai abordé que les extensions utilisées par moi-même. Je vous serais reconnaissante de prendre le temps d'écrire dans les commentaires ce qui peut encore être utilisé pour rendre le développement plus pratique. Cet article n'en sera que plus utile !

    0
    0 297
    Article Sylvain Guilbaud · Mai 15, 2023 5m read

    Aperçu général

    En passant d'IRIS objectScript à Python, on s'aperçoit qu'il existe des différences syntaxiques fascinantes.

    L'une d'entre elles concerne la manière dont Python renvoie des tuples à partir d'une méthode à décompression automatique.

    En fait, il s'agit d'une méthode qui renvoie plusieurs valeurs. Quelle invention géniale :)

    out1, out2 = some_function(in1, in2)

    ObjectScript a une autre approche avec les paramètres ByRef et Output.

    Do ##class(some_class).SomeMethod(.inAndOut1, in2, .out2)

    Où:

    • inAndOut1 représente ByRef
    • out2 représente Output

    Le point initiale (".") devant le nom de la variable passe ByRef et pour Output.

    Le but de cet article est de décrire comment l'utilitaire communautaire PyHelper a été amélioré pour donner une façon pythonique de tirer parti des paramètres ByRef et Output. Il donne accès à %objlasterror et a une approche pour la gestion des types Python None.
     

    Exemple ByRef

    L'invocation normale pour python intégré serait :

    oHL7=iris.cls("EnsLib.HL7.Message")._OpenId('er12345')

    Lorsque cette méthode ne parvient pas à s'ouvrir, la variable "oHL7" est une chaîne vide.
    Dans la signature de cette méthode, il y a un paramètre d'état qui est disponible pour le script de l'objet qui donne une explication du problème exact.
    Par exemple :

    • L'enregistrement peut ne pas exister
    • L'enregistrement ne peut être ouvert dans le mode d'accès simultané exclusif par défaut ("1"), pendant un timeout.
    ClassMethod %OpenId(id As %String = "", concurrency As %Integer = -1, ByRef sc As %Status = {$$$OK}) As %ObjectHandle

    La méthode TupleOut peut aider à renvoyer la valeur de l'argument sc dans un contexte python.
     

    > oHL7,tsc=iris.cls("alwo.PyHelper").TupleOut("EnsLib.HL7.Message","%OpenId",['sc'],1,'er145999', 0)
    > oHL7
    ''
    > iris.cls("%SYSTEM.Status").DisplayError(tsc)
    ERROR #5809: Objet à charger introuvable, classe 'EnsLib.HL7.Message', ID 'er145999'1
    ```

    La liste ['sc'] contient un seul élément dans ce cas. Elle peut retourner plusieurs valeurs ByRef, et dans l'ordre spécifié. Ce qui est utile pour décompresser automatiquement vers les variables python prévues.

    La gestion des paramètres de sortie (Output) en cas d'exemple

    Code Python :

    > oHL7=iris.cls("EnsLib.HL7.Message")._OpenId('145')
    > oHL7.GetValueAt('&lt;%MSH:9.1')
    ''

    La chaîne renvoyée est vide, mais est-ce parce que l'élément est effectivement vide OU parce que quelque chose s'est mal passé ?
    Dans le script objet, il existe également un paramètre de sortie d'état (pStatus) auquel il est possible d'accéder pour déterminer cette condition.

    Code script objet :

    > write oHL7.GetValueAt("&lt;%MSH:9.1",,.pStatus)
    ''
    > Do $System.Status.DisplayError(pStatus)
    ERROR &lt;Ens>ErrGeneral: Aucun segment n'a été trouvé lors du trajet '&lt;%MSH'

    Avec TupleOut, la fonctionnalité équivalente peut être atteinte en renvoyant et en décompressant à la fois la valeur de retour de la méthode ET le paramètre de sortie d'état.

    Code Python :

    > hl7=iris.cls("EnsLib.HL7.Message")._OpenId(145,0)
    > val, status = iris.cls("alwo.PyHelper").TupleOut(hl7,"GetValueAt",['pStatus'],1,"&lt;&$BadMSH:9.1")
    > val==''
    True
    > iris.cls("%SYSTEM.Status").IsError(status)
    1
    > iris.cls("%SYSTEM.Status").DisplayError(status)
    ERROR &lt;Ens>ErrGeneral: Aucun segment n'a été trouvé lors du trajet '&lt;&$BadMSH'1

    Variable spéciale %objlasterror

    Dans ObjectScript, il est possible d'accéder à des variables de pourcentage dans le cadre d'une méthode.
    Dans certains cas, il est utile de détecter la variable spéciale %objlasterror ou d'y accéder après avoir appelé une API CORE ou une API de tiers.
    La méthode TupleOut permet d'accéder à %objlasterror, comme si elle avait été définie en tant que paramètre de sortie, lors de l'invocation de méthodes à partir de Python.

    > del _objlasterror
    
    > out,_objlasterror=iris.cls("alwo.PyHelper").TupleOut("EnsLib.HL7.Message","%OpenId",['%objlasterror'],1,'er145999', 0)
    
    > iris.cls("%SYSTEM.Status").DisplayError(_objlasterror)
    ERROR #5809: Objet à charger introuvable, classe 'EnsLib.HL7.Message', ID 'er145999'1

    Quand "None" n'est pas une chaîne

    TupleOut traite les références python "None" comme objectscript "undefined". Cela permet aux paramètres d'être définis par défaut et aux méthodes de se comporter de manière cohérente.
    C'est important, par exemple, dans le cas de %Persistent::%OnNew, où la méthode %OnNew n'est pas déclenchée lorsque "None" est fourni comme valeur initiale "initvalue", alors qu'elle serait déclenchée si une chaîne vide était fournie.

    En objectscript, l'implémentation pourrait être la suivante :

    do oHL7.myMethod("val1",,,"val2")

    Notez l'absence de variables entre les virgules.

    TupleOut facilite le même comportement avec :

    Python:

    iris.cls("alwo.PyHelper").TupleOut(oHL7,"myMethod",[],0,"val1",None,None,"val2")

    Une autre façon d'envisager la question est d'avoir une implémentation du code d'invocation en une seule ligne, qui se comporte de manière flexible en fonction de la configuration préalable des variables :

    Object Script:

    set arg1="val1"
    kill arg2
    kill arg3
    set arg4="val2"
    do oHL7.myMethod(.arg1, .arg2, .arg3, .arg4)

    TupleOut facilite le même comportement avec :

    Python:

    arg1="val1"
    arg2=None
    arg3=None
    arg4="val2"
    iris.cls("alwo.PyHelper").TupleOut(oHL7,"myMethod",[],0,arg1,arg2,arg3,arg4)

    Liste et dictionnaires

    Lors de la gestion des paramètres d'entrée, de ByRef et Output, TupleOut utilise la correspondance automatique de PyHelper entre: l les listes IRIS et les listes Python
    les tableaux IRIS et les tableaux Python
    Il prend soin de toujours utiliser des chaînes pour représenter les clés du dictionnaire lorsqu'il passe des tableaux IRIS aux types Dict de Python.

    Conclusion

    J'espère que cet article contribuera à inspirer de nouvelles idées et discussions sur les idées et suggestions relatives à Python intégré.

    J'espère aussi qu'il encouragera à explorer la flexibilité d'IRIS, qui peut facilement s'adapter à de nouveaux défis.

    0
    0 80
    InterSystems officiel Sylvain Guilbaud · Mars 16, 2023

    Je suis heureux d'annoncer la version 2.6.0 de l'extension VS Code ObjectScript, contenant un certain nombre d'améliorations qui facilitent la vie d'un développeur. Certains faits saillants sont décrits ci-dessous. Comme toujours, retrouvez la liste complète des modifications dans le CHANGELOG, y compris de nombreux correctifs de bogues et de vulnérabilités.

    Changez rapidement de namespace

    0
    0 138
    Article Evgeny Shvarov · Mars 8, 2023 7m read

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

    Description

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

    0
    0 64
    InterSystems officiel Sylvain Guilbaud · Fév 28, 2023 3m read

    Je voulais donner un aperçu d'une amélioration de la façon dont nous générons et appelons le code de méthode dans IRIS 2023.1.

    Une classe dans IRIS est composée de deux composants d'exécution principaux :

    1. Descripteur de classe - une liste hautement optimisée de méthodes, propriétés, paramètres de classe qui composent la classe ainsi que les attributs associés à chacun d'entre eux, par ex. cadre public/privé.
    2. Code ObjectScript : ensemble de routines contenant le code ObjectScript à exécuter lorsqu'une méthode est appelée.
    0
    0 86
    Article Iryna Mykhailova · Déc 28, 2022 1m read

    Salut les Devs !

    Pour moi, l'une des choses les plus pénibles à propos d'ObjectScript est de taper ##class(Class).Method() pour appeler une méthode de classe dans le code ou dans un terminal. J'ai même soumis une idée pour le simplifier en ObjectScript.

    Mais! Il y a une nouvelle fonctionnalité dans VSCode ObjectScript qui vient d'être introduite dans le plugin - Copy Invocation !

    Passez simplement le curseur sur le lien Copy Invocation au-dessus de chaque méthode de classe dans un code, cliquez dessus et l'invocation est copiée dans le buffer:

    Collez-le où vous voulez qu'il s'exécute !

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

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


    Les stipulations de base de cet exemple sont les suivantes :

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

      

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


    PDF Processing

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

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

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

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


    HL7 Processing

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

    === BPL ===

    Le BPL accomplit les tâches suivantes :

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

     

    === DTL ===

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

     

    === DTL Code Action ===

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

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

       Try {

         Set pdfStreamObj = pdfStreamContainer.StreamGet()

       }

       Catch {

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

          Quit

       }

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

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

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


    Sample Production

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

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

     

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

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

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

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

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

    Problème

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

    Approche

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

    Exécution

    Le code (partie 1)

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

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

     

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

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

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

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

     

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

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

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

     

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

    Comme vous pouvez le voir, nous obtenons deux traces.

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

     

    La transformée

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

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

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

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

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

    Le code (partie 2)

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

    Class Demo.Processes.MessageSplitter Extends Ens.BusinessProcess{
    
    Property TargetConfigName As Ens.DataType.ConfigName;
    Parameter SETTINGS = "TargetConfigName";
    
    Method OnRequest(pRequest As EnsLib.HL7.Message) As %Status{
    
       Set ORCCount = pRequest.GetValueAt("PIDgrpgrp(1).ORCgrp(*)")
        For i=1:1:ORCCount {
            Set contextString = ##class(Ens.StringContainer).%New()
           Set contextString.StringValue = i
           $$$QuitOnError(##Class(Demo.Transformations.R01Split).Transform(pRequest,.SplitR01,.contextString))
            $$$QuitOnError(..SendRequestAsync(..TargetConfigName,SplitR01,0))
        }
       Quit $$$OK
     }
    }
    

     

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

     

    Conclusion

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

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

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

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

    0
    0 65
    Article Kevin Koloska · Déc 8, 2022 1m read

    Lors de la création d’un PRA (Privileged Routine Application; qui d’ailleurs n’est pas pertinent uniquement pour les routines mais aussi pour les classes/méthodes), il est important de s’assurer d’inclure un new $ROLES, avant d’appeler AddRoles(). Par exemple:

     New $ROLES

    set status=$System. Security.AddRoles(« MyPrivilegedRoutineApplication »)

    De cette façon, vous vous assurez que les rôles ajoutés (élevés) « s’évaporent » pour l’utilisateur exécutant ce code, une fois que l’utilisateur est hors du champ d’application de cette routine / méthode.

    [Merci @Andreas Dieckow d’avoir validé cela]

    0
    0 57
    Article Kevin Koloska · Nov 9, 2022 5m read

    Bonjour les développeurs!

    Souvent, lorsque nous développons une bibliothèque, un outil, un package, quel qu’il soit dans InterSystems ObjectScript, nous avons une question, comment pouvons-nous déployer ce package sur la machine cible ?

    De plus, nous attendons souvent d’autres bibliothèques déjà installées, donc notre paquet dépend d’elles, et souvent d’une version spécifique de celui-ci.

    Lors de l’encodage javascript, python, etc., le rôle de l’implémentation de packages de gestion des dépendances nécessite un gestionnaire depaquets.

    C’est pourquoi j’ai le plaisir de vous annoncer que InterSystems ObjectScript Package Manager est disponible !

    0
    0 84
    Annonce Irène Mykhailova · Sept 24, 2022

    Si vous êtes sur le point d'apprendre InterSystems ObjectScript, nous rendons la décision beaucoup plus facile.

    Nous venons de mettre à jour le parcours d'apprentissage " Premiers pas avec InterSystems ObjectScript " avec 3 nouvelles vidéos de 5 minutes - et un exercice de synthèse pour vous aider à rassembler tout ce que vous apprendrez.

    🤝 Obtenez une introduction à InterSystems ObjectScript

    🤿 Plongez plus profondément dans les commandes et les fonctions

    🤔 Comprendre les types de données et les variables

    👨‍💻 Créer une définition de classe

    Faites tout cela dans notre parcours d'apprentissage mis à jour, Premiers pas avec InterSystems ObjectScript.

    0
    0 61