0 Abonnés · 22 Publications

Un outil de programmation ou outil de développement logiciel est un programme informatique que les développeurs logiciel utilisent pour créer, déboguer, maintenir, ou autrement prendre en charge d'autres programmes et applications.

Article Sylvain Guilbaud · Oct 29, 2025 14m read

Aperçu

Cette interface Web est conçue pour faciliter la gestion des tables de recherche de données (Data Lookup Tables) via une page Web conviviale. Elle est particulièrement utile lorsque les valeurs de votre table de recherche sont volumineuses, dynamiques et changeantes. Les utilisateurs finaux peuvent gérer efficacement les données de la table de recherche en fonction de leurs besoins grâce à un accès contrôlé à cette interface Web (autorisations de lecture, d'écriture et de suppression limitées à cette page).

Les données gérées via cette interface peuvent être utilisées de manière transparente pour les règles HealthConnect ou les transformations de données, ce qui élimine le besoin d'une surveillance et d'une gestion manuelles constantes des tables de recherche et permet ainsi un gain de temps considérable.

Remarque:
Si la table de recherche de données classique ne répond pas à vos besoins en matière de mappage, vous pouvez créer une table personnalisée et adapter cette interface Web ainsi que sa classe de support avec un minimum de modifications. Un exemple de code de classe est disponible sur demande.

Conditions préalables

Avant d'utiliser l'interface Web, veillez à avoir créé une table de recherche de données vide dans HealthConnect:

  1. Accédez à Interoperability → Configure → Data Lookup Tables.
  2. Cliquez sur New pour ouvrir la fenêtre contextuelle "Create New Lookup Table" (Créer une nouvelle table de recherche).
  3. Saisissez le nom souhaité pour votre table de recherche et cliquez sur OK.
  4. Cliquez sur Save pour finaliser la création de la table.
  5. Vérifiez la création de la table de recherche en cliquant sur Open dans la même fenêtre. Votre nouvelle table devrait apparaître dans la liste.

Dans cette documentation, la table de recherche de données utilisée à titre d'exemple est intitulée:
"Exemple de table de recherche de données"

Mode d'emploi

Le processus de configuration est simple:

  1. Obtention du code:Téléchargez le code fourni à partir du référentiel GitHub ou copiez-le à partir du lien communautaire.
  2. Création d'une page CSP:Créez une nouvelle page CSP dans votre environnement HealthConnect et insérez tout le code du fichier DataLookupWebApp.
  3. Création d'une classe:Copiez et insérez le code de classe fourni dans la documentation ou dans le référentiel.

Configuration

Avant la compilation et l'exécution:

  1. Indiquez le nom de votre table de recherche de données sur la page Web en remplaçant l'espace réservé indiqués par le cadre rouge dans la capture d'écran ci-dessous par le nom réel de votre table de recherche.

  1. Vérifiez si le nom de la classe dans le code correspond au nom de votre classe personnalisée.

  1. Attribuez un nom à votre table de recherche dans la classe, comme indiqué par myDtName = 'votre table de recherche'. Consultez la capture d'écran ci-dessus.
  1. Compilez la classe et la page CSP pour finaliser la configuration.

Mise à l'essai de l'application

Ajout d'enregistrements

  • Saisissez la clé et la paire clé-valeur correspondante dans les champs du formulaire.
  • Cliquez sur Add Record pour insérer les données dans la table de recherche.

Capture d'écran 1: Ajout d'enregistrements (122 – Radiographie de la colonne vertébrale)

Mise à jour de l'enregistrement

  • Sélectionnez un enregistrement existant et modifiez la paire clé-valeur si nécessaire.

  • Cliquez sur Update pour enregistrer les modifications. Exemple : Modification de  122 "Radiographie de la colonne vertébrale" en "Radiographie abdominale".

Suppression de l'enregistrement

  • Sélectionnez un enregistrement à supprimer (144 – IRM)

  • Cliquez sur le bouton Delete pour supprimer l'enregistrement de la table de recherche. Exemple : Suppression de "144 - IRM".

Vérification de la table de recherche de données dans HealthConnect

Accédez à Interoperability → Configure → Data Lookup Tables à Open

Recherche de l'enregistrement

Code / Ressources

  • La version complète du code source est également disponible pour téléchargement et consultation sur GitHub.

https://github.com/pandeySR/Reusable-Web-Interface-for-Data-Lookup-Table

<!DOCTYPE html>
<!--
This web application is designed and developed to insert, update, and delete records (key–value pairs) in the Data Lookup Table through a web interface.
Author: Sanjib Pandey
Date: 16/10/2025
-->
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="author" content="Sanjib Pandey" />
  <title>YOUR TITLE : Data Lookup Table - Data Record </title>
  <!-- Bootstrap CSS CDN, jQuery, Datatable -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> 
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"> 
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
  <link rel="stylesheet" href="https://cdn.datatables.net/1.13.5/css/dataTables.bootstrap5.min.css" />
  <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
  <script src="https://cdn.datatables.net/1.13.5/js/jquery.dataTables.min.js"></script>
  <script src="https://cdn.datatables.net/1.13.5/js/dataTables.bootstrap5.min.js"></script>
  <style>
    h2 {
      text-align: center;
      color: #fff;
      background-color: #00b300;
      padding: 15px 10px;
      border-radius: 5px;
      margin-bottom: 5px;
      margin-top: 0px;
    }
    html, body {
      height: 100%;
      margin: 0;
    }
    body {
      display: flex;
      min-height: 100vh;
      background-color: #f8f9fa;
      padding-top: 10px;
      flex-direction: column;
      align-items: center;
    }
    .container {
      max-width: 1100px;
      background: white;
      padding: 20px;
      border-radius: 8px;
      box-shadow: 0015px rgba(0,0,0,0.2);
      margin-top: 5px;
      width: 100%;
    }
 
    .table-scroll-vertical
    {
 	 max-height: 450px;
 	 overflow-y: auto;
  	 border: 1px solid #dee2e6;
  	 border-radius: 5px;
  	 margin-top: 0; 
	}
	
	.small-italic 
	{
  	 font-size: 0.85em; 
  	 font-style: italic; 
  	 color: #C0C0C0; 
	}
	
	.dataTables_filter label 
	{
  	 font-weight: bold;
  	 color: #333;
  	 color: red;
  	 float: left;
	}

	.dataTables_filter input
	{
  	 border: 2px solid #993399;
  	 border-radius: 5px;
  	 padding: 6px 10px;
  	 outline: none;
   	 width: 250px;
	}
	
	.page-footer
	{
  	 text-align: center;
  	 padding: 10px 5px;
  	 font-size: 0.95rem;
 	 color: #809fff;
 	 background-color: #f8f9fa;
  	 border-top: 1px solid #993399;
  	 margin-top: 5px;
	}
  </style>
  <style>
  @keyframes blink
  {
    50%
    {
      opacity: 0;
    }
  }
</style>
</head>
<body>
<div class="container">
<div style="overflow: hidden;">
  <div style="float: left; font-size: smaller; font-weight: bold;color:#b3ffff;animation: blink 1s infinite;">
    Environment : DEV
  </div>
  <h2 style="text-align: center; margin: 0;">
    Your Organization Name or Company Name<br>
    <span style="font-size: smaller; color: yellow;text-align: center;">Data Lookup Table</span>
  </h2>
</div> 
 <form id="recordForm"class="form-horizontal" action="" method="POST">
 	<div class="form-group">
        <!--for any other information if you want> -->
       </div>
      <div class="row mb-3">
        <label for="key"class="col-sm-3 col-form-label fw-bold">Key :</label>
        <div class="col-sm-9">
          <input type="text"class="form-control" id="key" name="key" placeholder="Enter the Key data !" required />
        </div>
      </div>
      <div class="row mb-3">
        <label for="kvalue"class="col-sm-3 col-form-label fw-bold">Key Pair Value</label>
        <div class="col-sm-9">
          <input type="text"class="form-control" id="kvalue" name="kvalue" placeholder="Enter the key paris value !" required />
        </div>
      </div>
	<div class="d-flex justify-content-between align-items-center mt-4 flex-wrap">
		<p class="mb-2 mb-md-0 text-primary">
    		<i>Click the edit icon to modify a record, then press "Update" to save. To delete, press the "Delete" button.</i> 
  		</p> 
  		
	<div class="d-flex flex-wrap justify-content-end">
		<button class="btn btn-success me-2" id="Add" type="submit" name="Add">
			<i class="fa fa-plus me-1" style="font-size:18px; color:white;"></i> Add Record
		</button>
	
		<button class="btn btn-primary me-2" type="submit" name="Update">
			<i class="fa fa-edit me-1" style="font-size:18px;"></i> Update
    	</button>

		<button class="btn btn-danger me-2" id="Delete" type="submit" name="Delete">
			<i class="fa fa-remove me-1" style="font-size:18px;"></i> Delete
		</button>
		
		<button class="btn btn-info" id="Cancel" type="submit" name="Cancel">
  			<i class="glyphicon glyphicon-refresh" style="font-size:18px;"></i> Cancel
		</button>
  	   </div> 
    </div>
    <div></div>
	</form>
<script language=SQL name="query">
  SELECT KeyName, DataValue FROM Ens_Util.LookupTable WHERE TableName='Data Lookup Table Example'
</script>

<script language=cache runat=server>
 S myKeyName=$Get(%request.Data("key",1))
 S myKeyValue=$Get(%request.Data("kvalue",1))
 S myDtName="Data Lookup Table Example"
 I ($Data(%request.Data("Add",1)))
 {
	I ((myKeyName '="") && (myKeyValue '="") && (myDtName '=""))
	{	
		I ##Class(SANJIB.DataLookup.Methods).ChkLookupKeyValue(myDtName,myKeyName)="F"
		{
			D##Class(SANJIB.DataLookup.Methods).InsertLookUpValues(myDtName,myKeyName,myKeyValue)
		}
		else
		{	W"The key has already been inserted.."&html<<divstyle=color:red>#(myKeyName)#</div>> }
		
	}
 }
 I ($Data(%request.Data("Update",1)))
 {
	I ((myKeyName '="") && (myKeyValue '="") && (myDtName '=""))
	{
		D##Class(SANJIB.DataLookup.Methods).UpdateLookUpValues(myDtName,myKeyName,myKeyValue)
	}
 }
 I ($Data(%request.Data("Delete",1)))
 {
	 I ((myKeyName '="") && (myKeyValue '="") && (myDtName '=""))
	 {
		D##Class(SANJIB.DataLookup.Methods).DeleteLookUpValues(myDtName,myKeyName)		
	 }
 }	
</script>
 <div class="table-responsive table-scroll-vertical"> 
    <table class="table table-bordered border-primary table table-hover mt-2" id="dataTable" style="min-width: 1000px;">
      <thead class="table-warning">
        <tr>
          <th>Key Data</th>
          <th>Key Pair Value</th>
          <th style="text-align:center;">Actions</th> 
        </tr>
      </thead>
      <tbody id="tableRecordset">
 		</tr>
 		<csp:while counter=myQueryRow condition=query.Next()>
 			<tr class='#($S(myQueryRow#2:"LightRow",1:"LightRow"))#'>
  				<csp:while counter=myQueryColumn condition="(myQueryColumn<query.GetColumnCount())">
      				<td style="background-color:#e6ffff">#(query.GetData(myQueryColumn))#</td>
  						</csp:while>
 							<td style="text-align:center;">
          						<button class="btn btn-sm btn-danger me-2 edit-delete-btn" data-id="#(query.GetData(0))#">
    								<i class="fa fa-edit"></i>
    								<i class="fa fa-trash"></i>
  								</button>
        					</td> 
 						</tr>
 				</csp:while>
      		</tbody>
       </table>
    </div>
  </div>
  
<script language=javascript>
  document.addEventListener('DOMContentLoaded', () => 
  {
    const editButtons = document.querySelectorAll('.edit-delete-btn');
    editButtons.forEach(button =>
    {
      button.addEventListener('click', () => 
      {
        const row = button.closest('tr');
        document.getElementById("Add").disabled=true
        const kID=row.cells[0].textContent.trim();
        const pairVal = row.cells[1].textContent.trim();
        document.getElementById('key').value = kID;
        document.getElementById('kvalue').value = pairVal;
        document.getElementById('key').focus();
      });
    });
  });
</script>
 <script language=javascript>
    document.getElementById('Cancel').addEventListener('click', () => 
    {
    	event.preventDefault();
    	document.getElementById('recordForm').reset();
    });
</script>

<script language=javascript>
  $(document).ready(function()
  {
    $('#dataTable').DataTable(
    {
      "order": [[0, "asc"]],
      "paging": true,
      "lengthChange": false,
      "searching": true,
      "info": true,
      "autoWidth": false
    });
  		$('#dataTable_filter input').css('font-size', '12px').attr('placeholder', 'Type to search..?');
  });
</script>

</body> 

<script language="javascript">
  document.addEventListener('DOMContentLoaded', () =>
  {
  	const updateBtn = document.querySelector('button[name="Update"]');
  	const deleteBtn = document.querySelector('button[name="Delete"]');
  	const addBtn = document.getElementById('Add');
  	const cancelBtn = document.getElementById('Cancel');
  	updateBtn.style.display = 'none';
  	deleteBtn.style.display = 'none';
  	addBtn.style.visibility = 'visible';  
  	addBtn.disabled = false;
  document.querySelectorAll('.edit-delete-btn').forEach(editBtn =>
  {
    editBtn.addEventListener('click', () =>
    {
      updateBtn.style.display = 'inline-block';
      deleteBtn.style.display = 'inline-block';
      addBtn.style.display = 'none';
      addBtn.style.visibility = 'visible'; 
      addBtn.disabled = true;
    });
  });
  updateBtn.addEventListener('click', (e) =>
  {
    updateBtn.style.display = 'none';
    deleteBtn.style.display = 'none';
    addBtn.style.display = 'inline-block';
    addBtn.style.visibility = 'visible';
    addBtn.disabled = false;
  });
  deleteBtn.addEventListener('click', (e) =>
  {
    updateBtn.style.display = 'none';
    deleteBtn.style.display = 'none';
    addBtn.style.display = 'inline-block';
    addBtn.style.visibility = 'visible';
    addBtn.disabled = false;
  });
  cancelBtn.addEventListener('click', (e) =>
  {
    e.preventDefault();  
    document.getElementById('recordForm').reset();
    updateBtn.style.display = 'none';
    deleteBtn.style.display = 'none';
    addBtn.style.display = 'inline-block';
    addBtn.style.visibility = 'visible';  
    addBtn.disabled = false;              
  });
});
</script>

<footer class="page-footer">
  <a href="https://sanjibpandey.wixsite.com/pandey/" target="_blank" rel="noopener noreferrer" style="color: #6c757d; text-decoration: none;"> https://sanjibpandey.wixsite.com/pandey/</a>
</footer>
</html>

 

/// Cette classe est conçue et créée pour insérer, mettre à jour et supprimer des enregistrements (clé & valeur)/// dans la table de recherche de données à partir d'une application Web. /// Ensuite, cela peut être utilisé dans des règles ou des transformations de données à l'aide de la fonction de recherche./// Auteur : Sanjib Pandey/// Date : 16/10/2025Class SANJIB.DataLookup.Methods Extends%Persistent
{

/// Avec cette méthode, on ajoute une valeur à la table de recherche. Les paramètres obligatoires sont:/// dtTableName : Le nom de votre table de recherche de données/// keyData     : La clé/// keyValue    : La valeur ( la valeur de paire de clés)	ClassMethod InsertLookUpValues(dtTable As%String = "", keyData As%String = "", keyValue As%String = "") As%Status
{
	S tSC = $$$OKTry
	{
		I (dtTable '="") && (keyData '="") && (keyValue '="")
		{
			S mySQLStatement = "INSERT into Ens_Util.LookupTable (KeyName,DataValue,TableName) values ('"_keyData_"','"_keyValue_"','"_dtTable_"')"S myRecordSet = ##class(%SQL.Statement).%ExecDirect(,mySQLStatement)
			S tSC={"InserRecorded":(myRecordSet)}
		}
	}
	Catch (Exception)
	{
		Throw Exception	
	}
	Q tSC
}

/// Cette méthode vérifie si la valeur existe déjà dans la table de recherche de données.ClassMethod ChkLookupKeyValue(dtTable As%String, keyData As%String) As%Status
{
	S tSC = $$$OKTry
	{
		I (dtTable '="") && (keyData '="")
		{
			S mySQLStatement="SELECT KeyName FROM Ens_Util.LookupTable WHERE TableName='"_dtTable_"' and KeyName='"_keyData_"'"S myRecordSet = ##class(%SQL.Statement).%ExecDirect(,mySQLStatement)
			D myRecordSet.%Next()
			I (myRecordSet.%SQLCODE=0)
			{ 
				S tSC= "T"
			}
			else
			{
				S tSC="F"
			}
		}
		else
		{
			S tSC= "Invalid Parameters - missing table name or key !"
		}
	}
	Catch (Exception)
	{
		Throw Exception	
	}
	Q tSC
}

/// Cette méthode met à jour uniquement la valeur clé dans la table de recherche de données.ClassMethod UpdateLookUpValues(dtTable As%String = "", keyData As%String = "", keyValue As%String = "") As%Status
{
	S tSC = $$$OKTry
	{
		I (dtTable '="") && (keyData '="") && (keyValue '="")
		{
			S mySQLStatement = "UPDATE Ens_Util.LookupTable SET DataValue='"_keyValue_"' WHERE TableName='"_dtTable_"' AND KeyName='"_keyData_"'"S myRecordSet = ##class(%SQL.Statement).%ExecDirect(,mySQLStatement)
			S tSC={"Updated Record":(myRecordSet)}
		}
	}
	Catch (Exception)
	{
		Throw Exception	
	}
	Q tSC
}

/// Cette méthode est utilisée pour supprimer un enregistrement de la table de recherche de données.ClassMethod DeleteLookUpValues(dtTable As%String, keyData As%String) As%Status
{
	S tSC = $$$OKTry
	{
		I (dtTable '="") && (keyData '="") 
		{
			S mySQLStatement = "DELETE FROM Ens_Util.LookupTable WHERE  TableName='"_dtTable_"' And KeyName='"_keyData_"'"S myRecordSet = ##class(%SQL.Statement).%ExecDirect(,mySQLStatement)
			S tSC={"Deleted Record":(myRecordSet)}
		}
	}
	Catch (Exception)
	{
		Throw Exception	
	}
	Q tSC
}

Storage Default
{
<Data name="MethodsDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
</Data>
<DataLocation>^SANJIB.DataLookup.MethodsD</DataLocation>
<DefaultData>MethodsDefaultData</DefaultData>
<IdLocation>^SANJIB.DataLookup.MethodsD</IdLocation>
<IndexLocation>^SANJIB.DataLookup.MethodsI</IndexLocation>
<StreamLocation>^SANJIB.DataLookup.MethodsS</StreamLocation>
<Type>%Storage.Persistent</Type>
}

}

Conclusion

Cette interface Web réutilisable et cette classe offrent un moyen simple mais efficace pour la gestion des tables de recherche de données dans HealthConnect, en améliorant l'efficacité et la flexibilité. Grâce à l'accès contrôlé dont bénéficient les utilisateurs finaux, cette interface réduit les frais administratifs traditionnellement associés à la maintenance des tables de recherche.

0
0 17
Article Guillaume Rongier · Sept 11, 2025 6m read

img

Connaissant désormais bien Python et ses fonctionnalités, voyons comment nous pouvons tirer parti de Python dans IRIS.

Balise de langue

La balise de langue est une fonctionnalité d'IRIS qui vous permet d'écrire du code Python directement dans vos classes ObjectScript.

Cette fonctionnalité est utile pour le prototypage rapide ou lorsque vous souhaitez utiliser les fonctionnalités de Python sans créer de script Python séparé.

Utilisation

Pour utiliser la balise de langue, il faut définir une méthode de classe avec l'attribut Language = python. Exemple:

Class Article.LanguageTagExample Extends %RegisteredObject
{

ClassMethod Run() [ Language = python ]
{
        import requests

        response = requests.get("https://2eb86668f7ab407989787c97ec6b24ba.api.mockbin.io/")

        my_dict = response.json()

        for key, value in my_dict.items():
            print(f"{key}: {value}") # print message: Hello World
}

}

Quels sont donc les avantages et les inconvénients de l'utilisation de la balise de langue?

Avantages

  • Simplicité : vous pouvez écrire du code Python directement dans vos classes ObjectScript sans avoir à créer de fichiers Python séparés.
  • Prototypage rapide : idéal pour le prototypage rapide ou le test de petits fragments de code Python.
  • Intégration : vous pouvez facilement intégrer du code Python à votre code ObjectScript.

Inconvénients

  • Code mixte : le mixage de code Python et ObjectScript peut rendre votre code plus difficile à lire et à maintenir.
  • Débogage : vous ne pouvez pas déboguer à distance le code Python écrit dans la balise de langage, ce qui peut constituer une limitation pour les applications complexes.
  • Tracebacks : les tracebacks Python ne s'affichent pas, vous ne voyez qu'un message d'erreur ObjectScript, ce qui peut rendre le débogage plus difficile.

Conclusion

La balise de langue est une fonctionnalité puissante qui vous permet d'écrire du code Python directement dans vos classes ObjectScript. Cependant, elle a ses limitations et il est important de l'utiliser de manière raisonnable. Pour les projets plus importants ou lorsque vous devez déboguer votre code Python, il est préférable de créer des scripts Python séparés et de les importer dans vos classes ObjectScript.

Importation de modules Python (modules pypi)

Maintenant que nous avons une bonne compréhension de la balise de langue, voyons comment importer des modules Python et les utiliser dans ObjectScript.

Tout d'abord, nous allons nous limiter aux modules intégrés et aux modules tiers provenant de PyPI, tels que module de demande requests, module numpy, etc.

Utilisation

Ici, nous allons faire la même chose, mais en utilisant uniquement le module de demande de PyPI.

Class Article.RequestsExample Extends %RegisteredObject
{

ClassMethod Run() As %Status
{
    set builtins = ##class(%SYS.Python).Import("builtins")
    Set requests = ##class(%SYS.Python).Import("requests")

    Set response = requests.get("https://2eb86668f7ab407989787c97ec6b24ba.api.mockbin.io/")
    Set myDict = response.json()

    for i=0:1:builtins.len(myDict)-1 {
        set key = builtins.list(myDict.keys())."__getitem__"(i)
        set value = builtins.list(myDict.values())."__getitem__"(i)
        write key, ": ", value, !
    }
}

}

Exécutons-le:

iris session iris -U IRISAPP '##class(Article.RequestsExample).Run()'

Vous verrez le résultat:

message: Hello World

Avantages

  • Accès aux bibliothèques Python : vous pouvez utiliser toutes les bibliothèques Python disponibles sur PyPI, ce qui vous donne accès à un vaste écosystème de bibliothèques et d'outils.
  • Un seul type de code : vous n'écrivez que du code ObjectScript, ce qui facilite la lecture et la maintenance.
  • Débogage : vous pouvez déboguer votre code ObjectScript comme s'il s'agissait uniquement de code ObjectScript, ce qui est le cas :)

Inconvénients

  • Bonne connaissance de Python : vous devez avoir une bonne compréhension de Python pour utiliser efficacement ses bibliothèques.
  • Consultez des exemples dans les articles sur les méthodes dunder.
  • Pas d'écriture de code Python : vous n'écrivez pas de code Python, mais du code ObjectScript qui appelle du code Python, ce qui évite le sucre syntaxique de Python.

Conclusion

En conclusion, l'importation de modules Python dans ObjectScript peut considérablement améliorer les capacités de votre application en tirant parti du vaste écosystème de bibliothèques Python. Cependant, il est essentiel de comprendre les compromis impliqués, tels que la nécessité d'une solide maîtrise de Python.

Importation de modules Python (modules personnalisés)

Continuons avec le même exemple, mais cette fois-ci, nous allons créer un module Python personnalisé et l'importer dans ObjectScript.

Cette fois-ci, nous utiliserons Python autant que possible, et ObjectScript ne sera utilisé que pour appeler le code Python.

Utilisation

Créons un module Python personnalisé dénommé my_script.py avec le contenu suivant:

import requests

def run():
    response = requests.get("https://2eb86668f7ab407989787c97ec6b24ba.api.mockbin.io/")

    my_dict = response.json()

    for key, value in my_dict.items():
        print(f"{key}: {value}") # print message: Hello World

Maintenant, nous allons créer une classe ObjectScript pour importer et exécuter ce module Python:

Class Article.MyScriptExample Extends %RegisteredObject
{
    ClassMethod Run() As %Status
    {
        set sys = ##class(%SYS.Python).Import("sys")
        do sys.path.append("/irisdev/app/src/python/article")  // Adjust the path to your module

        Set myScript = ##class(%SYS.Python).Import("my_script")

        Do myScript.run()

        Quit $$$OK
    }
}

Maintenant, exécutons-le:

iris session iris -U IRISAPP '##class(Article.MyScriptExample).Run()'

⚠️ N'oubliez pas de modifier votre session iris afin de vous assurer que vous disposez de la dernière version du code. Pour plus d'informations, consultez le premier article. Vous verrez le résultat:

message: Hello World

Voici une démonstration de l'importation d'un module Python personnalisé dans ObjectScript et de l'exécution de son code.

Avantages

  • Modularité : vous pouvez organiser votre code Python en modules, ce qui facilite sa gestion et sa maintenance.
  • Syntaxe Python : vous pouvez écrire du code Python en utilisant sa syntaxe et ses fonctionnalités.
  • Débogage : cette fonctionnalité n'est pas disponible pour le moment, mais dans le prochain article, nous verrons comment déboguer du code Python dans IRIS.

Inconvénients

  • Gestion des chemins : vous devez gérer le chemin d'accès à votre module Python. Pour plus d'informations, consultez l'article [https://community.intersystems.com/post/introduction-python-modules] sur sys.path.
  • Connaissances Python : vous devez toujours avoir une bonne compréhension de Python pour écrire et maintenir vos modules.
  • Connaissances ObjectScript : vous devez savoir comment utiliser ObjectScript pour importer et appeler vos modules Python.

Conclusion

En conclusion, l'importation de modules Python dans ObjectScript peut considérablement améliorer les capacités de votre application en tirant parti du vaste écosystème de bibliothèques Python. Cependant, il est essentiel de comprendre les compromis impliqués, tels que la nécessité d'une solide maîtrise de Python.

0
0 21
Article Lorenzo Scalese · Juil 28, 2025 17m read

Découvrez comment concevoir des agents IA évolutifs et autonomes qui combinent raisonnement, recherche vectorielle et intégration d'outils à l'aide de LangGraph.

cover

C'est trop long, vous n'avez pas lu

  • Les agents IA sont des systèmes proactifs qui combinent mémoire, contexte et initiative pour automatiser des tâches dépassant le simple champ d'action des chatbots.
  • LangGraph est un framework qui nous permet de créer des workflows d'IA complexes, en utilisant des nœuds (tâches) et des arêtes (connexions) avec une gestion d'état intégrée.
  • Ce guide vous explique comment créer un agent de support client basé sur l'IA qui classe les priorités, identifie les sujets pertinents et détermine si une escalade ou une réponse automatique est nécessaire.

Alors, les agents IA, c'est quoi exactement?

Soyons réalistes, le terme "agents IA" peut faire penser à des robots qui vont envahir votre bureau. En réalité, il s'agit de vos assistants proactifs qui peuvent rationaliser des flux de travail complexes et éliminer les tâches répétitives. Considérez-les comme la prochaine étape évolutive après les chatbots: ils ne se contentent pas d'attendre des instructions, ils lancent des actions, coordonnent plusieurs étapes et s'adaptent tout au long du processus.

Autrefois, créer un système "intelligent" signifiait jongler avec différents modèles pour la compréhension du langage, la génération de code, la recherche de données, etc., puis les assembler à la va-vite. Auparavant, vous passiez la moitié de votre temps dans l'enfer de l'intégration, et l'autre moitié à corriger les erreurs.

Les agents renversent la tendance. Ils regroupent le contexte, l'initiative et l'adaptabilité en un seul flux orchestré. Il ne s'agit pas seulement d'automatisation, mais d'intelligence au service d'une mission. Et grâce à des frameworks tels que LangGraph, la création de votre propre équipe d'agents peut même devenir... oserais-je dire, amusante?

image

LangGraph, c'est quoi exactement?

LangGraph est un cadre innovant qui révolutionne la manière dont nous construisons des applications complexes impliquant des modèles linguistiques à grande échelle (LLM).

Imaginez que vous dirigez un orchestre: chaque instrument (ou "nœud") doit savoir quand entrer, à quel volume jouer et dans quel ordre. LangGraph, dans ce cas**,** est votre baguette, qui vous donne les informations suivantes:

  • Structure graphique: utilise une structure graphique avec des nœuds et des arêtes, permettant aux développeurs de concevoir des flux de travail flexibles et non linéaires qui s'adaptent aux branches et aux boucles. Elle reflète les processus décisionnels complexes qui ressemblent au fonctionnement des voies neuronales.
  • Gestion d'état: LangGraph offre des outils intégrés pour la persistance d'état et la récupération d'erreurs, simplifiant la gestion des données contextuelles à différentes étapes d'une application. Il peut basculer efficacement entre la mémoire à court terme et la mémoire à long terme, améliorant ainsi la qualité des interactions grâce à des outils tels que Zep.
  • Intégration d'outils: Avec LangGraph, les agents LLM peuvent facilement collaborer avec des services ou des bases de données externes pour récupérer des données du monde réel, améliorant ainsi la fonctionnalité et la réactivité de vos applications.
  • Human-in-the-Loop: Au-delà de l'automatisation, LangGraph s'adapte aux interventions humaines dans les flux de travail, qui sont essentielles pour les processus décisionnels nécessitant une supervision analytique ou une réflexion éthique.

Lorsque vous créez un chatbot doté d'une mémoire réelle, un moteur d'histoires interactives ou une équipe d'agents chargés de résoudre un problème complexe, LangGraph transforme les tâches fastidieuses en une machine à états simple et visuelle.

Pour commencer

Pour commencer à utiliser LangGraph, vous aurez besoin d'une configuration de base qui implique généralement l'installation de bibliothèques essentielles telles que langgraph et langchain-openai. Ensuite, vous pourrez définir les nœuds (tâches) et les bords (connexions) au sein du graphe, en mettant efficacement en œuvre des points de contrôle pour la mémoire à court terme et en utilisant Zep pour les besoins en mémoire plus persistants.

Lorsque vous utilisez LangGraph, gardez à l'esprit les remarques suivantes:

  • Flexibilité de conception: Tirez parti de la puissante structure graphique pour prendre en compte les ramifications et les interactions potentielles du flux de travail qui ne sont pas strictement linéaires.
  • Interaction prudente avec les outils: Améliorez les capacités du LLM à l'aide d'outils externes, mais ne les remplacez pas. Fournissez à chaque outil des descriptions complètes pour permettre une utilisation précise.
  • Utilisation de solutions de mémoire riches: Utilisez la mémoire de manière efficace, soyez attentif à la fenêtre contextuelle du LLM et envisagez d'intégrer des solutions externes pour la gestion automatiquedu contenu factuel.

Nous avons abordé les bases de LangGraph, passons maintenant à un exemple pratique. Pour cela, nous allons développer un agent IA spécialement conçu pour le support client.

Cet agent recevra les demandes par e-mail, analysera la description du problème dans le corps du message, puis déterminera la priorité de la demande et le sujet/la catégorie/le secteur approprié.

Alors, attachez vos ceintures et c'est parti!

buckle up

Pour commencer, nous devons définir ce qu'est un "outil". Vous pouvez le considérer comme un "assistant manager" spécialisé pour votre agent, lui permettant d'interagir avec des fonctionnalités externes.

Le décorateur @tool est ici essentiel. LangChain simplifie la création d'outils personnalisés, ce qui signifie que vous définissez d'abord une fonction Python, puis appliquez le décorateur @tool.

tools

Illustrons cela en créant notre premier outil. Cet outil aidera l'agent à classer la priorité d'un ticket d'assistance informatique en fonction du contenu de l'e-mail:

    from langchain_core.tools import tool
    
    @tool
    def classify_priority(email_body: str) -> str:
        """Classify the priority of an IT support ticket based on email content."""
        prompt = ChatPromptTemplate.from_template(
            """Analyze this IT support email and classify its priority as High, Medium, or Low.
            
            High: System outages, security breaches, critical business functions down
            Medium: Non-critical issues affecting productivity, software problems
            Low: General questions, requests, minor issues
            
            Email: {email}
            
            Respond with only: High, Medium, or Low"""
        )
        chain = prompt | llm
        response = chain.invoke({"email": email_body})
        return response.content.strip()

Excellent! Nous avons maintenant une invite qui demande à l'IA de recevoir le corps de l'e-mail, de l'analyser et de classer sa priorité comme Élevée, Moyenne ou Faible.

C'est tout! Vous venez de créer un outil accessible à votre agent!

Créons maintenant un outil similaire pour identifier le sujet principal (ou la catégorie) de la demande de support:


    @tool
    def identify_topic(email_body: str) -> str:
        """Identify the main topic/category of the IT support request."""
        prompt = ChatPromptTemplate.from_template(
            """Analyze this IT support email and identify the main topic category.
            
            Categories: password_reset, vpn, software_request, hardware, email, network, printer, other
            
            Email: {email}
            
            Respond with only the category name (lowercase with underscores)."""
        )
        chain = prompt | llm
        response = chain.invoke({"email": email_body})
        return response.content.strip()

Nous devons maintenant créer un état, et dans LangGraph, cette petite partie est plutôt importante.

Considérez-le comme le système nerveux central de votre graphe. C'est ainsi que les nœuds communiquent entre eux, en se passant des petits mots comme des surdoués à l'école.

Selon la documentation:

“Un état est une structure de données partagée qui représente l'instantané actuel de votre application.”

Et en pratique? L'état est un message structuré qui circule entre les nœuds. Il transporte le résultat d'une étape comme entrée pour la suivante. En gros, c'est le ciment qui maintient l'ensemble de votre flux de travail.

Par conséquent, avant de construire le graphique, nous devons d'abord définir la structure de notre état. Dans cet exemple, notre état sera composé des éléments suivants:

  • La demande de l'utilisateur (corps de l'email)
  • La priorité attribuée
  • Le sujet identifié (catégorie)

C'est simple et clair, vous pouvez donc vous déplacer à travers le graphe comme un pro.

    from typing import TypedDict

    # Définition de la structure d'état
    class TicketState(TypedDict):
        email_body: str
        priority: str
        topic: str
        
    
    # Initialisation d'état
    initial_state = TicketState(
        email_body=email_body,
        priority="",
        topic=""
    )

Nœuds et bords: Composants clés de LangGraph

Les éléments fondamentaux de LangGraph sont les nœuds et les bords.

  • Nœuds: Il s'agit des unités opérationnelles du graphe qui effectuent le travail proprement dit. Un nœud se compose généralement d'un code Python capable d'exécuter n'importe quelle logique, qu'il s'agisse de calculs ou d'interactions avec des modèles linguistiques (LLM) ou des intégrations externes. Les nœuds sont essentiellement similaires aux fonctions ou agents individuels de la programmation traditionnelle.
  • Bords: les bords définissent le flux d'exécution entre les nœuds et déterminent ce qui se passe ensuite. Elles agissent comme des connecteurs qui permettent à l'état de passer d'un nœud à l'autre en fonction de conditions prédéfinies. Dans le contexte de LangGraph, les bords sont essentiels pour orchestrer la séquence et le flux de décision entre les nœuds.

Pour comprendre le fonctionnement des bords, prenons l'exemple simple d'une application de messagerie:

  • Nœuds sont similaires aux utilisateurs (ou à leurs appareils) qui participent activement à une conversation.
  • Bords symbolisent les fils de discussion ou les connexions entre les utilisateurs qui facilitent la communication.

Lorsqu'un utilisateur sélectionne un fil de discussion pour envoyer un message, un bord est créée, le reliant à un autre utilisateur. Chaque interaction, qu'il s'agisse d'envoyer un message textuel, vocal ou vidéo, suit une séquence prédéfinie, comparable au schéma structuré de l'état de LangGraph. Cela garantit l'uniformité et l'interprétabilité des données transmises le long des bords.

Contrairement à la nature dynamique des applications pilotées par les événements, LangGraph utilise un schéma statique qui reste cohérent tout au long de l'exécution. Il simplifie la communication entre les nœuds, permettant aux développeurs de s'appuyer sur un format stable, garantissant ainsi une communication fluide au niveau des bords.

Conception d'un flux de travail de base

L'ingénierie des flux dans LangGraph peut être conceptualisée comme la conception d'une machine d'état. Dans ce paradigme, chaque nœud représente un état ou une étape de traitement distinct, tandis que les arêtes définissent les transitions entre ces états. Cette approche est particulièrement avantageuse pour les développeurs qui cherchent à trouver un équilibre entre les séquences de tâches déterministes et les capacités décisionnelles dynamiques de l'IA. Commençons à construire notre flux en initialisant StateGraph avec la classe TicketState que nous avons définie précédemment.

    from langgraph.graph import StateGraph, START, END
    
    workflow = StateGraph(TicketState)

Ajout de nœuds: Les nœuds sont des éléments fondamentaux, définis pour exécuter des tâches spécifiques telles que la classification de la priorité d'un ticket ou l'identification de son sujet.

Chaque fonction de nœud reçoit l'état actuel, effectue son opération et renvoie un dictionnaire permettant de mettre à jour l'état:

   def classify_priority_node(state: TicketState) -> TicketState:
        """Node to classify ticket priority."""
        priority = classify_priority.invoke({"email_body": state["email_body"]})
        return {"priority": priority}

    def identify_topic_node(state: TicketState) -> TicketState:
        """Node to identify ticket topic."""
        topic = identify_topic.invoke({"email_body": state["email_body"]})
        return {"topic": topic}
        
        
    workflow.add_node("classify_priority", classify_priority_node)
    workflow.add_node("identify_topic", identify_topic_node)

Les méthodes classify_priority_node et identify_topic_node modifieront le TicketState et enverront la saisie du paramètre.

Création des bords: Définissez les bords pour connecter les nœuds:


    workflow.add_edge(START, "classify_priority")
    workflow.add_edge("classify_priority", "identify_topic")
    workflow.add_edge("identify_topic", END)

The classify_priority establishes the start, whereas the identify_topic determines the end of our workflow so far.

Compilation et exécution: Une fois les nœuds et les bords configurés, compilez le flux de travail et exécutez-le.


    graph = workflow.compile()
    result = graph.invoke(initial_state)

Très bien! Vous pouvez également générer une représentation visuelle de notre flux LangGraph.

graph.get_graph().draw_mermaid_png(output_file_path="graph.png")

Si vous exécutez le code jusqu'à ce point, vous obtiendrez un graphe similaire au suivant:

first_graph.png

Cette illustration visualise une exécution séquentielle: démarrage, suivi du classement des priorités, puis identification du sujet et enfin terminaison.

L'un des aspects les plus puissants de LangGraph est sa flexibilité, qui nous permet de créer des flux et des applications plus complexes. Par exemple, nous pouvons modifier le flux de travail pour ajouter des bords depuis START vers les deux nœuds avec la ligne suivante:

    workflow.add_edge(START, "classify_priority")
    workflow.add_edge(START, "identify_topic")

Cette modification aura pour conséquence que l'agent exécutera simultanément classify_priority et identify_topic.

Une autre fonctionnalité très utile de LangGraph est la possibilité d'utiliser des bords conditionnels. Ils permettent au flux de travail de se ramifier en fonction de l'évaluation de l'état actuel, ce qui permet un routage dynamique des tâches.

Améliorons notre flux de travail. Nous allons créer un nouvel outil qui analyse le contenu, la priorité et le sujet de la demande afin de déterminer s'il s'agit d'un problème hautement prioritaire nécessitant une escalade (c'est-à-dire l'ouverture d'un ticket pour être traité par une équipe humaine). Si ce n'est pas le cas, une réponse automatisée sera générée pour l'utilisateur.


    @tool
    def make_escalation_decision(email_body: str, priority: str, topic: str) -> str:
        """Decide whether to auto-respond or escalate to IT team."""
        prompt = ChatPromptTemplate.from_template(
            """Based on this IT support ticket, decide whether to:
            - "auto_respond": Send an automated response for simple/common or medium priority issues
            - "escalate": Escalate to the IT team for complex/urgent issues
            
            Email: {email}
            Priority: {priority}
            Topic: {topic}
            
            Consider: High priority items usually require escalation, while complex technical issues necessitate human review.
            
            Respond with only: auto_respond or escalate"""
        )
        chain = prompt | llm
        response = chain.invoke({
            "email": email_body,
            "priority": priority,
            "topic": topic
        })
        return response.content.strip()
        

De plus, si la demande est jugée de priorité faible ou moyenne (ce qui entraîne une décision "auto_respond"), nous effectuerons une recherche vectorielle pour récupérer les réponses historiques. Ces informations seront ensuite utilisées pour générer une réponse automatisée appropriée. Cependant, cela nécessitera deux outils supplémentaires:


    @tool
    def retrieve_examples(email_body: str) -> str:
        """Retrieve relevant examples from past responses based on email_body."""
        try:
            examples = iris.cls(__name__).Retrieve(email_body)
            return examples if examples else "No relevant examples found."
        except:
            return "No relevant examples found."

    @tool
    def generate_reply(email_body: str, topic: str, examples: str) -> str:
        """Generate a suggested reply based on the email, topic, and RAG examples."""
        prompt = ChatPromptTemplate.from_template(
            """Generate a professional IT support response based on:
            
            Original Email: {email}
            Topic Category: {topic}
            Example Response: {examples}
            
            Create a helpful, professional response that addresses the user's concern.
            Keep it concise and actionable."""
        )
        chain = prompt | llm
        response = chain.invoke({
            "email": email_body,
            "topic": topic,
            "examples": examples
        })
        return response.content.strip()

Maintenant, définissons les nœuds correspondants à ces nouveaux outils:

    
    def decision_node(state: TicketState) -> TicketState:
        """Node to decide on escalation or auto-response."""
        decision = make_escalation_decision.invoke({
            "email_body": state["email_body"],
            "priority": state["priority"],
            "topic": state["topic"]
        })
        return {"decision": decision}
        
    
    def rag_node(state: TicketState) -> TicketState:
        """Node to retrieve relevant examples using RAG."""
        examples = retrieve_examples.invoke({"email_body": state["email_body"]})
        return {"rag_examples": examples}

    def generate_reply_node(state: TicketState) -> TicketState:
        """Node to generate suggested reply."""
        reply = generate_reply.invoke({
            "email_body": state["email_body"],
            "topic": state["topic"],
            "examples": state["rag_examples"]
        })
        return {"suggested_reply": reply}
        
    
    def execute_action_node(state: TicketState) -> TicketState:
        """Node to execute final action based on decision."""
        if state["decision"] == "escalate":
            action = f"&#x1f6a8; ESCALATED TO IT TEAM\nPriority: {state['priority']}\nTopic: {state['topic']}\nTicket created in system."
            print(f"[SYSTEM] Escalating ticket to IT team - Priority: {state['priority']}, Topic: {state['topic']}")
        else:
            action = f"&#x2705; AUTO-RESPONSE SENT\nReply: {state['suggested_reply']}\nTicket logged for tracking."
            print(f"[SYSTEM] Auto-response sent to user - Topic: {state['topic']}")
        
        return {"final_action": action}
        
        
        
    workflow.add_node("make_decision", decision_node)
    workflow.add_node("rag", rag_node)
    workflow.add_node("generate_reply", generate_reply_node)
    workflow.add_node("execute_action", execute_action_node)

Le bord conditionnel utilisera alors le résultat du nœud make_decision pour diriger le flux:

    workflow.add_conditional_edges(
        "make_decision",
        lambda x: x.get("decision"),
        {
            "auto_respond": "rag",
            "escalate": "execute_action"
        }
    )

Si l'outil make_escalation_decision (via decision_node) renvoie "auto_respond", le workflow passe par le nœud rag (pour récupérer des exemples), puis par generate_reply (pour rédiger la réponse) et enfin par execute_action (pour enregistrer la réponse automatique).

En revanche, si la décision est “escalate”, le flux contournera le RAG et passera aux étapes de génération, passant directement à execute_action pour gérer l'escalade. Pour compléter le graphe en ajoutant les bords standard restants, procédez comme suit:

    workflow.add_edge("rag", "generate_reply")
    workflow.add_edge("generate_reply", "execute_action")
    workflow.add_edge("execute_action", END)

Remarque sur le jeu de données: pour ce projet, le jeu de données utilisé pour alimenter la génération augmentée par récupération (RAG) provient du jeu de données Customer Support Tickets dataset on Hugging Face. Le jeu de données a été filtré afin de n'inclure que les éléments classés dans la catégorie de support technique 'Technical Support' et limité aux saisies en anglais English. Cela a permis de garantir que le système RAG ne récupère que des exemples très pertinents et spécifiques au domaine pour les tâches de support technique.

À ce stade, notre graphique devrait ressembler au suivant:

graph.png

Lorsque vous exécutez ce graphe avec un e-mail qui entraîne une classification de priorité élevée et une décision "escalate", vous obtenez la réponse suivante:

image.png

Au même moment, une demande classée comme faible priorité et donnant lieu à une décision « auto_respond » déclenchera une réponse similaire à la suivante:

image.png

Alors... Est-ce que tout est rose?

Pas tout à fait. Il y a quelques obstacles à éviter:

  • Confidentialité des données: Soyez prudent avec les informations sensibles, ces agents nécessitent des mesures de protection.
  • Coûts informatiques: Certaines configurations avancées nécessitent des ressources importantes.
  • Hallucinations: Les LLM peuvent parfois inventer des choses (même s'ils restent plus intelligents que la plupart des stagiaires).
  • Non-déterminisme: Une même saisie peut donner des résultats différents, ce qui est excellent pour la créativité, mais problématique pour les processus rigoureux.

Cependant, la plupart de ces lacunes peuvent être comblées grâce à une bonne planification, aux bons outils et, bien sûr, à un peu de réflexion.

LangGraph transforme les agents IA, qui ne sont encore que des mots à la mode, en solutions fonctionnelles et tangibles. Que vous souhaitiez automatiser votre service client, traiter vos tickets informatiques ou créer des applications autonomes, ce framework rend tout cela possible et même agréable.

Avez-vous des questions ou des commentaires? Parlons-en. La révolution de l'IA a besoin de créateurs comme vous.

0
0 27
Article Sylvain Guilbaud · Juil 18, 2025 11m read

🛠️ Gestion des configurations d'InterSystems API Manager (IAM = Kong Gateway) en CI/CD

🔍 Contexte : configurations d'InterSystems IAM

Dans le cadre de l'intégration d'InterSystems IAM dans un environnement sécurisé et contrôlé, InterSystems IAM repose sur Kong Gateway pour gérer les API exposées. Kong agit comme une API Gateway moderne, capable de gérer l’authentification, la sécurité, la gestion du trafic, les plugins, et bien plus encore.

0
0 24
Question Franck Hanotin · Juil 3, 2025

Bonjour,

j'essaie de faire une connexion avec un serveur SFTP avec 

set ssh = ##class(%Net.SSH.Session).%New()

set Status=ssh.Connect(Server,FtpPort)

set Status=ssh.AuthenticateWithKeyPair(User,"E:\Datas\export\ClePublic.txt","E:\Clé ssh\id-rsa.ppk","")

et là j'ai l'erreur:

TOOLS>zw Status                                                                

1
0 45
Article Lorenzo Scalese · Mai 7, 2025 6m read

Introduction

À mesure que l'automatisation pilotée par l'IA devient un élément essentiel des systèmes d'information modernes, l'intégration des capacités d'IA dans les plateformes existantes doit être transparente et efficace. Le projet IRIS Agent montre comment l'IA générative peut fonctionner sans effort avec InterSystems IRIS, grâce à son puissant cadre d'interopérabilité, sans qu'il soit nécessaire d'apprendre Python ou de créer des workflows d'IA distincts à partir de zéro.
Ce projet examine la manière dont ChatGPT et Anthropic Claude, deux modèles d'IA parmi les plus avancés, peuvent interagir avec IRIS à l'aide de GPT personnalisés et du protocole MCP (Model Context Protocol). Plutôt que de créer des pipelines d'IA isolés, IRIS Agent traite l'IA comme un service d'interopérabilité, permettant ainsi aux organisations d'améliorer l'automatisation, la prise de décision et le traitement des données sans perturber leur architecture existante.
En s'appuyant sur des outils d'interopérabilité natifs d'IRIS, les développeurs peuvent intégrer des modèles d'IA de manière transparente, comme n'importe quel autre composant du système. Cette approche garantit la stabilité, la sécurité, l'évolutivité et l'auditabilité, tout en permettant une interaction en langage naturel, la récupération de données en temps réel et l'administration automatisée du système, le tout dans l'environnement familier d'IRIS.

Technologies sous-jacentes à IRIS Agent

Le projet IRIS Agent s'appuie sur un ensemble de technologies puissantes pour garantir efficacité, évolutivité et intégration transparente:

  • InterSystems IRIS – Une plateforme robuste dédiée au développement d'applications et à l'intégration de données, qui utilise ObjectScript pour le code côté serveur.
  • Custom GPT Bot – Un assistant IA personnalisé conçu pour rationaliser les interactions avec le système, basé sur ChatGPT.
  • Claude AI Desktop – Facilite la communication avec le serveur via le protocole MCP (Model Context Protocol).
  • Node.js – Gère les communications avec le serveur MCP
  • OpenAPI – Documentation API standardisée.
  • Docker – Compartimente l'application pour simplifier le déploiement et la gestion des dépendances.

Présentation interne

Voici à quoi ressemble notre projet sous le capot IRIS:

La Production comprend les éléments suivants:

  • LanguageModelIn: un service qui reçoit une requête API et la redirige vers l'opération responsable.
  • Meds: une opération qui recherche des médicaments sur une API tierce.
  • Metrics: une opération qui recherche des informations OPS telles que les journaux, les erreurs et les messages dans le système Iris.
  • Namespaces: une opération qui répertorie, recherche et modifie les espaces de noms dans le système Iris.
  • Users: une opération qui répertorie, recherche, crée et supprime des utilisateurs dans le système Iris.

Le fonctionnement du service est particulièrement visible dans l'afficheur de messages et les traces. Il peut nous aider à suivre les opérations et à diagnostiquer les problèmes, par exemple les requêtes:

…et les réponses:


L'un des points forts de ce projet réside dans le fait que le service LanguageModelIn génère automatiquement une documentation API ouverte pour les opérations métier dans le cadre de la production IRIS Interoperability. Cette API  nous permet de connecter facilement les GPT personnalisés à ChatGPT et au serveur MCP (Model Context Protocol) à Claude AI Desktop. 

Intégration avec ChatGPT 

Après avoir terminé tous les processus d'installation et de configuration, que vous trouverez dans notre fichier ReadMe, ReadMe, demandons à notre agent IRIS GPT d'OpenAI quelles sont ses fonctionnalités actuelles:

  

Et créez un nouvel utilisateur…

   


Vérification des mises à jour...

   

Intégration avec des Sources de données externes

L'une des fonctionnalités remarquables de l'agent IRIS est sa capacité à interroger de manière transparente non seulement les données stockées dans la base de données InterSystems IRIS, mais également des sources de données externes. Dans notre exemple, l'agent s'intègre à l'OpenFDA API pour fournir des informations en temps réel sur les médicaments. Cela permet aux utilisateurs de rechercher des informations sur les médicaments, des rapports de sécurité et des données de conformité réglementaire directement dans le système. 
Dans notre projet, l'API offre la possibilité de rechercher des médicaments par leur nom. Recherchons donc les médicaments dont le nom contient le mot "flu".

   

Si vous souhaitez l'essayer dès maintenant, suivez le lien et engagez une conversation avec notre IRIS Agent démo.

Intégration avec Claude AI Desktop

L'une des fonctionnalités clés de l'agent IRIS est sa capacité à interagir avec Claude AI Desktop, un assistant IA local développé par Anthropic. L'intégration est assurée par le protocole MCP (Model Context Protocol), qui facilite la communication entre Claude et les applications externes. Pour ce faire, l'agent IRIS utilise un serveur MCP Node.js dédié qui sert d'intermédiaire, traitant les requêtes entrantes et les acheminant entre Claude et le système IRIS.
Cette configuration permet aux utilisateurs d'interagir directement avec l'agent IRIS via l'interface Claude, en émettant des commandes en langage naturel pour récupérer des données système, gérer les configurations et exécuter des tâches administratives. Le serveur MCP garantit un échange de données fluide, tout en maintenant l'efficacité et la sécurité, et fournit aux administrateurs un assistant puissant basé sur l'IA pour la gestion du système.

         

Conclusion

Le projet IRIS Agent présente le potentiel de l'automatisation pilotée par l'IA dans les environnements d'entreprise modernes. En intégrant de manière transparente InterSystems IRIS, Claude AI et Custom GPT, il simplifie la gestion des systèmes, améliore l'efficacité et ouvre la voie à de futures innovations. Notre projet démontre également que vous êtes déjà prêt pour la révolution de l'IA avec InterSystems IRIS!


Si vous avez apprécié la découverte d'IRIS Agent et de ses fonctionnalités et que vous souhaitez nous soutenir, veuillez prendre quelques instants pour voter pour notre application ici https://openexchange.intersystems.com/contest/40. Je vous remercie!


Développeurs de projet:

Banksia Global est une société de conseil technologique de premier plan qui aide les organisations à exploiter pleinement le potentiel des solutions modernes de gestion des données. En mettant l'accent sur la plateforme InterSystems IRIS, Banksia Global fournit des conseils stratégiques, des services d'intégration de systèmes et de développement de logiciels personnalisés à des clients issus de divers secteurs. Son expertise réside dans la fourniture de solutions hautement performantes et évolutives qui permettent aux entreprises de stimuler l'innovation, de rationaliser leurs opérations et de saisir de nouvelles opportunités. Réputée pour son approche collaborative et agile, Banksia Global travaille en étroite collaboration avec ses clients afin de s'assurer que chaque solution est adaptée à leurs besoins spécifiques, favorisant ainsi des partenariats à long terme et une croissance durable.

0
0 59
Article Pierre LaFay · Déc 30, 2023 6m read

Introduction

Depuis qu'InterSystems a récemment annoncé l'arrêt du support d'InterSystems Studio à partir de la version 2023.2 au profit du développement exclusif d'extensions pour l'IDE Visual Studio Code (VSC), estimant que ce dernier offre une expérience supérieure par rapport à Studio, beaucoup d'entre nous, développeurs, avons changé ou commencé à utiliser VSC. Beaucoup se sont peut-être demandé comment ouvrir le Terminal pour effectuer des opérations, car VSC n'a pas de panneau de sortie comme Studio, ni de fonctionnalité intégrée pour ouvrir le terminal IRIS, sauf en téléchargeant les plugins développés par InterSystems.

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

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

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


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

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

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

Option 1 - Simple: Exportation de CSV

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

Option 2 - Chic: Analyse

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

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

 

 

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

La troisième question se pose.

Quelles commandes sont passées par quels sites?

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

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

 

Et enfin, la question la plus difficile.

Quand tout cela se produit-il?

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

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

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

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

 

Conclusions

1. Données sensibles:

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

2. Vitesse:

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

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

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

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

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

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

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

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

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

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

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

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

 

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

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

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

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

 

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

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

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

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

 

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

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

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

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

Bonus! 

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

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

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

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

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

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

Catalogue d'Applications

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

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

Détails de l'application 

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

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

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

 Apprentissage en ligne - learning.intersystems.com

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

 Documentation - docs.intersystems.com 

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

 Évaluation - evaluation.intersystems.com

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

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

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

 Idées InterSystems - ideas.intersystems.com

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

 Les Masters Mondiaux - globalmasters.intersystems.com

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

 Open Exchange - openexchange.intersystems.com 

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

 WRC - wrc.intersystems.com

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

 iService - iservice.intersystems.com

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

 ICR - containers.intersystems.com

Audience

  • Tous ceux qui souhaitent utiliser les conteneurs InterSystems

Description

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

Comment cela aide à améliorer votre niveau technique

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

 Répertoire de partenaires - partner.intersystems.com 

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

 CCR - ccr.intersystems.com 

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

 Connexion Client - client.intersystems.com  

Audience

  • Disponible pour tous les clients de TrakCare

Description

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

Comment cela aide à améliorer votre niveau technique

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

 Commande en ligne - store.intersystems.com

Audience

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

Description

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

Comment cela aide à améliorer votre niveau technique

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

Autres choses à savoir sur votre compte de connexion InterSystems

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

Comment créer un compte de connexion

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

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

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

Autres options de connexion

Certaines applications permettent de se connecter avec Google ou GitHub:

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

Profil du compte

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

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

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

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

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

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

image

Introduction

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

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

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

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

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

Débuter avec Python

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

Pourquoi ? Ils ont beaucoup de choses en commun.

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

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

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

Python n'est pas ObjectScript

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

Pour moi il y a principalement 3 différences :

  • Pep8
  • Modules
  • Dunders

Pep8

Mais qu'est-ce que Pep8 ?

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

pep8.org

Quelques-unes d'entre elles sont :

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

Pourquoi est-ce important ?

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

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

J'ai appris Pep8 à la dure.

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

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

Modules

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

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

Qu'est-ce qu'un module ?

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

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

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

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

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

Un mauvais exemple :

MyClass.py

class MyClass:
    def __init__(self):
        pass

    def my_method(self):
        pass

Pour instancier cette classe, vous allez faire :

import MyClass.MyClass # weird right ?

my_class = MyClass()

Bizarre, hein ?

Dunders

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

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

Elles sont utilisées pour ce qui suit :

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

Exemple :

class MyClass:
    def __init__(self):
        pass

    def __repr__(self):
        return "MyClass"

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

Ici, nous avons 3 méthodes de dunder :

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

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

Conclusion

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

De bons sites pour apprendre Python :


Embedded Python

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

Qu'est-ce que Embedded Python ?

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

Comment utiliser Embedded Python ?

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

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

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

image

Pourquoi ?

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

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

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

Comment j'utilise Embedded Python

Je crois que vous avez deux options :

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

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

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

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

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

Avec la balise de langue

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

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

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

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

Sans de balise de langue

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

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

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

Conclusion

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

Utilisation de première approche en python

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

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

Quelques exemples :

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

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

Comment y parvenir ?

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


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

Exemple : iris-fhir-python-strategy

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

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

Une Stratégie est un ensemble de deux classes :

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

Ces deux classes sont des classes abstraites Abtract Abstract.

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

Remarques

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

Où trouver le code

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

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

Comment mettre en œuvre une Stratégie

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

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

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

Mise en œuvre d'InteractionsStrategy

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

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

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

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

Mise en œuvre des Interactions

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

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

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

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

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

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

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


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

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

}

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

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

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

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

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

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

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

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

}

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

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

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

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

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

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

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

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

Interactions en Python

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

Voici la classe python abstraite :

import abc
import iris

class Interaction(object):
    __metaclass__ = abc.ABCMeta

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

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

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

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

Mise en œuvre de la classe abstraite python

from FhirInteraction import Interaction

class CustomInteraction(Interaction):

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

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

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

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

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

Trop long, faisons un résumé

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

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

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

0
0 69
Annonce Irène Mykhailova · Août 14, 2024

L'équipe de développement d'extensions VS Code recherche des bêta-testeurs pour fournir des commentaires sur une refonte proposée du flux de travail d'édition côté client. La liste complète des modifications se trouve dans la description de la GitHub pull request. Voici les points saillants :

0
0 39
Article Iryna Mykhailova · Juin 27, 2024 6m read

Introduction

Dans les prochaines semaines, mes collègues prévoient de commencer à utiliser VSCode pour coder avec les produits d'InterSystems. Parmi les nombreux avantages, je soulignerais la possibilité de se connecter facilement à d'autres technologies, telles que GitHub. En outre, VSCode propose également une vaste boutique d'extensions, où vous pouvez trouver de nombreux add-ons gratuits qui rendent le codage plus rapide et plus efficace. Enfin, pour conquérir le cœur de tous les développeurs, c'est open source.

Cela étant dit, commençons le tutoriel. N'hésitez pas à sauter certaines étapes si vous vous sentez à l'aise pour les réaliser sans aide.

Étape 1 - Téléchargement et installation de VSCode

Recherchez "VSCode download" ou cliquez sur this linkce lien pour télécharger à partir de la page officielle. Sélectionnez l'installation correcte pour votre ordinateur et exécutez le fichier téléchargé.

Une fois l'exécutable lancé, il vous sera demandé d'accepter la licence, de sélectionner quelques préférences (qui sont très personnelles et dont je ne parlerai pas dans ce texte), et de procéder à l'installation. 
 

Étape 2 - Installation des extensions InterSystems

Ouvrez votre VSCode dans l'onglet Extensions et recherchez le paquet ObjectScript Extension Pack dans la boutique.

Installez le paquet complet.
 

Étape 3 - Configuration de votre VSCode pour vos instances

Une fois les extensions installées, vous devriez avoir un onglet InterSystems. Vous pouvez le sélectionner et cliquer sur "+" dans la section des serveurs, comme indiqué ci-dessous, et vous serez invité à fournir des informations sur la connexion, telles que l'hôte, le port, le nom d'utilisateur et le mot de passe.

Sinon, vous pouvez aller dans votre fichier VSCode general settings.json, que vous pouvez trouver en cliquant sur l'engrenage de gestion   sur le côté gauche en bas de l'écran, en sélectionnant les configurations, et en cliquant sur "Ouvrir les paramètres (JSON)" ("Open Settings (JSON)")    sur le côté droit en haut de l'écran.

Là, vous pouvez coller le fragment JSON suivant, en le modifiant pour qu'il pointe vers votre instance.

"intersystems.servers": {
        "[YOUR SERVER NAME]": {
            "webServer": {
                "scheme": "http",
                "host": "127.0.0.1",
                "port": 53472
            },
            "username": "_system"
        }

 

Étape 4.1 - Connexion à un serveur et à un espace de noms pour le développement local

Toujours dans l'onglet InterSystems, sélectionnez la section Explorer et cliquez sur le bouton "Choisir le serveur et l'espace de noms" (Choose server and namespace).

Vous devriez être invité à choisir parmi les serveurs ajoutés lors de la dernière étape, votre nom d'utilisateur et votre mot de passe, ainsi qu'un espace de noms.

Ensuite, toujours dans le même onglet, vous pouvez utiliser la section d'exploration (Explore) ou la section des projets (Projects) pour choisir les classes et/ou les projets que vous souhaitez exporter. Si vous choisissez ce type de développement, vous pouvez ouvrir les fichiers exportés dans un espace de travail VSCode et vous connecter à GitHub, par exemple.

Étape 4.2 - Connection à un serveur et à un espace de noms pour développer sur le serveur (ma préférence)

Ouvrez l'onglet Intersystèmes dans la section Serveurs et choisissez un serveur.

Vous pouvez choisir un projet et cliquer sur le crayon pour l'ouvrir en mode édition sur votre espace de travail ou ou sur l'œil pour l'ouvrir en mode lecture.

De même, vous pouvez choisir entre les mêmes options directement sur l'espace de noms pour ouvrir tous ses fichiers dans l'espace de travail (ma préférence)

Dans les deux cas, il se peut que l'on vous demande à nouveau un nom d'utilisateur et un mot de passe. 
 

Étape 5 - Codage

Dans l'onglet de l'explorateur, après la dernière étape, votre espace de travail ressemble à l'image ci-dessous.

Vous pouvez créer un fichier et de nouveaux dossiers en cliquant avec le bouton droit de votre souris sur un répertoire, en sélectionnant "nouveau fichier" ("new file") et en le nommant avec le modèle FOLDERNAME.FILENAME.cls. En d'autres termes, vous devez nommer les dossiers en séparant les noms par des points. Le nom précédant le dernier point sera le nom du fichier, et le nom suivant le dernier point sera l'extension des fichiers COS, cls.
Par exemple, vous pouvez nommer votre fichier comme dans l'image ci-dessous, "folderone.foldertwo.filename.cls", dans le dossier User, et cliquer sur "Enter".

Ainsi, le répertoire et le fichier suivants devraient être créés automatiquement.

Maintenant, vous êtes prêt à coder comme vous le feriez en Studio.

 

Quelques conseils supplémentaires

- Vous pouvez appuyer sur Ctrl+Shift+V pour ouvrir les fichiers .int.

- Attention: contrairement à Studio, Ctrl + S dans VSCode compilera le fichier en plus de l'enregistrer.

- Maintenant, je vais vous montrer la meilleure fonctionnalité que j'ai rencontrée jusqu'à présent en codant avec VSCode. C'est de l'or en barre. Imaginez que vous ayez une instance de développeur pour les premières phases d'un projet, une autre instance pour les tests avec vos clients, et peut-être même encore une autre pour la production. Il peut être fastidieux de migrer les fichiers et les modifications entre les instances de Studio. Cependant, si vous utilisez VSCode, c'est assez simple.

Dans l'onglet Explorateur (Explorer), ouvrez la source et le fichier cible. Par exemple, si j'ai un fichier définissant des  données persistantes, et que j'ai ajouté des champs sur mon serveur de développement, et que je souhaite les transférer sur mon serveur de production, j'ouvrirai le fichier de développement et le fichier de production pour les mêmes données persistantes (une discussion sur l'ouverture de plusieurs serveurs ou espaces de noms est abordée dans la rubrique suivante).

Une fois que les deux sont ouverts, ils apparaissent dans la section "Éditeurs ouverts" ("Open Editors").

Sélectionnez le fichier source avec le bouton droit de votre souris et cliquez sur "Sélectionner pour comparer" ("Select to compare"). Cliquez avec le bouton droit sur le fichier cible et choisissez "Comparer avec sélectionné" ("Compare with selected").
BOUM! Vous aurez alors ce magnifique fichier de comparaison ouvert, comme illustré ci-dessous.

La barre située à droite de l'écran indique les différences entre les fichiers, et vous pouvez l'utiliser pour naviguer simultanément entre eux. Les couleurs rouge et verte signifient que quelque chose diffère dans les fichiers. Le rouge signifie que le fichier source contient du texte qui ne figure pas dans le fichier cible. Le vert signifie que le fichier cible contient du texte qui ne figure pas dans le fichier source. 

Cliquez sur les flèches du milieu pour transférer les modifications de la source vers la cible.

En outre, cliquez sur la flèche biface dans le coin supérieur droit de l'écran pour changer les côtés des fichiers : la source sera la cible et vice versa.

En appuyant sur Ctrl+S lorsque l'éditeur est ouvert, les deux fichiers sont enregistrés et compilés.

- Vous pouvez ouvrir plusieurs instances et espaces de noms à la fois dans votre espace de travail, tant que votre ordinateur peut le gérer. Cependant, pour que vous puissiez vous faire une idée, ces dernières semaines, j'ai travaillé avec deux espaces de noms lourds provenant de différentes instances, Studio, Microsoft Teams (qui est scandaleusement lourd), et deux autres applications pour se connecter à la base de données et aux API, le tout en même temps, avec un ordinateur ayant la configuration suivante: 8 Go de RAM et CPU de 2,10 GHz. La seule chose qui a parfois été un peu lente, c'est Studio.

Poursuite de la discussion

J'aimerais parler de débogage avec VSCode. Je n'ai pas encore trouvé quelqu'un qui l'apprécie plus que les outils de débogage de Studio. Personnellement moi, je ne suis pas très habitué à utiliser les outils de débogage VSCode avec les produits InterSystems, et même si Studio est beaucoup plus lourd, je préfère le laisser ouvert lorsque j'ai besoin de déboguer. Comment combinez-vous ces technologies? Quels sont les outils que vous préférez?

0
0 83
Article Pierre LaFay · Fév 18, 2024 2m read

InterSystems FAQ rubric

Un outil (l'utilitaire ^GLOBUFF) est disponible pour vérifier l'utilisation du cache de la base de données pour chaque variable globale.

Vous pouvez exécuter l'utilitaire directement ou par programme dans l'espace de noms %SYS.

Voici comment exécuter l'utilitaire directement :

0
0 64
Question Sylvain Guilbaud · Août 23, 2023

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

exemple :

LOAD DATA .../...
USING
{
  "from": {
    "file": {
       "dateformat": "DD/MM/YYYY"
    }
  }
}
3
0 88
Article Andrii Mishchenko · Nov 28, 2023 2m read

GmOwl est une solution qui offre une plateforme d'apprentissage organisée et engageante. Il a été développé pour répondre au besoin croissant d'outils d'apprentissage offrant un environnement de quiz polyvalent qui répond aux exigences des utilisateurs.

L'objectif principal de GmOwl est d'offrir une expérience utilisateur aux personnes participant à des quiz tout en donnant aux administrateurs un contrôle complet sur le contenu et l'engagement des utilisateurs.

Si vous appréciez mon application, n'oubliez pas de la soutenir lors du concours.

0
0 65
Article Sylvain Guilbaud · Août 25, 2023 1m read

Rubrique FAQ InterSystems

Vous pouvez voir l'espace disponible libre pour la base de données à l'aide du bouton radio « Vue de l'espace libre » dans le portail de gestion : Exploitation du système > Bases de données.

Et il peut être obtenu par programmation par la requête FreeSpace de la classe système SYS.Database.

0
0 70
Article Guillaume Rongier · Fév 24, 2023 4m read

Je vous présente mon nouveau projet, qui est irissqlcli, REPL (Read-Eval-Print Loop) pour InterSystems IRIS SQL

  • Mise en évidence de la syntaxe
  • Suggestions (tableaux, fonctions)
  • Plus de 20 formats de sortie
  • Support de stdin
  • Sortie vers des fichiers 

L'installez avec pip

pip install irissqlcli

Ou lancez avec docker

docker run -it caretdev/irissqlcli irissqlcli iris://_SYSTEM:SYS@host.docker.internal:1972/USER

Connection à IRIS

$ irissqlcli iris://_SYSTEM@localhost:1972/USER -W
Password for _SYSTEM:
Server:  InterSystems IRIS Version 2022.3.0.606 xDBC Protocol Version 65
Version: 0.1.0
[SQL]_SYSTEM@localhost:USER> select $ZVERSION
+---------------------------------------------------------------------------------------------------------+
| Expression_1                                                                                            |
+---------------------------------------------------------------------------------------------------------+
| IRIS for UNIX (Ubuntu Server LTS for ARM64 Containers) 2022.3 (Build 606U) Mon Jan 30202309:05:12 EST |
+---------------------------------------------------------------------------------------------------------+
1 row in set
Time: 0.063s
[SQL]_SYSTEM@localhost:USER> help
+----------+-------------------+------------------------------------------------------------+
| Commande  | Raccourci          | Description                                                |
+----------+-------------------+------------------------------------------------------------+
| .exit    | \q                | Sortie.                                                      |
| .mode    | \T                | Modifier le format de tableau utilisé pour les résultats.            |
| .once    | \o [-o] filename  | Ajout du résultat suivant à un fichier de sortie (écraser en utilisant -o). |
| .schemas | \ds               | Liste des schémas.                                             |
| .tables  | \dt [schema]      | Liste des tableaux.                                               |
| \e       | \e                | Commande d'édition avec éditeur (utilise $EDITOR).                   |
| help     | \?                | Montre cette utilité.                                            |
| nopager  | \n                | Désactiver le pager, imprimer vers stdout.                            |
| notee    | notee             | Arrête l'écriture des résultats dans un fichier de sortie.                    |
| pager    | \P [command]      | Definition du PAGER. Impression des résultats de la requête via PAGER.              |
| prompt   | \R                | Modification du format de l'invite.                                      |
| quit     | \q                | Quit.                                                      |
| tee      | tee [-o] filename | Ajout de tous les résultats à un fichier de sortie (écraser en utilisant -o). |
+----------+-------------------+------------------------------------------------------------+
Time: 0.012s
[SQL]_SYSTEM@localhost:USER>
$ irissqlcli --help
Usage: irissqlcli [OPTIONS] [URI] [NOM D'UTILISATEUR]

Options:
  -h, --host TEXT         Adresse hôte de l'instance IRIS.
  -p, --port INTEGER      Numéro de port sur lequel l'instance IRIS està l'écoute.
  -U, --username TEXT     Nom d'utilisateur pour se connecter à l'instance IRIS.
  -u, --user TEXT         Nom d'utilisateur pour se connecter à l'instance IRIS.
  -W, --password          Invite de mot de passe forcée.
  -v, --version           Version de irissqlcli.
  -n, --nspace TEXT       nom de l'espace de nom auquel se connecter.
  -q, --quiet             Mode silencieux, saut de l'intro au démarrage etau revoir à la
                          sortie.
  -l, --logfile FILENAME  Enregistrez chaque requête etses résultats dans un fichier.
  --irissqlclirc FILE     L'emplacement du fichier irissqlclirc.
  --auto-vertical-output  Passage automatique en mode de sortie verticale sile résultat est plus large que la largeur du terminal. résultat estplus large que la largeur du terminal.
  --row-limit INTEGER     Définissez le seuil pour l'invite de limite de rangée de . Utilisez 0 pour désactiver
                          l'invite.
  -t, --table             Affichez la sortie du lot au format tableau .
  --csv                   Affichez la sortie du lot in au format CSV.
  --warn / --no-warn      Avertissement avant d'exécuter une requête destructive.
  -e, --execute TEXT      Exécutez la commande etquitter.
  --help                  Affichage de ce message et sortie.

ou en Python Embedded (nécessite de %Service_CallIn activé)

$ irissqlcli iris+emb:///USER
Server:  IRIS for UNIX (Ubuntu Server LTS for ARM64 Containers) 2022.2 (Build 368U) Fri Oct 21202216:39:41 EDT
Version: 0.1.0
[SQL]irisowner@/usr/irissys/:USER>

L'application supporte stdin, ce qui vous permet d'envoyer un fichier SQL avec un tas de requêtes SQL et de commandes irissqcli. Par exemple, cette commande produira 3 fichiers dans différents formats (parmi plus de 20 formats disponibles)

$ cat <select top 10 TABLE_SCHEMA,TABLE_NAME
from information_schema.tables
orderby TABLE_SCHEMA,TABLE_NAME;
notee;

.mode latex;
tee -o test.tex;
select top 10 TABLE_SCHEMA,TABLE_NAME
from information_schema.tables
orderby TABLE_SCHEMA,TABLE_NAME;
notee;

.mode html;
tee -o test.html;
select top 10 TABLE_SCHEMA,TABLE_NAME
from information_schema.tables
orderby TABLE_SCHEMA,TABLE_NAME;
notee;

EOF

De plus, il est possible d'exécuter un terminal web avec docker

docker run -d --name irissqlcli \
  --restart always \
  -p 7681:7681\
  caretdev/irissqlcli-web irissqlcli iris://_SYSTEM:SYS@host.docker.internal:1972/USER

http://localhost:7681/

Et avec docker-compose

version: '3'
services:
  iris:
    image: intersystemsdc/iris-community
    ports:
      - 1972
      - 52773
    command:
      - -a
      - '##class(Security.Users).UnExpireUserPasswords("*")'
  cli:
    image: caretdev/irissqlcli-web
    ports:
      - 7681:7681
    environment:
      - IRIS_HOSTNAME:iris
      - IRIS_PORT=1972
      - IRIS_NAMESPACE=USER
      - IRIS_USERNAME=_SYSTEM
      - IRIS_PASSWORD=SYS
0
0 105
Annonce Irène Mykhailova · Jan 17, 2023

Salut les développeurs,

Nous aimerions vous inviter à participer à notre prochain concours dédié à la création d'outils utiles pour faciliter la vie de vos collègues développeurs :

🏆 Concours d'outils de développement InterSystems 🏆

Soumettez une application qui aide à développer plus rapidement, contribue à un code plus qualitatif et aide au test, au déploiement, au support ou à la surveillance de votre solution avec InterSystems IRIS.

Durée: du 23 janvier au 12 février 202

Prix: $13,500!

 

0
0 291
Article Guillaume Rongier · Juin 15, 2022 17m read

Introduction

Nous sommes à l'ère de l'économie multiplateforme et les API sont la "colle " de ce scénario numérique. Étant donné leur importance, les développeurs les considèrent comme un service ou un produit à consommer. Par conséquent, l'expérience d'utilisation est un facteur crucial de leur succès.

Afin d'améliorer cette expérience, des normes de spécification telles que la spécification OpenAPI (OAS) sont de plus en plus adoptées dans le développement des API RESTFul.

IRIS ApiPub - qu'est-ce que c'est ?

IRIS ApiPub est un projet de type code source ouvert Open Source dont l'objectif principal est de publier automatiquement les API RESTful créées avec la technologie Intersystems IRIS, de la manière la plus simple et la plus rapide possible en utilisant la norme Open Specification API (OAS) standard, version 3.0.

Il permet à l'utilisateur de se concentrer sur la mise en œuvre et les règles métier (méthodes Web) de l'API, en abstrayant et en automatisant les autres aspects liés à la documentation, l'exposition, l'exécution et la surveillance des services.

Ce projet comprend également un exemple complet de mise en œuvre (apiPub.samples.api) de la Swagger Petstore, qui est l'échantillon officiel de swagger.

Testez-le avec vos services SOAP actuels

Si vous avez déjà publié des services SOAP, vous pouvez les tester en utilisant Rest/JSON avec OAS 3.0.

Lors de la publication de méthodes avec des types complexes, la classe de l'objet doit être une sous-classe de %XML.Adapter. De cette manière, les services SOAP précédemment installés sont rendus automatiquement compatibles.

Surveillez vos API avec IRIS Analytics

Activez la surveillance des API pour gérer et suivre tous vos Appels Rest. Vous pouvez également configurer vos propres indicateurs.

Installation

  1. Effectuez un clone/git pull depuis le dépôt dans le répertoire local.
$ git clone https://github.com/devecchijr/apiPub.git
  1. Ouvrez le terminal dans ce répertoire et exécutez la commande suivante :
$ docker-compose up -d
  1. Exécutez le conteneur IRIS avec le projet :
$ docker-compose up -d

Test de l'application

Ouvrez l'URL http://localhost:52773/swagger-ui/index.html de swagger

Essayez d'exécuter une opération à l'aide de l'API Petstore, par exemple en effectuant un postage d'un nouveau pet.

Consultez la table de bord du moniteur apiPub. Essayez d'explorer le domaine petStore pour explorer et analyser les messages.

Modifiez ou créez des méthodes dans la classe apiPub.samples.api et revenez à la documentation générée. Notez que toutes les modifications sont automatiquement reflétées dans la documentation OAS ou dans les schémas.

Publiez votre API selon la norme OAS 3.0 en seulement 3 étapes :

Étape 1

Définissez la classe d'implémentation de votre API et balises les méthodes avec l'attribut [WebMethod]

Cette étape n'est pas nécessaire si vous disposez déjà d'une mise en œuvre de WebServices.

Étape 2

Créez une sous-classe de apiPub.core.service et définissez sa propriété DispatchClass comme la classe Implementation créée précédemment. Incluez également le chemin de la documentation OAS 3.0. Si vous le souhaitez, pointez vers la classe apiPub.samples.api (PetStore).

Étape 3

Créez une application Web et définissez la classe de répartition comme la classe de service créée ci-dessus.

Utilisation de Swagger

Avec iris-web-swagger-ui vous pouvez exposer votre spécification de service. Il vous suffit de pointer vers le chemin de la documentation et... VOILÁ!!

Définition de l'en-tête de la spécification OAS

Il existe deux façons de définir l'en-tête OAS 3.0 :

La première consiste à créer un bloc JSON XDATA nommé apiPub dans la classe d'implémentation. Cette méthode autorise plus d'une balise et la modélisation est compatible avec la norme OAS 3.0. Les propriétés qui peuvent être personnalisées sont info, tags et servers.

XData apiPub [ MimeType = application/json ]
{
    {
        "info" : {
            "description" : "Il s'agit d'un exemple de serveur Petstore.  Vous pouvez en savoir plus sur Swagger à l'adresse suivante\n[http://swagger.io](http://swagger.io) or on\n[irc.freenode.net, #swagger](http://swagger.io/irc/).\n",
            "version" : "1.0.0",
            "title" : "IRIS Petstore (Dev First)",
            "termsOfService" : "http://swagger.io/terms/",
            "contact" : {
            "email" : "apiteam@swagger.io"
            },
            "license" : {
            "name" : "Apache 2.0",
            "url" : "http://www.apache.org/licenses/LICENSE-2.0.html"
            }
        },
        "tags" : [ {
            "name" : "pet",
            "description" : "Tout sur vos Pets",
            "externalDocs" : {
            "description" : "Pour en savoir plus",
            "url" : "http://swagger.io"
            }
        }, {
            "name" : "store",
            "description" : "Accès aux commandes du Petstore"
        }, {
            "name" : "user",
            "description" : "Opérations sur l'utilisateur",
            "externalDocs" : {
            "description" : "En savoir plus sur notre magasin",
            "url" : "http://swagger.io"
            }
        } ]
    }
}

La seconde méthode consiste à définir des paramètres dans la classe d'implémentation, comme dans l'exemple suivant :

Parameter SERVICENAME = "My Service";

Parameter SERVICEURL = "http://localhost:52776/apipub";

Parameter TITLE As %String = "REST aux API SOAP";

Parameter DESCRIPTION As %String = "API pour le proxy des services Web SOAP via REST";

Parameter TERMSOFSERVICE As %String = "http://www.intersystems.com/terms-of-service/";

Parameter CONTACTNAME As %String = "John Doe";

Parameter CONTACTURL As %String = "https://www.intersystems.com/who-we-are/contact-us/";

Parameter CONTACTEMAIL As %String = "support@intersystems.com";

Parameter LICENSENAME As %String = "Copyright InterSystems Corporation, tous droits réservés.";

Parameter LICENSEURL As %String = "http://docs.intersystems.com/latest/csp/docbook/copyright.pdf";

Parameter VERSION As %String = "1.0.0";

Parameter TAGNAME As %String = "Services";

Parameter TAGDESCRIPTION As %String = "Services d'héritage";

Parameter TAGDOCSDESCRIPTION As %String = "Pour en savoir plus";

Parameter TAGDOCSURL As %String = "http://intersystems.com";

Personnalisez vos API

Vous pouvez personnaliser plusieurs aspects de l'API, tels que les balises, les chemins et les verbes. Pour cela, vous devez utiliser une notation spéciale, déclarée dans le commentaire de la méthode personnalisée.

Syntaxe:

/// @apiPub[assignment clause]
[Method/ClassMethod] methodName(params as type) As returnType {

}

Toutes les personnalisations présentées à titre d'exemple dans cette documentation se trouvent dans la classe apiPub.samples.api.

Personnalisation des verbes

Lorsqu'aucun type complexe n'est utilisé comme paramètre d'entrée, apiPub attribue automatiquement le verbe Get. Dans le cas contraire, le verbe Post sera attribué.

Si vous souhaitez personnaliser la méthode, ajoutez la ligne suivante aux commentaires de la méthode.

/// @apiPub[verb="verb"]

verbe peut être get, post, put, delete ou patch.

Exemple:

/// @apiPub[verb="put"]

Personnalisation des chemins

Cet outil attribue automatiquement des chemins ou des routages aux Méthodes Web. Il utilise le nom de la méthode comme chemin, par défaut.

Si vous souhaitez personnaliser le chemin, ajoutez la ligne suivante aux commentaires de la méthode.

/// @apiPub[path="path"]

chemin peut être n'importe quelle valeur précédée d'un slash, tant qu'il n'entre pas en conflit avec un autre chemin dans la même classe d'implémentation.

Exemple:

/// @apiPub[path="/pet"]

Une autre utilisation très courante du chemin est de définir un ou plusieurs paramètres dans le chemin lui-même. Pour cela, le nom du paramètre défini dans la méthode doit être entouré d'accolades.

Exemple:

/// @apiPub[path="/pet/{petId}"]
Method getPetById(petId As %Integer) As apiPub.samples.Pet [ WebMethod ]
{
}

Lorsque le nom du paramètre interne diffère du nom du paramètre affiché, le nom peut être égalisé selon l'exemple suivant :

/// @apiPub[path="/pet/{petId}"]
/// @apiPub[params.pId.name="petId"]
Method getPetById(pId As %Integer) As apiPub.samples.Pet [ WebMethod ]
{
}

Dans l'exemple ci-dessus, le paramètre interne pId est affiché sous la forme petId.

Personnalisation des balises

Il est possible de définir le tag (regroupement) de la méthode lorsque plus d'un tag est défini dans l'en-tête.

/// @apiPub[tag="value"]

Exemple:

/// @apiPub[tag="user"]

Personnalisation du succès Code d'état

Si vous souhaitez modifier le Code d'état de réussite de la méthode, qui est 200 par défaut, il convient d'utiliser la notation suivante.

/// @apiPub[successfulCode="code"]

Exemple:

/// @apiPub[successfulCode="201"]

Personnalisation de l'exception Code d'état

Cet outil traite toutes les exceptions comme Code d'état 500 par défaut. Si vous souhaitez ajouter de nouveaux codes d'exception à la documentation, utilisez la notation suivante.

/// @apiPub[statusCodes=[{code:"code",description:"description"}]]

Où la propriété statusCodes est un tableau d'objets contenant le code et la description.

Exemple:

/// @apiPub[statusCodes=[
/// {"code":"400","description":"Invalid ID supplied"}
/// ,{"code":"404","description":"Pet not found"}]
/// ]

Lorsque vous soulevez l'exception, incluez Code d'état dans la description de l'exception entre les caractères "<" et ">".

Exemple:

Throw ##Class(%Exception.StatusException).CreateFromStatus($$$ERROR($$$GeneralError, "<400> Invalid ID supplied"))}

Voir la méthode getPetById de la classe apiPub.samples.api

Marquer l'API comme déconseillé

Pour que l'API soit affichée comme déconseillée, la notation suivante doit être utilisée :

/// @apiPub[deprecated="true"]

Personnalisation de l'operationId

Selon la spécification OAS, operationId est une chaîne unique utilisée pour identifier une API ou une opération. Dans cet outil, il est utilisé dans le même but lors des opérations de surveillance et suivi operations.

Par défaut, elle porte le même nom que la méthode de la classe d'implémentation.

Si vous souhaitez le modifier, utilisez la notation suivante

/// @apiPub[operationId="updatePetWithForm"]

Modification du jeu de caractères de la méthode

Le jeu de caractères par défaut est généralement défini à l'aide du paramètre CHARSET de la classe de service, décrit dans étape 2. Si vous souhaitez personnaliser le jeu de caractères d'une méthode, vous devez utiliser la notation suivante ::

/// @apiPub[charset="value"]

Exemple:

/// @apiPub[charset="UTF-8"]

Personnalisation des noms et autres caractéristiques des paramètres

Vous pouvez personnaliser plusieurs aspects des paramètres d'entrée et de sortie de chaque méthode, tels que les noms et les descriptions qui seront affichés pour chaque paramètre.

Pour personnaliser un paramètre spécifique, utilisez la notation suivante

/// @apiPub[params.paramId.property="value"]

ou pour des réponses :

/// @apiPub[response.property="value"]

Exemple:

/// @apiPub[params.pId.name="petId"]
/// @apiPub[params.pId.description="ID of pet to return"]

Dans ce cas, le nom petId et la description ID du pet à rendre sont attribués au paramètre défini comme pId

Lorsque la personnalisation n'est pas spécifique à un paramètre donné, la notation suivante est utilisée

/// @apiPub[params.property="value"]

Dans l'exemple suivant, la description Ceci ne peut être fait que par l'utilisateur connecté est attribuée à l'ensemble de la demande, et pas seulement à un seul paramètre :

/// @apiPub[params.description="Ceci ne peut être fait que par l'utilisateur connecté."]

Autres propriétés qui peuvent être personnalisées pour des paramètres spécifiques

Utilisez la notation suivante pour les paramètres d'entrée ou de sortie :

/// @apiPub[params.paramId.property="value"]

Pour les reponses:

/// @apiPub[response.property="value"]

Propriété
required: "true" si le paramètre est obligatoire. Tous les paramètres de type path sont déjà automatiquement requis
schema.items.enum: afficher les énumérateurs pour les types %String ou %Library.DynamicArray. Voir la méthode findByStatus de la classe apiPub.samples.api
schema.default: Pointe vers une valeur par défaut pour les énumérateurs
inputType: Pour les types simples, il s'agit par défaut d'un paramètre de requête. Pour les types complexes (corps), il s'agit par défaut de application/json. Dans le cas où vous souhaitez changer le type d'entrée, vous pouvez utiliser ce paramètre. Exemple d'utilisation : Téléchargement d'une image, qui n'est généralement pas de type JSON. Voir la méthode uploadImage de la classe apiPub.samples.api.
outputType: Pour les types %Status, la valeur par défaut est header. Pour les autres types, la valeur par défaut est application/json. Si vous souhaitez modifier le type de sortie, vous pouvez utiliser ce paramètre. Exemple d'utilisation : Retourner un jeton ("text/plain"). Voir la méthode loginUser de la classe apiPub.samples.api

Relier des schémas analysables à des types JSON dynamiques (%Library.DynamicObject)

Vous pouvez relier les schémas OAS 3.0 aux types dynamiques internes

L'avantage d'associer le schéma au paramètre, outre le fait d'informer l'utilisateur sur une spécification d'objet requise, est l'analyse automatique de la demande, qui est effectuée pendant l'appel API. Si l'utilisateur de l'API, par exemple, soumet une propriété qui ne figure pas dans le schéma, ou envoie une date dans un format non valide, ou n'inclut pas une propriété obligatoire, une ou plusieurs erreurs seront renvoyées à l'utilisateur contenant des informations sur ces problèmes.

La première étape consiste à inclure le schéma souhaité dans le bloc XDATA, comme indiqué ci-dessous. Dans ce cas, le schéma appelé User peut être utilisé par n'importe quelle méthode. Il doit suivre les mêmes règles que celles utilisées dans la modélisation [OAS 3.0] (https://swagger.io/docs/specification/data-models/).

XData apiPub [ MimeType = application/json ]
{
    {
        "schemas": {
            "User": {
                "type": "object",
                "required": [
                    "id"
                ],
                "properties": {
                    "id": {
                        "type": "integer",
                        "format": "int64"
                    },
                    "username": {
                        "type": "string"
                    },
                    "firstName": {
                        "type": "string"
                    },
                    "lastName": {
                        "type": "string"
                    },
                    "email": {
                        "type": "string"
                    },
                    "password": {
                        "type": "string"
                    },
                    "phone": {
                        "type": "string"
                    },
                    "userStatus": {
                        "type": "integer",
                        "description": "(short) User Status"
                    }
                }
            }            
        }
    }
}

La deuxième étape consiste à associer le nom du schéma renseigné à l'étape précédente au paramètre interne de type %Library.DynamicObject en utilisant la notation suivante :

/// @apiPub[params.paramId.schema="schema name"]

Exemple associant le paramètre user au schéma User :

/// @apiPub[params.user.schema="User"]
Method updateUserUsingOASSchema(username As %String, user As %Library.DynamicObject) As %Status [ WebMethod ]
{
    code...
}

Exemple de soumission d'une requête avec une erreur. La propriété username2 n'existe pas dans le schéma User. La propriété id n'est pas non plus définie alors qu'elle est requise :

{
  "username2": "devecchijr",
  "firstName": "claudio",
  "lastName": "devecchi junior",
  "email": "devecchijr@gmail.com",
  "password": "string",
  "phone": "string",
  "userStatus": 0
}

Exemple d'une réponse avec une erreur :

{
  "statusCode": 0,
  "message": "ERROR #5001: <Bad Request> Path User.id is required; Invalid path: User.username2",
  "errorCode": 5001
}

Voir les méthodes updateUserUsingOASSchema et getInventory de la classe apiPub.samples.api. La méthode getInventory est un exemple de schéma associé à la sortie de la méthode (réponse), elle n'est donc pas analysable.

Générer le schéma OAS 3.0 à partir d'un objet JSON

Pour faciliter la génération du schéma OAS 3.0, vous pouvez utiliser les éléments suivants ::

Définissez une variable avec un échantillon de l'objet JSON.

set myObject = {"prop1":"2020-10-15","prop2":true, "prop3":555.55, "prop4":["banana","orange","apple"]}

Utilisez la méthode utilitaire de la classe apiPub.core.publisher pour générer le schéma :

do ##class(apiPub.core.publisher).TemplateToOpenApiSchema(myObject,"objectName",.schema)

Copiez et collez le schéma renvoyé dans le bloc XDATA :

Exemple:

XData apiPub [ MimeType = application/json ]
{
    {
        "schemas": {
            {
                "objectName":   
                {
                    "type":"object",
                    "properties":{
                        "prop1":{
                            "type":"string",
                            "format":"date",
                            "example":"2020-10-15"      
                        },
                        "prop2":{
                            "type":"boolean",
                            "example":true
                        },
                        "prop3":{
                            "type":"number",
                            "example":555.55
                        },
                        "prop4":{
                            "type":"array",
                            "items":{
                                "type":"string",
                                "example":"apple"
                            }
                        }
                    }
                }
            }
        }
    }
}

Activer la surveillance (facultatif)

1 - Ajoutez et activez les composants suivants dans votre Production (IRIS Interopérabilité)

ComposantType
apiPub.tracer.bmService (BS)
apiPub.tracer.bsService (BS)
apiPub.tracer.boOpération (BO)

2 - Activez la surveillance de la classe décrite dans [l'étape 2] (https://github.com/devecchijr/apiPub#passo-2)

Le paramètre Traceable doit être activé.

Parameter Traceable As %Boolean = 1;

Parameter TracerBSName = "apiPub.tracer.bs";

Parameter APIDomain = "samples";

Le paramètre APIDomain est utilisé pour regrouper les API à surveiller.

3 - Importez les tableaux de bord

zn "IRISAPP"
Set sc = ##class(%DeepSee.UserLibrary.Utils).%ProcessContainer("apiPub.tracer.dashboards",1)

Vous pouvez également créer d'autres tableaux de bord, basés sur le cube apiPub Monitor.

Utilisez cet outil conjointement avec Intersystems API Manager

Acheminez vos API générées et bénéficiez de l'[Intersystems API Manager] (https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=AFL_IAM)

Compatibilité

ApiPub est compatible avec Intersystems IRIS ou Intersystems IRIS pour la santé, à partir de la version 2018.1.

Dépôt

Github: apiPub

2
0 116
Annonce Irène Mykhailova · Avr 6, 2022

Nous sommes ravis de vous présenter notre nouveau portail de commentaires, InterSystems Ideas !

Nous souhaitons améliorer nos mécanismes de rétroaction afin que vous puissiez suggérer des façons dont nos produits pourraient évoluer pour répondre à vos défis commerciaux. Les questions sur la Communauté des Développeurs sont un excellent moyen d'interagir avec vos pairs autour d'un problème de codage spécifique, et le Support Client est, comme toujours, le moyen de résoudre un problème immédiat.

Ce nouveau portail est destiné à capturer des idées de niveau supérieur. Moins sur la façon dont vous faites quelque chose aujourd'hui, et plus sur la façon dont vous aimeriez voir le produit mieux fonctionner à l'avenir. Il vous permet de publier vos propres commentaires et de commenter/voter pour les commentaires fournis par d'autres. InterSystems peut afficher tous les commentaires que vous soumettez, y répondre directement et mettre à jour le statut de vos éléments de commentaires au fur et à mesure que nous progressons sur vos demandes.

Alors jetez-y un coup d'œil, apportez vos idées et marquez-le pour l'avenir. Merci!

0
0 68