#Embedded Python

0 Abonnés · 83 Publications

Embedded Python fait référence à l'intégration du langage de programmation Python dans le noyau InterSystems IRIS, permettant aux développeurs d'opérer avec des données et de développer une logique d'entreprise pour les applications côté serveur en utilisant Python.

Documentation.

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
Article Lorenzo Scalese · Oct 14, 2022 6m read

Présentation générale

Il y a trois ans, nous avons commencé à utiliser Azure Service Bus (ASB) comme solution de messagerie d'entreprise. Elle est utilisée pour publier et consommer des données entre de nombreuses applications de l'entreprise. Comme le flux de données est complexe et les données d'une application sont généralement nécessaires à plusieurs applications, le modèle "éditeur" ---> "abonnés multiples" était parfaitement adapté. L'utilisation d'ASB dans l'organisation donne des dizaines de millions de messages par jour, tandis que la plate-forme IRIS a environ 2-3 millions de messages par jour.

Le problème d'ASB

Lorsque nous avons commencé l'intégration d'ASB, nous avons constaté que le protocole AMQP n'est pas " prêt à l'emploi " pour le déploiement d'IRIS. Nous avons donc cherché une solution alternative pour pouvoir communiquer avec l'ASB.

La solution

Nous avons développé un service Windows local (en .NET et Swagger) qui effectuait la communication réelle avec l'ASB. Il était installé sur la même machine qu'IRIS. Nous avons envoyé les messages à ASB à ce service local (en utilisant : localhost et en faisant une API REST), et ce service a fait le reste. Cette solution a bien fonctionné pendant quelques années, mais il était très difficile de surveiller le trafic, de déboguer les messages "perdus" et de mesurer la performance globale. Le fait que nous avions ce service Windows local comme "un homme au milieu" n'est pas toujours la meilleure architecture.

Programme d'accès anticipé à IRIS Embedded Python (EAP)

On m'a demandé (ou on m'a proposé, je ne sais plus) de participer au programme de diffusion anticipée (ERP) pour le programme Embedded Python. C'était très excitant pour moi, de pouvoir tester de nouvelles fonctionnalités, de donner du feedback à l'équipe de développement. C'était également très agréable de participer activement au développement de ce produit et de l'influencer. À ce stade, nous avons commencé à tester le programme Embedded Python pour l'ERP et nous avons décidé de vérifier si nous pouvions élargir le programme Embedded Python pour résoudre des problèmes "réels". Prendre la "connectivité directe entre IRIS et ASB" dans ce but était tout à fait logique.

Le POC (preuve de concept**)**

Nous avons commencé à coder la solution et avons découvert que l'utilisation de la bibliothèque ASB de Microsoft pour Python nous faciliterait la vie (et le codage). L'étape initiale était de développer une fonction qui peut se connecter à un sujet spécifique dans ASB et recevoir des messages. Cela a été fait assez rapidement (1 jour de développement) et ensuite nous sommes passés à la phase de "publication" (envoi à ASB).

Quelques jours supplémentaires nous ont permis de développer une " enveloppe " complète pour ces fonctions d'envoi et de réception : nous avons construit ce qui suit :

  • Une "base de transit" adéquate pour contrôler le flux des messages entrants et sortants
  • Un mécanisme central pour stocker le trafic d'entrée et de sortie de l'ASB afin de pouvoir disposer de statistiques et de mesures de performance appropriées
  • Une page de surveillance du CSP pour activer/désactiver les services, montrer les statistiques et donner des alertes en cas de problème

La mise en œuvre

Configuration préalable

Avant d'utiliser les bibliothèques ASB de Microsoft pour Python, il est nécessaire de les installer dans un dossier dédié ..\python.
Nous avons choisi d'utiliser ../mge/python/ mais vous pouvez utiliser n'importe quel autre dossier (initialement c'était un dossier vide).
Ces commandes doivent être exécutées à partir d'un CMD élevé (sous Windows) ou en utilisant un utilisateur élevé (sous Linux) :

..\bin\irispip install --target ..\mgr\python asyncio

..\bin\irispip install --target ..\mgr\python azure.servicebus

Réception de messages à l'ASB (consommation)

Nous avons une méthode ClassMethod avec quelques paramètres :

  • Le topiId - L'ASB est divisée en thèmes à partir desquels vous consommez des messages
  • subscriptionName  - Nom de l'abonnement à Azure
  •  connectionString  - Pour pouvoir vous connecter à la rubrique à laquelle vous êtes abonné(e)

Notez que nous utilisons [ Language = python ] pour indiquer que cette ClassMethod est écrite en Python (!)

ClassMethod retrieveASB(topicId As %Integer = 1, topicName As %String = "", subscriptionName As %String = "", connectionString As %String = "", debug As %Integer = 1, deadLetter As %Integer = 0) As %Integer [ Language = python ]

Pour utiliser la bibliothèque ASB, nous devons d'abord importer les bibliothèques ServiceBusClient et ServiceBusSubQueue :

 d' azure.servicebus importer ServiceBusClient,ServiceBusSubQueue

pour pouvoir interagir avec IRIS (par exemple pour exécuter du code), nous devons également :

importer iris  

À ce stade, nous pouvons utiliser les bibliothèques ASB :

avec ServiceBusClient.from_connection_string(connectionString) en tant que client:

    avec client.get_subscription_receiver(topicName, subscriptionName, max_wait_time=1, sub_queue=subQueue) en tant que récepteur :

        pour msg dans récepteur:

À ce stade, nous avons un objet Python "msg" (flux) que nous pouvons transmettre (en tant que flux bien sûr) à IRIS et stocker directement dans la base de données :

result = iris.cls("anyIRIS.Class").StoreFrom Python(stream)

Envoi de messages à l'ASB (publication)

Nous avons une ClassMethod avec quelques paramètres :

  • topicName  - La rubrique (Topic) sur laquelle nous voulons publier (ici, nous devons passer le nom et non l'identifiant)
  • connectionString  - Pour pouvoir vous connecter à la rubrique à laquelle vous êtes abonné(e)
  • JSONmessage  - le message que nous voulons envoyer (publier)

Notez que nous utilisons [ Language = python ] pour indiquer que cette ClassMethod est écrite en Python (!)

ClassMethod publishASB(topicName As %String = "", connectionString As %String = "", JSONmessage As %String = "") As %Status [ Language = python ]

Pour utiliser la bibliothèque ASB, nous devons d'abord importer les bibliothèques ServiceBusClient et ServiceBusMessage :

from azure.servicebus import ServiceBusClient, ServiceBusMessage

L'utilisation des bibliothèques de l'ASB est alors très facile :

try:

        résultat=1

        avec ServiceBusClient.from_connection_string(connectionString) en tant que client:

            avec client.get_queue_sender(topicName) en tant qu'expéditeur:

                single_message = ServiceBusMessage(JSONmessage)

                sender.send_messages(single_message)        

    sauf Exception comme e:

        publier(e)

        résultat=0

    retourner le résultat

Avantages de l'utilisation de la connectivité directe de l'ASB

  • Beaucoup plus rapide que l'utilisation de l'"ancienne alternative" du "service local de Windows"
  • Facile à surveiller, à collecter des statistiques, à déboguer les problèmes
  • La mise hors service de l'"homme du milieu" (service local de Windows) réduit un "point de défaillance" potentiel
  • La possibilité de gérer automatiquement les "lettres mortes" pour toute rubrique et de faire en sorte que l'ASB renvoie ces messages à la rubrique de l'abonné

Références

Je tiens à remercier @David.Satorres6134 (notre développeur principal pour IRIS) pour son énorme contribution à la conception, au codage et aux tests. Sans son aide, ce projet n'aurait pas été possible.

0
0 99
Annonce Irène Mykhailova · Oct 10, 2022

Salut la communauté,

Rejoignez-nous pour une rencontre de développeurs InterSystems pendant TechCrunch Disrupt 2022 !

Nous nous réunirons le mercredi 19 octobre au Bartlett Hall, situé au 242 O'Farrell St. (à quelques pâtés de maisons du Moscone Center) de 18 h 00 à 20 h 30 PT, où les conférenciers discuteront de la façon dont les développeurs peuvent apporter le code aux données, et non les données au code avec Embedded Python et Integrated ML sur InterSystems IRIS.

Nourriture et boissons seront servies accompagnées de discussions.

Ordre du jour:

0
0 34
Article Lorenzo Scalese · Sept 23, 2022 4m read

Bonjour la communauté,

Cet article montre comment créer des ressources pour le patient et l'observation du patient en utilisant l'application iris-fhir-client .
image

Je recommande de lire mon premier article sur cette application et de regarder le Video Youtube avant de continuer.

Donc commençons

1-Création d'une Ressource Patient

La fonction CreatePatient() de dc.FhirClient ci-dessous peut être utilisée pour créer une Ressource Patient

ClassMethod CreatePatient(givenName As %String, familyName As %String, birthDate As %String,gender As %String)

La fonction nécessite giveName, failyName, birthDate et sex pour créer la Resource Patient La commande ci-dessous créera le Patient

do ##class(dc.FhirClient).CreatePatient("PatientGN","PatientFN","2000-06-01","male")

image

Voici la fonction python dans le fichier irisfhirclient.py qui va créer le patient

import json
from fhirpy import SyncFHIRClient
from tabulate import tabulate
from fhirpy.base.searchset import Raw
import requests

def CreatePatient(givenName,familyName,birthDate,gender,url,api_key):
    headers = {"Content-Type":contentType,"x-api-key":api_key}
    client = SyncFHIRClient(url = url, extra_headers=headers)
    
    patient = client.resource("Patient")
    patient['name'] = [
        {
            'given': [givenName],
            'family': familyName,
            'use': 'official'
        }
    ]

    patient['birthDate'] = birthDate
    patient['gender'] = gender
    try:
        patient.save()
    except Exception as e:
        print("Error while creating Patient:" +str(e))       
        return
    print("Patient Created Successfully")    

 

2- Création d'une ressource pour l'observation des patients

Créons une Observation sur notre ressource patient nouvellement créée

La fonction CreateObservatoin() de dc.FhirClient ci-dessous peut être utilisée pour la Création des observations de patients
ClassMethod CreateObservation(patientId As %String, loincCode As %String, ObrCategory As %String, ObrValue As %Integer, ObrUOM As %String, effectiveDate As %String)

Paramètres

  • patientId est l'identifiant du patient
  • LioncCode est le Code Lionc, les détails sont présentés ici
  • ObrCategory est une catégorie d'observation, les détails sont disponibles ici
  • ObrValue est la valeur d'observation
  • ObrUOM est l'unité d'observation
  • EffectiveDate

La commande ci-dessous créera l'observation des Signes Vitaux du patient

do ##class(dc.FhirClient).CreateObservation("8111","8310-5","vital-signs",96.8,"degF","2022-01-22")

image

Énumérons les observations des patients

do ##class(dc.FhirClient).GetPatientResources("Observation","8111")

image

Voici la fonction python dans le fichier irisfhirclient.py qui va créer le patient

import json
from fhirpy import SyncFHIRClient
from tabulate import tabulate
from fhirpy.base.searchset import Raw
import requests

#Function to create Patient Observation
def CreateObservation(patientId,loincCode,ObrCategory,ObrValue,ObrUOM,effectiveDate,url,api_key):
    headers = {"Content-Type":contentType,"x-api-key":api_key}
    client = SyncFHIRClient(url = url, extra_headers=headers)
    observation = client.resource(
    'Observation',
    status='preliminary',
    category=[{
        'coding': [{
            'system': 'http://hl7.org/fhir/observation-category',
            'code': ObrCategory
        }]
    }],
    code={
        'coding': [{
            'system': 'http://loinc.org',
            'code': loincCode
        }]
    })
    observation['effectiveDateTime'] = effectiveDate
       
    observation['valueQuantity'] = {
    'system': 'http://unitsofmeasure.org',
    'value': ObrValue,
    'code': ObrUOM
    }
    
    #find the patient
    patient = client.resources('Patient').search(_id=patientId).first()
    observation['subject'] = patient.to_reference()
    
    try:
        observation.save()
    except Exception as e:
        print("Error while creating observation :"+ str(e))       
        return
    print("Patient Observation Created Successfully")

C'est comme ça

Si vous avez trouvé cette application utile, merci de voter pour mon application.

Merci!

0
0 88
Article Guillaume Rongier · Août 31, 2022 6m read

Nous continuons à observer les possibilités de Django, et son utilisation avec IRIS. Dans la première partie nous avons regardé comment définir des modèles et se connecter à des tableaux déjà existants dans IRIS, dans la suite nous avons étendu le portail d'administration intégré de Django, avec la possibilité de voir quelles données nous avons dans ces modèles, avec des filtres, l'édition et même la pagination.

Il est temps de passer à l'action, nous allons maintenant créer une API REST, sur Django, basée sur les mêmes données, que nous avons utilisées auparavant à partir du paquet posts-and-tags.

Pour ce faire, nous utiliserons Django REST Framework

Django REST Framework

Le cadre REST de Django est une boîte à outils puissante et flexible permettant de créer des API Web.

Quelques raisons pour lesquelles vous pourriez vouloir utiliser le cadre REST :

  • L'API navigable sur le Web est un avantage considérable pour vos développeurs.
  • Politiques d'authentification comprenant des paquets pour OAuth1a et OAuth2.
  • Sérialisation qui prend en charge les sources de données ORM et non ORM.
  • Personnalisable jusqu'en bas - utilisez simplement des visualisations régulières basées sur des fonctions si vous n'avez pas besoin des fonctionnalités les plus puissantes.
  • Une documentation complète et un support communautaire important.
  • Utilisé par des entreprises de renommée internationale, telles que Mozilla, Red Hat, Heroku et Eventbrite, qui lui font confiance.
Tout d'abord, nous devons mettre à jour notre fichier requirements.txt avec les dépendances
# Django itself
django>=4.0.0

# Pilote InterSystems IRIS pour Django, et pilote DB-API d'InterSystems
django-iris=>0.1.13
https://raw.githubusercontent.com/intersystems-community/iris-driver-distribution/main/DB-API/intersystems_irispython-3.2.0-py3-none-any.whl

# Cadre REST de Django et ses dépendances facultatives
djangorestframework>=3.4.4
# Support du format Markdown pour l'API navigable.
markdown>=3.0.0
# Support de filtrage
django-filter>=1.0.1

Et installez-les

python install -r requirements.txt

Première version de l'API

Pour cela, mettez à jour le fichier urls.py. Ici on dit, cela de la racine pour notre api comme api/, donc toutes les demandes à http://localhost:8000/api/ seront utilisées comme racine pour nos demandes API

from django.contrib import admin
from django.urls import path, include
from rest_framework import routers

router = routers.DefaultRouter()

urlpatterns = [
    path('api/', include(router.urls)),
    path('admin/', admin.site.urls),
    path('api-auth/', include('rest_framework.urls')),
]

Le cadre Django REST a une interface utilisateur intégrée pour les API, lorsque le serveur fonctionne en mode de développement avec DEBUG=True dans settings.py. Et nous pouvons ouvrir cette URL

Tout fonctionne, même sans rien définir, juste en connectant ce cadre à l'url. Il supporte l'autorisation, pour les requêtes nécessitant une autorisation. 

$ curl http://127.0.0.1:8000/api/
{}

Définissez une API pour le projet, nous utiliserons au minimum quelques fonctionnalités du cadre REST.

  • Les sérialiseurs permettent de convertir des données complexes, telles que des querysets et des instances de modèles, en types de données Python natifs qui peuvent ensuite être facilement convertis en JSON, XML ou d'autres types de contenu. Les sérialiseurs fournissent également une désérialisation, permettant aux données analysées d'être reconverties en types complexes, après avoir validé les données entrantes.
  • ViewSet - permet de combiner la logique d'un ensemble de visualisations connexes dans une seule classe

Ajoutons un point de terminaison pour nos messages de notification. Très simple, pourtant. Regardez le contenu mis à jour de _urls.py _

from django.contrib import admin
from django.urls import path, include
from rest_framework import routers, serializers, viewsets
from .models import CommunityPost

router = routers.DefaultRouter()

classCommunityPostSerializer(serializers.HyperlinkedModelSerializer):classMeta:# classe avec modèle
        model = CommunityPost
        # liste des champs à afficher, ou uniquement '__all__'
        fields = '__all__'# les ViewSets définissent le comportement de la visualisation.classCommunityPostViewSet(viewsets.ModelViewSet):
    queryset = CommunityPost.objects.all()
    serializer_class = CommunityPostSerializer

# connexion à l'API
router.register(r'posts', CommunityPostViewSet)

urlpatterns = [
    path('api/', include(router.urls)),
    path('admin/', admin.site.urls),
    path('api-auth/', include('rest_framework.urls')),
]

Et il apparaît maintenant dans l'interface web

Il suffit de cliquer sur le lien ici, et vous obtiendrez la réponse à cette question

Faites défiler jusqu'à la fin, et vous trouverez un formulaire généré pour les nouveaux éléments, qui peuvent être ajoutés par requête POST. Tous les champs sont adaptés au type de propriétés

Dans la liste des éléments, vous pouvez cliquer sur l'URL de n'importe quel élément, et voir ceci. Seulement cet élément comme réponse, et un formulaire d'édition avec une requête PUT

Authentication

Et vous pouvez déjà changer les données, par PUT ou POST. Le besoin d'autorisation n'est pas encore activé. Le cadre REST offre diverses combinaisons d'authentifications à utiliser. Ainsi, vous pouvez ouvrir certaines ressources pour un accès anonyme en lecture seule. Et s'authentifier pour pouvoir effectuer des modifications. Ou fermer complètement tout accès. Il suffit de configurer l'accès en lecture seule pour les anonymes, et de demander une authentification pour toute modification. Pour ce faire, il suffit d'ajouter ces lignes à settings.py

REST_FRAMEWORK = {
    # Utilisez les permissions standard de Django `django.contrib.auth`,# ou autorisez l'accès en lecture seule pour les utilisateurs non authentifiés.'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
    ],
}

Avec ceci, vous ne verrez plus le formulaire jusqu'à ce que vous vous connectiez, avec le nom d'utilisateur et le mot de passe d'instance créés précédemment pour l'administration de Django.

Pagination

Par défaut, il n'y a pas de pagination, mais nous pouvons facilement l'ajouter à toute requête de liste. Mettez à jour la variable REST_FRAMEWORK dans settings.py. Configurer la classe de pagination et la taille de page par défaut

REST_FRAMEWORK = {
...
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 10,
...
}

En conséquence, cela a légèrement modifié le JSON résultant, en ajoutant des éléments pertinents, tels que les liens vers les pages suivantes et précédentes, ainsi que la quantité de tous les éléments. Et avec l'interface Web, vous pouvez parcourir les pages.

Filtrage et recherche

Il est également assez simple d'ajouter des capacités de filtrage et de recherche. Mettez à jour la variable REST_FRAMEWORK dans settings.py

REST_FRAMEWORK = {
... 'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
    ],
...
]

Et mettez à jour CommunityPostViewSet avec une liste de champs pour le filtrage et la recherche

classCommunityPostViewSet(viewsets.ModelViewSet):
    queryset = CommunityPost.objects.all()
    serializer_class = CommunityPostSerializer
    filterset_fields = ['posttype', 'lang', 'published']
    search_fields = ['name',]

Et cela fonctionne directement depuis l'interface Web

Et finalement, nous disposons d'une API REST entièrement fonctionnelle, juste pour cette seule ressource, pour le moment. C'est assez simple pour le moment, mais il est possible de faire beaucoup de personnalisation, de connecter d'autres ressources et de les lier.

0
0 85
Article Guillaume Rongier · Août 17, 2022 3m read

Avec les dernières améliorations concernant le support de Python dans IRIS, et la poursuite du travail sur le support de Python DB-API par InterSystems. J'ai implémenté le support IRIS dans le projet Django où Python DB-API est utilisé pour travailler avec d'autres bases de données.

Essayons une application simple sur Django, qui stocke ses données dans IRIS.

todo App

Cette application est disponible sur [GitHub](https://github.com/caretdev/django-iris-todo), clonons-la
git clone https://github.com/caretdev/django-iris-todo
cd django-iris-todo

Et nous pouvons le construire et le démarrer avec Docker-compose

docker-compose up -d --build

Cela prendra un certain temps, quand IRIS sera démarré, et l'application Django l'attrapera, migrez les modèles là et lancez l'application aussi, une fois qu'elle est démarrée, elle sera disponible par http://localhost:8000/ et vous devriez avoir l'image comme ci-dessus.

Mais, comment cela fonctionne-t-il ?

Pour le faire fonctionner, il faut que l'interface InterSystems Python DB-API soit installée, elle est livrée avec la dernière version de l'aperçu, vous devez donc avoir la version 2022.1.0.114.0 ou une version supérieure et elle se trouve dans le dossier de l'instance &lt;INSTALL_DIR>/dev/python/. Ce fichier peut également être téléchargé à partir de ce depôt GitHub 

pip3 install intersystems_irispython-3.2.0-py3-none-any.whl

Le back-end IRIS de Django est implémenté par un autre projet, et qui peut être installé avec pip, et pour sûr nous avons besoin de django lui-même, installons-les

pip3 install django django-iris

Une autre façon d'installer les paquets requis dans Python est d'utiliser le fichier requirements.txt

pip3 install -r requirements.txt

Alors que ce fichier contient, les lignes suivantes

https://raw.githubusercontent.com/intersystems-community/iris-driver-distribution/main/intersystems_irispython-3.2.0-py3-none-any.whl
Django~=4.0.2
django-iris~=0.1.5

Connectons notre application Django à l'IRIS, ouvrons le fichier todoApp/settings.py

DATABASES définit les paramètres de connexion à la base de données

ENGINE doit être django_IRIS

NAME doit pointer vers l'espace de nom Namespace dans IRIS.

Il suffit de le changer en quelque chose, où il peut se connecter. Par exemple

DATABASES = {
    'default': {
        'ENGINE': 'django_iris',
        'NAME': 'DJANGOTODO',
        'HOST': 'localhost',
        'PORT': 1972,
        'USER': '_SYSTEM',
        'PASSWORD': 'SYS',
    }
}

Et lançons la migration

python3 manage.py migrate

Aucune erreur, tous les tableaux du côté d'IRIS sont créés.

Et nous sommes prêts à démarrer notre application

python3 manage.py runserver

Django est livré avec un panneau d'administration

python3 manage.py createsuperuser

Et le panneau d'administration est disponible par le lien http://localhost:8000/admin/

Et comment il est stocké dans IRIS

  Le travail est toujours en cours pour le pilote Python DB-API d'InterSystems, et aussi pour ce projet django-iris.

Soyez conscient que cela ne fonctionnera pas aussi bien sur la version Community Edition, en raison de la façon dont Django se connecte à la base de données, il peut utiliser toutes les licences très rapidement.

Si vous aimez le projet, et son impact futur sur l'utilisation d'IRIS, veuillez voter sur Compétition OpenExchange

0
0 78
Article Lorenzo Scalese · Août 10, 2022 11m read

Depuis IRIS 2021.2, il est possible d'écrire des Méthodes de classe en utilisant le langage Python. J'ai utilisé cette nouvelle fonctionnalité pour détecter des personnes et des objets dans des images, en utilisant ImageAI. Le créateur d'ImageAI la définit comme suit : "Une bibliothèque python open-source construite pour permettre aux développeurs de créer des applications et des systèmes avec des capacités autonomes de Deep Learning et de vision par ordinateur en utilisant des lignes de code simples et peu nombreuses." Dans cet article, vous apprendrez comment appliquer la Vision par ordinateur de l'IA pour détecter des objets et des personnes à l'intérieur d'images.

Étapes de l'analyse des images à l'aide d'ImageAI

  1. Cliquez sur https://openexchange.intersystems.com/package/AI-Image-Object-Detector et sur le bouton Télécharger (Download) pour accéder au projet Git.
  2. Clone/git tire le référentiel dans n'importe quel répertoire local
$ git clone https://github.com/yurimarx/image-analyzer.git
  1. Ouvrez un terminal Docker dans ce répertoire et exécutez :
$ docker-compose build
  1. Lancez le conteneur IRIS :
$ docker-compose up -d
  1. Connectez-vous à votre Postman (ou autre client REST similaire) et configurez la requête conformément à cette image :

Request Image Analysis input

  • Méthode : POST
  • URL: http://localhost:52773/image-analyzer/analyzeImage
  • Corps : données du formulaire "form-data"
  • Clé : fichier (le nom du champ fichier doit être fichier) et type Fichier
  • Valeur : sélectionnez une image avec des personnes et/ou des objets sur votre ordinateur
  1. Cliquez sur "envoyer" (send) et obtenez une réponse avec les objets détectés comme suit :

Request Image Analysis output

Les coulisses - le code source

Le fichier Dockerfile

Il s'agit d'une étape d'importation. Pour utiliser les bibliothèques python, vous devez les installer au préalable, mais vous devez faire attention à les installer dans le dossier IRIS Python correct (/usr/irissys/mgr/python) :

 
Dockerfile
FROMintersystemsdc/iris-community
 
      <div>
        <span style="color: #c586c0;">USER</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">root</span>
      </div>  

      <div>
        <span style="color: #c586c0;">ENV</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">DEBIAN_FRONTEND</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">noninteractive</span>
      </div>  

      <div>
        <span style="color: #6a9955;"># installation des bibliothèques requises ImageAI/OpenCV pour traiter les images et les vidéos</span>
      </div>

      <div>
        <span style="color: #c586c0;">RUN</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">apt-get</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">-y</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">update</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">\</span>
      </div>

      <div>
        <span style="color: #d4d4d4;">    </span><span style="color: #9cdcfe;">&&</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">apt-get</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">-y</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">install</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">apt-utils</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">\</span>
      </div>

      <div>
        <span style="color: #d4d4d4;">    </span><span style="color: #9cdcfe;">&&</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">apt-get</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">install</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">-y</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">build-essential</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">unzip</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">pkg-config</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">\</span>
      </div>

      <div>
        <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">zlib1g-dev</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">libncurses5-dev</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">libgdbm-dev</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">libnss3-dev</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">\</span>
      </div>

      <div>
        <span style="color: #d4d4d4;">        </span><span style="color: #9cdcfe;">libssl-dev</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">libreadline-dev</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">libffi-dev</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">wget</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">\</span>
      </div>

      <div>
        <span style="color: #d4d4d4;">    </span><span style="color: #9cdcfe;">&&</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">apt-get</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">install</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">-y</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">ffmpeg</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">libsm6</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">libxext6</span><span style="color: #d4d4d4;">  </span>
      </div>  

      <div>
        <span style="color: #6a9955;"># utiliser pip3 (le zpm de python) pour installer imageai et les dépendances d'imageai</span>
      </div>

      <div>
        <span style="color: #c586c0;">RUN</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">pip3</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">install</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">--upgrade</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">pip</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">setuptools</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">wheel</span>
      </div>

      <div>
        <span style="color: #c586c0;">RUN</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">pip3</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">install</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">--target</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">/usr/irissys/mgr/python</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">tensorflow==2.4.0</span>
      </div>

      <div>
        <span style="color: #c586c0;">RUN</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">pip3</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">install</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">--target</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">/usr/irissys/mgr/python</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">keras==2.4.3</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">numpy==1.19.3</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">pillow==8.1.1</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">scipy==1.4.1</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">h5py==2.10.0</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">matplotlib==3.3.2</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">opencv-python</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">keras-resnet==0.2.0</span>
      </div>

      <div>
        <span style="color: #c586c0;">RUN</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">pip3</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">install</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">--target</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">/usr/irissys/mgr/python</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">imageai</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">--upgrade</span>
      </div>  

      <div>
        <span style="color: #c586c0;">USER</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">root</span><span style="color: #d4d4d4;">   </span>
      </div>

      <div>
        <span style="color: #c586c0;">WORKDIR</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">/opt/irisbuild</span>
      </div>

      <div>
        <span style="color: #c586c0;">RUN</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">chown</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">${ISC_PACKAGE_MGRUSER}</span><span style="color: #9cdcfe;">:</span><span style="color: #9cdcfe;">${ISC_PACKAGE_IRISGROUP}</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">/opt/irisbuild</span>
      </div>

      <div>
        <span style="color: #c586c0;">USER</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">${ISC_PACKAGE_MGRUSER}</span>
      </div>  

      <div>
        <span style="color: #c586c0;">WORKDIR</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">/opt/irisbuild</span>
      </div>

      <div>
        <span style="color: #c586c0;">COPY</span><span style="color: #d4d4d4;">  </span><span style="color: #9cdcfe;">python</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">python</span>
      </div>

      <div>
        <span style="color: #c586c0;">COPY</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">input</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">input</span>
      </div>

      <div>
        <span style="color: #c586c0;">COPY</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">output</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">output</span>
      </div>

      <div>
        <span style="color: #c586c0;">COPY</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">models</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">models</span>
      </div>

      <div>
        <span style="color: #c586c0;">COPY</span><span style="color: #d4d4d4;">  </span><span style="color: #9cdcfe;">src</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">src</span>
      </div>

      <div>
        <span style="color: #c586c0;">COPY</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">module.xml</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">module.xml</span>
      </div>

      <div>
        <span style="color: #c586c0;">COPY</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">iris.script</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">iris.script</span>
      </div>  

      <div>
        <span style="color: #6a9955;"># download the trained model used to detect objects and persons inside images</span>
      </div>

      <div>
        <span style="color: #c586c0;">ADD</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">https://github.com/OlafenwaMoses/ImageAI/releases/download/essentials-v5/resnet50_coco_best_v2.1.0.h5</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">models</span>
      </div>  

      <div>
        <span style="color: #c586c0;">USER</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">root</span>
      </div>

      <div>
        <span style="color: #c586c0;">RUN</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">chmod</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">-R</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">777</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">input</span>
      </div>

      <div>
        <span style="color: #c586c0;">RUN</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">chmod</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">-R</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">777</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">output</span>
      </div>

      <div>
        <span style="color: #c586c0;">RUN</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">chmod</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">-R</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">777</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">models</span>
      </div>  

      <div>
        <span style="color: #c586c0;">USER</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">${ISC_PACKAGE_MGRUSER}</span>
      </div>  

      <div>
        <span style="color: #c586c0;">RUN</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">iris</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">start</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">IRIS</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">\</span>
      </div>

      <div>
        <span style="color: #d4d4d4;">    </span><span style="color: #9cdcfe;">&&</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">iris</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">session</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">IRIS</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">&lt;</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">iris.script</span><span style="color: #d4d4d4;"> </span><span style="color: #569cd6;">\</span>
      </div>

      <div>
        <span style="color: #d4d4d4;">    </span><span style="color: #9cdcfe;">&&</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">iris</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">stop</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">IRIS</span><span style="color: #d4d4d4;"> </span><span style="color: #9cdcfe;">quietly</span>
      </div>
    </div>
  </div>
</div>
  • pip3 est l'outil python utilisé pour installer les bibliothèques python, comme imageai, opencv, tensorflow et autres.
  • le paramètre target a été utilisé pour installer les bibliothèques python là où IRIS en a besoin, le dossier /usr/irissys/mgr/python.
  • ImageAI utilise les bibliothèques python suivantes :
    • TensorFlow : " C'est une bibliothèque open source pour le calcul numérique et l'apprentissage automatique à grande échelle. TensorFlow regroupe un grand nombre de modèles et d'algorithmes d'apprentissage automatique et d'apprentissage profond (alias réseaux neuronaux) et les rend utiles grâce à une métaphore commune. Il utilise Python pour fournir une API frontale pratique permettant de créer des applications à l'aide du cadre, tout en exécutant ces applications dans un C++ haute performance" (source : https://www.infoworld.com/article/3278008/what-is-tensorflow-the-machine-learning-library-explained.html)
    • Keras: "Keras est une API d'apprentissage profond écrite en Python, fonctionnant au-dessus de la plateforme d'apprentissage automatique TensorFlow. Elle a été développée dans le but de permettre une expérimentation rapide. Il est essentiel de pouvoir passer de l'idée au résultat aussi rapidement que possible pour effectuer de bonnes recherches" (source: https://keras.io/about/)
    • OpenCV : "( Bibliothèque de vision par ordinateur à source ouverte, Open Source Computer Vision Library) est une bibliothèque logicielle de vision par ordinateur et d'apprentissage automatique à source ouverte. OpenCV a été construite pour fournir une infrastructure commune pour les applications de vision par ordinateur et pour accélérer l'utilisation de la perception par machine dans les produits commerciaux." (source: https://opencv.org/about/).
  • ImageAI est un cadre qui combine TensorFlow, Keras et OpenCV pour faciliter la formation et/ou l'utilisation des modèles d'apprentissage profond du marché :
    • Prédiction de l'image;
    • Détection d'objets;
    • Détection et suivi d'objets vidéo;
    • Video analysis;
    • Apprentissage de modèles personnalisés (imaginez, par exemple, l'apprentissage d'un modèle pour détecter le risque de chute d'un patient hors de son lit dans le service de soins intensifs d'un hôpital).
  • ImageAI travaille avec les 3 principaux modèles de vision par ordinateur utilisés sur le marché pour la détection d'objets :
    • RetinaNet(Dimension = 145 Mo, haute performance et précision, avec un temps de détection plus long)
    • YOLOv3(Dimension = 237 Mo, performance et précision modérées, avec un temps de détection modéré)
    • TinyYOLOv3(Dimension = 34 Mo, optimisé pour la vitesse et les performances modérées, avec un temps de détection rapide)
  • Dans le dossier d'entrée se trouvent les images à analyser ;
  • Dans le dossier de sortie se trouvent les résultats (images avec balises de résultats visuels) ;
  • Dans le dossier des modèles se trouvent les modèles formés qui seront utilisés avec l'imageai.
  • Cet échantillon utilise le modèle RetinaNet (restnet50)

Mise en œuvre de la version Embedded Python

L'implémentation du code source est simple, et en quelques lignes nous réalisons la détection d'objets ( grâce à ImageAI). Si tensorflow était utilisé directement avec OpenCV et Keras, beaucoup plus de lignes de code auraient été écrites. Consultez le code commenté :

 
Mise en œuvre d'ImageAI
///Détection d'objets et de personnes dans les images et les vidéos
  <div>
    <span style="color: #85a6ff;">Class</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">dc</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">imageanalyzer</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">Detector</span>
  </div>

  <div>
    <span style="color: #ffffff;">{</span>
  </div>  

  <div>
    <span style="color: #80bd66;">///</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">Détection des personnes et des objets dans les paramètres de l'image</span>
  </div>

  <div>
    <span style="color: #85a6ff;">ClassMethod</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">GetImageFeatures</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">Image</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">[</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">Language</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">python</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">]</span>
  </div>

  <div>
    <span style="color: #ffffff;">{</span>
  </div>

  <div>
    <span style="color: #80bd66;">    # </span><span style="color: #80bd66;">import</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">imageai</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">and</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">json</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">libs</span>
  </div>

  <div>
    <span style="color: #85a6ff;">    </span><span style="color: #85a6ff;">from</span><span style="color: #ade2ff;"> </span><span style="color: #ade2ff;">imageai</span><span style="color: #ffffff;">.</span><span style="color: #ade2ff;">Detection</span><span style="color: #85a6ff;"> </span><span style="color: #85a6ff;">import</span><span style="color: #ade2ff;"> </span><span style="color: #ade2ff;">ObjectDetection</span>
  </div>

  <div>
    <span style="color: #85a6ff;">    </span><span style="color: #85a6ff;">import</span><span style="color: #ade2ff;"> </span><span style="color: #ade2ff;">json</span>
  </div>

  <div>
    <span style="color: #80bd66;">    </span>
  </div>

  <div>
    <span style="color: #80bd66;">    # </span><span style="color: #80bd66;">instantiate</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">imageai</span>
  </div>

  <div>
    <span style="color: #ade2ff;">    </span><span style="color: #ade2ff;">detector</span><span style="color: #ffffff;"> =</span><span style="color: #ade2ff;"> </span><span style="color: #ade2ff;">ObjectDetection</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
  </div>  

  <div>
    <span style="color: #80bd66;">    # </span><span style="color: #80bd66;">set</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">retinanet</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">as</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">model</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">to</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">detect</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">objects</span>
  </div>

  <div>
    <span style="color: #ade2ff;">    </span><span style="color: #ade2ff;">model</span><span style="color: #ade2ff;">_</span><span style="color: #ade2ff;">path</span><span style="color: #ffffff;"> =</span><span style="color: #d4b57c;"> </span><span style="color: #d4b57c;">"/opt/irisbuild/models/resnet50_coco_best_v2.1.0.h5"</span>
  </div>

  <div>
    <span style="color: #80bd66;">    # </span><span style="color: #80bd66;">set</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">folder</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">to</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">receive</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">the</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">image</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">to</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">be</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">processed</span>
  </div>

  <div>
    <span style="color: #ade2ff;">    </span><span style="color: #ade2ff;">input</span><span style="color: #ade2ff;">_</span><span style="color: #ade2ff;">path</span><span style="color: #ffffff;"> =</span><span style="color: #d4b57c;"> </span><span style="color: #d4b57c;">"/opt/irisbuild/input/"</span><span style="color: #ffffff;"> +</span><span style="color: #ade2ff;"> </span><span style="color: #ade2ff;">Image</span>
  </div>

  <div>
    <span style="color: #80bd66;">    # </span><span style="color: #80bd66;">set</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">folder</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">to</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">stores</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">the</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">results</span><span style="color: #80bd66;"> (</span><span style="color: #80bd66;">visual</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">tags</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">inside</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">image</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">processed</span><span style="color: #80bd66;">)</span>
  </div>

  <div>
    <span style="color: #ade2ff;">    </span><span style="color: #ade2ff;">output</span><span style="color: #ade2ff;">_</span><span style="color: #ade2ff;">path</span><span style="color: #ffffff;"> =</span><span style="color: #d4b57c;"> </span><span style="color: #d4b57c;">"/opt/irisbuild/output/"</span><span style="color: #ffffff;"> +</span><span style="color: #ade2ff;"> </span><span style="color: #ade2ff;">Image</span>
  </div>  

  <div>
    <span style="color: #80bd66;">    # </span><span style="color: #80bd66;">instantiate</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">retina</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">model</span>
  </div>

  <div>
    <span style="color: #ade2ff;">    </span><span style="color: #ade2ff;">detector</span><span style="color: #ffffff;">.</span><span style="color: #ade2ff;">setModelTypeAsRetinaNet</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
  </div>

  <div>
    <span style="color: #80bd66;">    </span>
  </div>

  <div>
    <span style="color: #80bd66;">    # </span><span style="color: #80bd66;">set</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">trained</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">detection</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">model</span><span style="color: #80bd66;"> - </span><span style="color: #80bd66;">retina</span>
  </div>

  <div>
    <span style="color: #ade2ff;">    </span><span style="color: #ade2ff;">detector</span><span style="color: #ffffff;">.</span><span style="color: #ade2ff;">setModelPath</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">model</span><span style="color: #ade2ff;">_</span><span style="color: #ade2ff;">path</span><span style="color: #ffffff;">)</span>
  </div>

  <div>
    <span style="color: #80bd66;">    # </span><span style="color: #80bd66;">load</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">the</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">model</span>
  </div>

  <div>
    <span style="color: #ade2ff;">    </span><span style="color: #ade2ff;">detector</span><span style="color: #ffffff;">.</span><span style="color: #ade2ff;">loadModel</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
  </div>

  <div>
    <span style="color: #80bd66;">    # </span><span style="color: #80bd66;">do</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">the</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">object</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">image</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">detection</span>
  </div>

  <div>
    <span style="color: #ade2ff;">    </span><span style="color: #ade2ff;">detection</span><span style="color: #ffffff;"> =</span><span style="color: #ade2ff;"> </span><span style="color: #ade2ff;">detector</span><span style="color: #ffffff;">.</span><span style="color: #ade2ff;">detectObjectsFromImage</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">input</span><span style="color: #ade2ff;">_</span><span style="color: #ade2ff;">image</span><span style="color: #ffffff;">=</span><span style="color: #ade2ff;">input</span><span style="color: #ade2ff;">_</span><span style="color: #ade2ff;">path</span><span style="color: #ffffff;">,</span><span style="color: #ade2ff;"> </span><span style="color: #ade2ff;">output</span><span style="color: #ade2ff;">_</span><span style="color: #ade2ff;">image</span><span style="color: #ade2ff;">_</span><span style="color: #ade2ff;">path</span><span style="color: #ffffff;">=</span><span style="color: #ade2ff;">output</span><span style="color: #ade2ff;">_</span><span style="color: #ade2ff;">path</span><span style="color: #ffffff;">)</span>
  </div>

  <div>
    <span style="color: #80bd66;">    # </span><span style="color: #80bd66;">return</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">json</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">with</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">the</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">results</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">of</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">the</span><span style="color: #80bd66;"> </span><span style="color: #80bd66;">processing</span><span style="color: #80bd66;"> </span>
  </div>

  <div>
    <span style="color: #80bd66;">    </span><span style="color: #80bd66;"># if</span><span style="color: #80bd66;"> vous voulez, vous pouvez voir l'image du résultat dans le dossier de sortie</span>
  </div>

  <div>
    <span style="color: #85a6ff;">    </span><span style="color: #85a6ff;">return</span><span style="color: #ade2ff;"> </span><span style="color: #ade2ff;">json</span><span style="color: #ffffff;">.</span><span style="color: #ade2ff;">dumps</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">detection</span><span style="color: #ffffff;">)</span>
  </div>

  <div>
    <span style="color: #ffffff;">}</span>
  </div>  

  <div>
    <span style="color: #80bd66;">///</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">Get JSON</span>
  </div>

  <div>
    <span style="color: #85a6ff;">ClassMethod</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">GetJSON</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">pythonObj</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">[</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">Language</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">python</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">]</span>
  </div>

  <div>
    <span style="color: #ffffff;">{</span>
  </div>

  <div>
    <span style="color: #85a6ff;">    </span><span style="color: #85a6ff;">import</span><span style="color: #ade2ff;"> </span><span style="color: #ade2ff;">json</span>
  </div>

  <div>
    <span style="color: #85a6ff;">    </span><span style="color: #85a6ff;">return</span><span style="color: #ade2ff;"> </span><span style="color: #ade2ff;">json</span><span style="color: #ffffff;">.</span><span style="color: #ade2ff;">dumps</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">pythonObj</span><span style="color: #ffffff;">)</span>
  </div>

  <div>
    <span style="color: #ffffff;">}</span>
  </div>  

  <div>
    <span style="color: #80bd66;">///</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">Description</span>
  </div>

  <div>
    <span style="color: #85a6ff;">ClassMethod</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">GetImageAI</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">Image</span><span style="color: #ffffff;">)</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">As</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">%Status</span>
  </div>

  <div>
    <span style="color: #ffffff;">{</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">sc</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">$$$</span><span style="color: #dcdcaa;">OK</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Write</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">..</span><span style="color: #dcdcaa;">GetImageFeatures</span><span style="color: #ffffff;">(</span><span style="color: #ff75f4;">Image</span><span style="color: #ffffff;">)</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Return</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">sc</span>
  </div>

  <div>
    <span style="color: #ffffff;">}</span>
  </div>  

  <div>
    <span style="color: #ffffff;">}</span>
  </div>
</div>

L'API IRIS pour exposer la détection d'objets en tant que Microservice IRIS de détection d'objets

Il est très facile d'appeler la méthode de classe python, c'est similaire d'appeler n'importe quelle méthode de classe objectcript, voir :

 
Microservice IRIS pour la détection d'objets dans les images
Classdc.imageanalyzer.ImageAnalyzerRESTAppExtends%CSP.REST
  <div>
    <span style="color: #ffffff;">{</span>
  </div>  

  <div>
    <span style="color: #85a6ff;">Parameter</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">CHARSET</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"utf-8"</span><span style="color: #ffffff;">;</span>
  </div>  

  <div>
    <span style="color: #85a6ff;">Parameter</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">CONVERTINPUTSTREAM</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">1</span><span style="color: #ffffff;">;</span>
  </div>  

  <div>
    <span style="color: #85a6ff;">Parameter</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">CONTENTTYPE</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"application/json"</span><span style="color: #ffffff;">;</span>
  </div>  

  <div>
    <span style="color: #85a6ff;">Parameter</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">Version</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"1.0.0"</span><span style="color: #ffffff;">;</span>
  </div>  

  <div>
    <span style="color: #85a6ff;">Parameter</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">HandleCorsRequest</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">1</span><span style="color: #ffffff;">;</span>
  </div>  

  <div>
    <span style="color: #85a6ff;">XData</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">UrlMap</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">[</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">XMLNamespace</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #d4b57c;">"http://www.intersystems.com/urlmap"</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">]</span>
  </div>

  <div>
    <span style="color: #ffffff;">{</span>
  </div>

  <div>
    <span style="color: #ffffff;">&lt;</span><span style="color: #85a6ff;">Routes</span><span style="color: #ffffff;">></span>
  </div>  

  <div>
    <span style="color: #80bd66;"><!--</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">Detect</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">objects</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">inside</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">an</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">image</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">--></span>
  </div>

  <div>
    <span style="color: #ffffff;">&lt;</span><span style="color: #85a6ff;">Route</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Url</span><span style="color: #ffffff;">=</span><span style="color: #d4b57c;">"/analyzeImage"</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Method</span><span style="color: #ffffff;">=</span><span style="color: #d4b57c;">"POST"</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">Call</span><span style="color: #ffffff;">=</span><span style="color: #d4b57c;">"AnalyzeImage"</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">/></span>
  </div>  

  <div>
    <span style="color: #ffffff;">&lt;/</span><span style="color: #85a6ff;">Routes</span><span style="color: #ffffff;">></span>
  </div>

  <div>
    <span style="color: #ffffff;">}</span>
  </div>  

  <div>
    <span style="color: #80bd66;">//</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">Detect objects inside an image</span>
  </div>  

  <div>
    <span style="color: #85a6ff;">ClassMethod</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">AnalyzeImage</span><span style="color: #ffffff;">()</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">As</span><span style="color: #d4d4d4;"> </span><span style="color: #4ec9b0;">%Status</span>
  </div>

  <div>
    <span style="color: #ffffff;">{</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">tSC</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #dcdcaa;">$$$</span><span style="color: #dcdcaa;">OK</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">    </span>
  </div>

  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">try</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>

  <div>
    <span style="color: #6a9955;">        </span><span style="color: #80bd66;">// Obtient le fichier à partir de la requête multipart</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">source</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">%request</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">GetMimeData</span><span style="color: #ffffff;">(</span><span style="color: #d4b57c;">"file"</span><span style="color: #ffffff;">)</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span>
  </div>

  <div>
    <span style="color: #6a9955;">        </span><span style="color: #80bd66;">// enregistrement du fichier dans le dossier d'entrée, pour être traité avec imageai</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">destination</span><span style="color: #ffffff;">=</span><span style="color: #85a6ff;">##class</span><span style="color: #ffffff;">(</span><span style="color: #4ec9b0;">%Stream</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">FileBinary</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%New</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">destination</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Filename</span><span style="color: #ffffff;">=</span><span style="color: #d4b57c;">"/opt/irisbuild/input/"</span><span style="color: #ffffff;">_</span><span style="color: #ade2ff;">source</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">FileName</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">tSC</span><span style="color: #ffffff;">=</span><span style="color: #ade2ff;">destination</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">CopyFrom</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">source</span><span style="color: #ffffff;">)</span><span style="color: #6a9955;"> </span><span style="color: #80bd66;">//reader open the file</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">result</span><span style="color: #ffffff;">=</span><span style="color: #ade2ff;">destination</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">%Save</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">%response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">ContentType</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">..</span><span style="color: #dcdcaa;">#</span><span style="color: #dcdcaa;">CONTENTTYPEJSON</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #64c9ff;">%response</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">Headers</span><span style="color: #ffffff;">(</span><span style="color: #d4b57c;">"Access-Control-Allow-Origin"</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">=</span><span style="color: #d4b57c;">"*"</span>
  </div>  

  <div>
    <span style="color: #6a9955;">        </span><span style="color: #80bd66;">//Appelle de la méthode de classe python intégrée pour détecter les objets et écrire les résultats en tant que contenu json</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Write</span><span style="color: #d4d4d4;"> </span><span style="color: #85a6ff;">##class</span><span style="color: #ffffff;">(</span><span style="color: #4ec9b0;">dc</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">imageanalyzer</span><span style="color: #4ec9b0;">.</span><span style="color: #4ec9b0;">Detector</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">GetImageAI</span><span style="color: #ffffff;">(</span><span style="color: #ade2ff;">source</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">FileName</span><span style="color: #ffffff;">)</span>
  </div>  

  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">tSC</span><span style="color: #ffffff;">=</span><span style="color: #dcdcaa;">$$$</span><span style="color: #dcdcaa;">OK</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">    </span>
  </div>

  <div>
    <span style="color: #6a9955;">    </span><span style="color: #80bd66;">//Renvoie d'un message d'erreur à l'utilisateur</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">catch</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">e</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">{</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">tSC</span><span style="color: #ffffff;">=</span><span style="color: #ade2ff;">e</span><span style="color: #ffffff;">.</span><span style="color: #dcdcaa;">AsStatus</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">        </span><span style="color: #ffffff;">Set</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">pOutput</span><span style="color: #d4d4d4;"> </span><span style="color: #ffffff;">=</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">tSC</span>
  </div>

  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">}</span>
  </div>  

  <div>
    <span style="color: #d4d4d4;">    </span><span style="color: #ffffff;">Quit</span><span style="color: #d4d4d4;"> </span><span style="color: #ade2ff;">tSC</span>
  </div>

  <div>
    <span style="color: #ffffff;">}</span>
  </div>

  <br /><br /><br /> 

  <div>
    <span style="color: #ffffff;">}</span>
  </div>
</div>

Vous pouvez faire beaucoup de choses avec Embedded Python et ImageAI. Pour en savoir plus sur ImageAI, consultez le site : https://github.com/OlafenwaMoses/ImageAI.

Cette application utilisant imageai participera au concours Python, si vous l'aimez, votez-la. Merci !

0
0 198
Article Irène Mykhailova · Août 3, 2022 5m read

Motivation

Ce projet a vu le jour lorsque j'ai réfléchi à la manière de permettre au code Python de traiter naturellement le mécanisme de stockage évolutif et de récupération efficace fourni par les globales IRIS, par le biais de la technologie Embedded Python.

Mon idée initiale était de créer une sorte d'implémentation de dictionnaire Python en utilisant les globales, mais j'ai vite réalisé que je devais d'abord m'occuper de l'abstraction des objets.

J'ai donc commencé à créer des classes Python capables d'envelopper des objets Python, de stocker et de récupérer leurs données dans des globales, c'est-à-dire de sérialiser et de désérialiser des objets Python dans des globales IRIS.

Comment cela fonctionne-t-il ?

Comme ObjectScript%DispatchGetProperty(), %DispatchSetProperty() et %DispatchMethod(), Python dispose de méthodes permettant de déléguer les appels aux propriétés et aux méthodes des objets.

Lorsque vous définissez ou récupérez une propriété d'objet, l'interpréteur Python vous permet d'intercepter cette opération par les méthodes __setattr__(self, name, value) et __getattr(self, name)__.

Regardez cet exemple de base :

>>> class Test:
...         def __init(self, prop1):
...                 self.prop1 = prop1
...         def __setattr__(self, name, value):
...                 print(f"setting property {name} to value {value}")
...         def __getattr__(self, name):
...                 print(f"getting property {name}")
...
>>> obj = Test()
>>> obj.prop1 = "test"
setting property prop1 to value test
>>> obj.prop1
getting property prop1
>>> obj.prop2
getting property prop2
>>> obj.prop2
getting property prop2
>>>

Notez que les méthodes __setattr__() et __getattr()__ ont été appelées indirectement par les opérations set et get sur les objets de la classe Test - qui implémente ces méthodes. Même une propriété non déclarée, comme prop2 dans cet exemple, émet des appels vers elles.

Ce mécanisme est le cœur du test de sérialisation que j'ai essayé dans mon projet python-globales-convertisseur-exemple. Avec ce mécanisme, vous pouvez intercepter les opérations set/get et stocker/récupérer les données des globales IRIS.

Modèle d'objet

Les globales offrent une structure hautement personnalisable. En utilisant leur modèle hiérarchique pour l'accès aux informations, qui est assez similaire aux objets JSON et aux dictionnaires Python, nous pouvons stocker les données des propriétés des objets et les métadonnées.

Voici comment j'ai utilisé une globale pour créer un modèle d'objet simple pour sérialiser des objets Python :

Pour chaque objet sérialisé, son nom de classe est sérialisé dans un nœud étiqueté "class" :

^test(1,"class")="<class 'employee.SalaryEmployee'>"

Le premier indice est un nombre incrémental qui est utilisé comme référence à cet objet dans le modèle. Ainsi, dans l'exemple ci-dessus, un objet de la classe employee.SalaryEmployee est stocké avec la valeur de référence 1.

Pour les propriétés de types de données primitives, leur type et leur valeur sont stockés. Par exemple :

^test(1,"name","type")="<class 'str'>"
^test(1,"name","value")="me"

Cette structure est interprétée comme l'objet référencé par l'index 1, a une propriété appelée name, avec une valeur égale à 'me'.

Pour les propriétés référençant des objets, le modèle est légèrement différent, car contrairement aux objets JSON ou aux dictionnaires Python, les globales sont destinés à stocker uniquement des données de type primitif. Donc un autre noeud "classe" est créé pour cet objet référencé, et son index de noeud (c'est-à-dire sa référence) est stocké dans le noeud de propriété :

^test(1,"company","oref")=2
^test(1,"company","type")="<class 'iris_global_object.IrisGlobalObject'>"
^test(2,"class")="<class 'employee.Company'>"
^test(2,"name","type")="<class 'str'>"
^test(2,"name","value")="Company ABC"

Ces structures signifient que l'objet 1 a une propriété appelée company, dont les valeurs sont stockées dans l'index 2 - notez la valeur de ^test(1, "company", "oref").

Processus de sérialisation/désérialisation

Lorsque vous créez un wrapper pour sérialiser ou désérialiser des objets Python, vous devez définir le nom de la globale qui stocke l'objet.

Ensuite, le processus de sérialisation est effectué lorsqu'une opération set est exécutée. La méthode __setattr__() définit la valeur et le type de la propriété dans la globale définie pour stocker l'objet, en utilisant le modèle d'objet simple expliqué précédemment.

Dans le sens inverse, une désérialisation est effectuée par la méthode __getattr__, lorsqu'une opération get est effectuée.

Pour les types de données primitifs, ce processus est simple : il suffit de récupérer la valeur stockée dans la globale et de la retourner.

Mais pour les objets, le processus doit instancier le type de données de leur classe et définir également toutes leurs propriétés. De cette façon, un objet Python restauré peut être utilisé, y compris les appels à ses méthodes.

Les travaux futurs

Comme nous l'avons dit au début de cette entrée, ce projet est né comme une simplification d'une façon de laisser le code Python utiliser les globales comme un moteur de stockage naturel, et vise à être juste une preuve de concept.

La sérialisation/désérialisation d'objets n'est que le début de cet objectif. Il y a donc beaucoup d'efforts à faire pour que cette idée arrive à maturité.

J'espère que cette entrée vous permettra de comprendre le but de mon travail dans ce projet, et qu'elle pourra vous inspirer à réfléchir à de nouvelles façons d'utiliser les globales IRIS pour rapprocher Python d'IRIS.

0
0 65
Article Guillaume Rongier · Juil 27, 2022 19m read

Introduction

Cet article a pour but de donner une introduction à ce qu'est gRPC ainsi que de donner un exemple de comment jouer avec le Hello World officiel en utilisant IRIS Embedded Python.

Vous pouvez trouver tout le code exposé ici, dans le référentiel suivant project repo.

gRPC

Le gRPC ( appel de procédure à distance gRPC) est un style architectural d'API basé sur le protocole RPC. Le projet a été créé par Google en 2015 et est sous licence Apache 2.0. Actuellement, le projet est soutenu par la fondation Cloud Native Computing Foundation (CNCF).

Les cas d'utilisation réussis sont liés à la connexion de services entre backends, tels que les services dans une architecture de type microservices.

Protocol buffer

La plupart des protocoles basés sur le RPC utilisent un IDL (langage de description d'interface) pour définir un contrat de communication entre un serveur et un client.

Le gRPC utilise un format de mécanisme de sérialisation appelé "Protocol Buffer".

L'objectif d'un tel format est similaire à celui d'un WSDL, dans lequel vous pouvez définir des méthodes et des structures de données. Toutefois, contrairement au WSDL, qui est défini à l'aide de XML, le Protocol Buffer utilise un langage (le langage du Protocol Buffer) semblable à un mélange des langages les plus courants.

Par exemple, pour définir un message d'informations d'interopérabilité, vous pouvez utiliser la définition de tampon de protocole suivante :

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;
}

Vous pouvez également définir des contrats de méthodes de service pour les messages. Par exemple :

//La définition du service d'accueil.
service Greeter {
  // Envoi d'un message d'accueil
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// Le message de requête contenant le nom de l'utilisateur.
message HelloRequest {
  string name = 1;
}

// Le message de réponse contenant les messages d'accueil
message HelloReply {
  string message = 1;
}

L'utilisation de Protocol Buffer dans gRPC lui permet de suivre un principe de conception fonctionnel plutôt que basé sur les ressources, utilisé par REST.

Outils gRPC

Ce que vous définissez dans le langage des Protocoles Buffer est inutilisable directement. Vous devez transpiler le langage des tampons de protocole vers un autre langage, qui est supporté par gRPC.

Ce transpilage est effectué par un paquetage appelé outils gRPC. Actuellement, la plateforme gRPC supporte des langages tels que Java, C++, Dart, Python, Objective-C, C#, Ruby, JavaScript et Go.

Dans cet article, nous allons utiliser le support Python afin d'utiliser gRPC avec la fonctionnalité Embedded Python dans IRIS.

Par exemple, avec cette commande des outils gRPC, vous pouvez transpiler la définition des tampons de protocole pour le service Greeter, les messages HelloRequest et HelloReply vers Python :

python3 -m grpc_tools.protoc -I ../../protos --python_out=. --grpc_python_out=. ../../protos/helloworld.proto

Cette commande produit ces fichiers Python :

-rwxrwxrwx  1 irisowner irisowner 2161 Mar 13 20:01 helloworld_pb2.py*
-rwxrwxrwx  1 irisowner irisowner 3802 Mar 13 20:01 helloworld_pb2_grpc.py*

Ces fichiers sont le code source Python généré à partir du fichier proto pour les messages et les méthodes de service, respectivement. Le serveur met en œuvre les méthodes de service, et le client (également appelé stub) les appelle.

Ainsi, vous pouvez utiliser Embedded Python pour envoyer/recevoir des messages Hello via le service Greeter.

Un autre outil utile pour gRPC est un équivalent de curl, l'utilitaire grpcurl. Après avoir introduit notre exemple "hello world", un aperçu de la façon d'utiliser un tel outil sera présenté.

Types de méthodes de service

Les clients peuvent varier en fonction de la manière dont ils envoient et reçoivent les messages des méthodes de service. Les messages peuvent être envoyés et reçus un par appel ou dans un flux. Pour chaque combinaison, gRPC dispose d'un type de méthode de service :

  • RPC simple ou unaire : les clients envoient un message simple et reçoivent une réponse simple du serveur, c'est-à-dire un appel de fonction standard ;
  • Response-streaming ou server streaming : les clients envoient un message simple et reçoivent un flux de messages du serveur ;
  • Request-streaming ou streaming client : les clients envoient un flux de messages et reçoivent un message simple du serveur ;
  • Bidirectional-streaming : les clients envoient un flux de messages et reçoivent un autre flux de messages du serveur.

La communication par ces méthodes peut être asynchrone (par défaut) ou synchrone, selon les besoins du client.

TLa page gRPC core-concepts définit ces types comme des caractéristiques du cycle de vie de gRPC. Elle met également en évidence d'autres fonctionnalités qui n'entrent pas dans le cadre de cette introduction, mais vous pouvez consulter les liens ci-dessous si vous souhaitez obtenir de plus amples informations :

Avantages et inconvénients

Voici quelques avantages et inconvénients que j'ai trouvés dans certains articles :

Avantages :

  • Des messages plus légers. Le tampon du protocole est un format binaire, ce qui permet d'éviter la surcharge JSON créée par les caractères spéciaux.
  • Sérialisation/désérialisation rapide. Encore une fois, en raison de son format binaire, les tampons de protocole pourraient être sérialisés/désérialisés dans des stubs clients utilisant des langages spécifiques sans interpréteurs.
  • Clients intégrés (stubs). Les tampons de protocole ont des générateurs intégrés pour la plupart des langages utilisés, contrairement à JSON qui dépend d'outils tiers comme OpenAPI et ses générateurs de clients.
  • Requêtes parallèles. HTTP/1 autorise jusqu'à 6 connexions simultanées, bloquant toute autre demande jusqu'à ce que les 6 connexions soient terminées - un problème connu sous le nom de HOL (head of line blocking) ; HTTP/2 corrige ces limitations.
  • Conception d'API axée sur le contrat. Bien que les API REST puissent exposer un contrat par le biais d'outils tiers comme OpenAPI, dans gRPC, un tel contrat est explicitement déclaré par les tampons du protocole.
  • Streaming natif. Grâce aux capacités de streaming de HTTP/2, gRPC permet un modèle de streaming bidirectionnel natif, contrairement à REST, qui doit imiter ce comportement sur HTTP/1.

Inconvénients :

  • Des messages plus légers. Le tampon du protocole est un format binaire, ce qui permet d'éviter la surcharge JSON créée par les caractères spéciaux.
  • Sérialisation/désérialisation rapide. Encore une fois, en raison de son format binaire, les tampons de protocole pourraient être sérialisés/désérialisés dans des stubs clients utilisant des langages spécifiques sans interpréteurs.
  • Clients intégrés (stubs). Les tampons de protocole ont des générateurs intégrés pour la plupart des langages utilisés, contrairement à JSON qui dépend d'outils tiers comme OpenAPI et ses générateurs de clients.
  • Requêtes parallèles. HTTP/1 autorise jusqu'à 6 connexions simultanées, bloquant toute autre demande jusqu'à ce que les 6 connexions soient terminées - un problème connu sous le nom de HOL (head of line blocking) ; HTTP/2 corrige ces limitations.
  • Conception d'API axée sur le contrat. Bien que les API REST puissent exposer un contrat par le biais d'outils tiers comme OpenAPI, dans gRPC, un tel contrat est explicitement déclaré par les tampons du protocole.
  • Streaming natif. Grâce aux capacités de streaming de HTTP/2, gRPC permet un modèle de streaming bidirectionnel natif, contrairement à REST, qui doit imiter ce comportement sur HTTP/1.

Toutefois, ces avantages et inconvénients ne constituent pas un consensus. Ils dépendent fortement des besoins de votre application.

Par exemple, un inconvénient supposé de REST/JSON est le besoin d'outils tiers comme OpenAPI. Cependant, ce n'est pas forcément un problème puisque ces outils sont largement supportés, maintenus et utilisés par plusieurs communautés/entreprises de développement dans le monde.

D'autre part, si votre projet doit faire face à des complexités que gRPC peut résoudre mieux que REST, vous devriez choisir gRPC, même si cette décision entraîne quelques complications, comme la création d'une équipe de développeurs qualifiés.

Où et quand faut-il utiliser le gRPC ?

Voici quelques cas d'utilisation où vous avez besoin de gRPC :

  • Communication par microservices
  • Application client/serveur, où les clients fonctionnent sur un matériel et/ou un réseau limité, en supposant que HTTP/2 soit disponible.
  • Interopérabilité facilitée par la conception d'une solide API contractuelle

Les acteurs du gRPC

Certaines grandes entreprises utilisent gRPC pour relever des défis spécifiques :

  • Salesforce : gRPC permet à la plate-forme de l'entreprise d'accroître la robustesse de l'interopérabilité grâce à la conception de contrats solides fournis par les tampons de protocole.
  • Netflix : utilise gRPC pour améliorer son environnement de microservices.
  • Spotify : comme Netflix, utilise gRPC pour relever les défis des microservices et gérer de nombreuses API.

Hello world via Embedded Python

Ok, après une brève introduction sur ce que représente gRPC et ce qu'il fait, vous pouvez maintenant le gérer, alors jouons un peu. Comme le dit le titre de cet article, il s'agit d'une adaptation de l'exemple original de hello world utilisant IRIS Embedded Python.

En fait, cet exemple est une modification de deux autres instances que l'on peut trouver dans le dépôt d'échantillons gRPC - helloworld et hellostreamingworld. À l'aide de cet exemple, je voudrais vous montrer comment envoyer et recevoir un message simple en mode simple et en mode flux. Bien qu'il s'agisse d'un exemple simple sans fonctionnalités vraiment utiles, il vous aidera à comprendre les principaux concepts liés au développement d'une application gRPC.

Installation de gRPC pour Python

Tout d'abord, installons les paquets nécessaires à l'utilisation de gRPC en Python. Si vous exécutez l'exemple à partir du conteneur défini dans mon projet github project, et si vous avez probablement déjà installé ces paquets, vous pouvez donc sauter cette étape.

python3 -m pip install --upgrade pip
python3 -m pip install --upgrade --target /usr/irissys/mgr/python grpcio grpcio-tools

Définir le contrat de service

Voyons maintenant le contrat de service, c'est-à-dire le fichier tampon du protocole (ou simplement protobuf pour faire court), avec le schéma des messages et les méthodes disponibles :

syntax = "proto3";

package helloworld;

// La définition du service d'accueil.
service MultiGreeter {
  // Envoi d'un message d'accueil
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Envoi de messages d'accueil multiples
  rpc SayHelloStream (HelloRequest) returns (stream HelloReply) {}
}

// Le message de requête contenant le nom de l'utilisateur et le nombre de messages d'accueil.
// qu'il souhaite.
message HelloRequest {
  string name = 1;
  Int32 num_greetings = 2;
}

// Un message de réponse contenant un message d'accueil
message HelloReply {
  string message = 1;
}

Ce fichier protobuf définit un service appelé MultiGreeter avec deux méthodes RPC, SayHello() et SayHelloStream().

La méthode SayHello() reçoit un message HelloRequest et envoie un message HelloReply. De même, la méthode SayHelloStream() reçoit et envoie les mêmes messages, mais elle envoie un flux de messages HelloRequest au lieu d'un message unique.

Après la définition du service, il y a les définitions des messages, HelloRequest et HelloReply. Le message HelloRequest encapsule juste deux champs : une chaîne de caractères appelée name et un entier appelé num_greetings. Le message HelloReply ne contient qu'un seul champ, une chaîne de caractères appelée message.

Les chiffres qui suivent les champs sont appelés numéros de champ. Ces numéros ne doivent pas être modifiés une fois que le message est utilisé, car ils servent d'identifiants.

Génération de code Python à partir du contrat de service

Comme vous pouvez probablement le remarquer, nous n'avons pas besoin d'écrire de code dans la définition de protobuf, seulement des interfaces. La tâche de mettre en œuvre le code pour différents langages de programmation est faite par le compilateur protoc de protobuf. Il existe un compilateur protoc pour chacun des langages supportés par gRPC.

Pour Python, le compilateur est déployé comme un module appelé grpc_tools.protoc.

Pour compiler la définition du protobuf en code Python, exécutez la commande suivante (en supposant que vous utilisez mon projet):

cd /irisrun/repo/jrpereira/python/grpc-test
/usr/irissys/bin/irispython -m grpc_tools.protoc -I ./ --python_out ./ --grpc_python_out ./ helloworld.proto

Cette commande invoque le module grpc_tools.protoc - le compilateur protoc pour Python, avec les paramètres suivants :

  • helloword.proto : le fichier .proto principal pour le contrat de service
  • -I : l'emplacement des fichiers .proto où le compilateur cherchera les dépendances
  • --python_out : l'emplacement du code Python généré pour les messages
  • --grpc_python_out : l'emplacement du code Python généré pour un serveur et un stub (client) basé sur les méthodes RPC dans la définition du service

Dans ce cas, tous ces paramètres de localisation sont configurés pour le répertoire courant.

Le code généré par le compilateur protoc n'est pas le meilleur exemple de lisibilité, bien qu'il ne soit pas si difficile à comprendre. Vous pouvez le vérifier dans le répertoire passé au compilateur protoc.

De toute façon, ces fichiers sont destinés à être importés dans votre propre code, alors utilisons-les, en mettant en œuvre un serveur et un client.

Mise en œuvre d'un serveur pour le service

Afin de mettre en œuvre un serveur pour le service défini ci-dessus, utilisons Embedded Python.

Tout d'abord, définissons un serveur en utilisant un fichier Python où la logique du serveur est mise en œuvre. J'ai décidé de le mettre en œuvre de cette façon en raison de la nécessité d'utiliser une bibliothèque de concurrence Python.

"""
La mise en œuvre de Python pour le serveur GRPC helloworld.Greeter.
Adapté de:
    - https://github.com/grpc/grpc/blob/master/examples/python/helloworld/async_greeter_server.py
    - https://github.com/grpc/grpc/blob/master/examples/python/hellostreamingworld/async_greeter_server.py
    - https://groups.google.com/g/grpc-io/c/6Yi_oIQsh3w
"""

from concurrent import futures
import logging
import signal
from typing import Any
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter

import grpc
from helloworld_pb2 import HelloRequest, HelloReply
from helloworld_pb2_grpc import MultiGreeterServicer, add_MultiGreeterServicer_to_server

import iris

NUMBER_OF_REPLY = 10

parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
parser.add_argument("-p", "--port", default="50051", help="Server port")
args = vars(parser.parse_args())

class Greeter(MultiGreeterServicer):

    def SayHello(self, request: HelloRequest, context) -> HelloReply:
        logging.info("Serving SayHello request %s", request)
        obj = iris.cls("dc.jrpereira.gRPC.HelloWorldServer")._New()
        # à votre code ObjectScript
        return obj.SayHelloObjectScript(request)

    def SayHelloStream(self, request: HelloRequest, context: grpc.aio.ServicerContext) -> HelloReply:
        logging.info("Serving SayHelloStream request %s", request)
        obj = iris.cls("dc.jrpereira.gRPC.HelloWorldServer")._New()
        n = request.num_greetings
        if n == 0:
            n = NUMBER_OF_REPLY
        for i in range(n):
            # à votre code ObjectScript
            yield obj.SayHelloObjectScript(request)

def get_server():
    port = args["port"]
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    add_MultiGreeterServicer_to_server(Greeter(), server)
    listen_addr = f"[::]:{port}"
    server.add_insecure_port(f"[::]:{port}")
    logging.info("Starting server on %s", listen_addr)
    return server

def handle_sigterm(*_: Any) -> None :
    """Shutdown gracefully."""
    done_event = server.stop(None)
    done_event.wait(None)
    print('Stop complete.')

logging.basicConfig(level=logging.INFO)

server = get_server()
server.start()

# https://groups.google.com/g/grpc-io/c/6Yi_oIQsh3w
signal.signal(signal.SIGTERM, handle_sigterm)

server.wait_for_termination()

Comme vous pouvez le voir, ici les méthodes définies dans la spécification protobuf - SayHello() et SayHelloStream(), sont mises en oeuvre.

La méthode SayHello() n'envoie qu'une seule valeur, alors que la méthode SayHelloStream() retourne au client un nombre de messages égal à NUMBER_OF_REPLY, grâce à l'opérateur Python yield.

Notez également que j'ai créé un accrochage pour injecter la logique définie en ObjectScript. Ainsi, j'ai défini une méthode appelée SayHelloObjectScript dans la classe dc.jrpereira.gRPC.HelloWorldServer :

Method SayHelloObjectScript(request)
{
    Set sys = $system.Python.Import("sys")
    Do sys.path.append("/usr/irissys/mgr/python/grpc-test/")

    Set helloworldpb2 = $system.Python.Import("helloworld_pb2")

    Set reply = helloworldpb2.HelloReply()
    Set reply.message = "Hi "_request.name_"! :)"

    Return reply
}

De cette façon, vous pouvez recevoir des demandes de clients gRPC à partir de Python et les traiter en utilisant un mélange de logique codée en Python et ObjectScript..

Mise en œuvre d'un client pour le service

Comme un code client n'a pas besoin de concurrence, je l'ai mis en œuvre en utilisant du code Python directement dans une classe ObjectScript :

ClassMethod ExecutePython() [ Language = python ]
{
    import sys
    sys.path.append('/usr/irissys/mgr/python/grpc-test/')

    import grpc
    from helloworld_pb2 import HelloRequest
    from helloworld_pb2_grpc import MultiGreeterStub

    channel = grpc.insecure_channel('localhost:50051')
    stub = MultiGreeterStub(channel)

    response = stub.SayHello(HelloRequest(name='you'))
    print("Greeter client received: " + response.message)

    for response in stub.SayHelloStream(HelloRequest(name="you")):
        print("Greeter client received from stream: " + response.message)
}

Tout d'abord, nous ajoutons le répertoire grpc-test au chemin Python afin de pouvoir importer le code.

Ensuite, une connexion à localhost sur le port 50051 et un stub (ou client) sont créés.

Avec un tel client, nous pouvons demander des informations sur le serveur qui écoute sur localhost:50051, grâce aux méthodes SayHello() et SayHelloStream().

La méthode SayHello() ne renvoie qu'une seule valeur, il nous suffit donc de faire une requête et d'utiliser sa réponse. En revanche, la méthode SayHelloStream() renvoie un flux de données dans une collection, et nous devons donc le traverser pour obtenir toutes ses données.

Test du code

Ok, maintenant testons notre code.

Vous pouvez consulter tout ce code dans mon projet hello world. Suivez ces étapes pour le faire fonctionner :

git clone https://github.com/jrpereirajr/iris-grpc-example
cd iris-grpc-example
docker-compose up -d

Ensuite, ouvrez un terminal IRIS via le terminal système ou via Visual Studio Code :

docker exec -it iris-grpc-example_iris_1 bash
iris session iris

Lancez notre serveur gRPC :

Set server = ##class(dc.jrpereira.gRPC.HelloWorldServer).%New()
Do server.Start()

Maintenant, créons un client gRPC pour intéragir avec ce serveur :

Set client = ##class(dc.jrpereira.gRPC.HelloWorldClient).%New()
Do client.ExecutePython()

Si tout est OK, vous devriez voir un tas de messages d'accueil dans le terminal.

Enfin, arrêtons le serveur :

Do server.Stop()

Utilisation de l'utilitaire grpcurl dans notre hello world

Comme je l'ai déjà dit, l'utilitaire grpcurl est un équivalent de curl, mais ici au lieu d'agir comme un client HTTP (comme curl), nous utilisons grpcurl comme un client gRPC pour tester les services d'un serveur gRPC en fonctionnement. Donc, utilisons-le pour jouer un peu plus avec notre hello world.

Tout d'abord, téléchargeons et installons l'utilitaire grpcurl :

cd /tmp
wget https://github.com/fullstorydev/grpcurl/releases/download/v1.8.6/grpcurl_1.8.6_linux_x86_64.tar.gz
tar -zxvf grpcurl_1.8.6_linux_x86_64.tar.gz

Vérifiez si l'installation est correcte, en tapant :

./grpcurl --help

Si tout est OK, vous devriez recevoir un résultat avec toutes les options grpcurl.

Maintenant, demandons quels services sont disponibles sur le serveur :

./grpcurl \
	-plaintext \
	-import-path /irisrun/repo/jrpereira/python/grpc-test \
	-proto helloworld.proto \
	localhost:50051 \
	list

Vous devriez recevoir la réponse suivante :

helloworld.MultiGreeter

Comme vous pouvez le voir, l'utilitaire a retourné notre service défini dans le fichier proto (helloworld.MultiGreeter) sous forme de réponse pour lister tous les services disponibles .

Dans la commande ci-dessus, j'ai mis chaque paramètre dans une ligne séparée. Donc, nous allons expliquer chacun d'eux :

-plaintext: permet d'utiliser gRPC sans TLS (mode non sécurisé) ; nous l'utilisons ici car nous n'avons pas implémenté de connexion sécurisée pour notre serveur. Bien entendu, ce mode ne doit être utilisé que dans un environnement de non-production -import-path et -proto : chemin et nom du fichier .proto (définition du service) ; nécessaire si le serveur n'implémente pas la réflexion

Après ces paramètres, nous fournissons le nom d'hôte et le port du serveur, et ensuite une commande grpcurl - list dans ce cas.

Maintenant, demandons toutes les méthodes du service helloworld.MultiGreeter :

./grpcurl \
	-plaintext \
	-import-path /irisrun/repo/jrpereira/python/grpc-test \
	-proto helloworld.proto \
	localhost:50051 \
	list helloworld.MultiGreeter

Vous devriez recevoir le message suivant :

helloworld.MultiGreeter.SayHello
helloworld.MultiGreeter.SayHelloStream

Comme vous pouvez le constater, ce sont les méthodes définies dans le fichier proto utilisé pour générer le code de notre serveur.

Ok, maintenant testons la méthode SayHello() :

./grpcurl \
	-plaintext  \
	-d '{"name":"you"}' \
	-import-path /irisrun/repo/jrpereira/python/grpc-test \
	-proto helloworld.proto \
	localhost:50051 \
	helloworld.MultiGreeter.SayHello

Voici le résultat attendu (tout comme celui que notre client a mis en œuvre précédemment) :

{
  "message": "Salut! :)"
}

Testons également l'autre méthode, SayHelloStream() :

./grpcurl \
	-plaintext -d '{"name":"you"}' \
	-import-path /irisrun/repo/jrpereira/python/grpc-test \
	-proto helloworld.proto localhost:50051 \
	helloworld.MultiGreeter.SayHelloStream

Et, nous devrions avoir un flux avec 10 messages d'accueil :

{
  "message": "Salut! :)"
}
{
  "message": "Salut! :)"
}
...
{
  "message": "Salut! :)"
}

Enfin, faisons un léger changement sur cette commande pour utiliser une autre propriété dans le message protobuf, la propriété num_greetings. Cette propriété est utilisée par le serveur pour contrôler le nombre de messages qui seront envoyés dans le flux.

Ainsi, cette commande demande au serveur de ne renvoyer que 2 messages dans le flux, au lieu des 10 par défaut :

./grpcurl \
	-plaintext -d '{"name":"you", "num_greetings":2}' \
	-import-path /irisrun/repo/jrpereira/python/grpc-test \
	-proto helloworld.proto localhost:50051 \
	helloworld.MultiGreeter.SayHelloStream

Et c'est ce que vous devriez voir dans le terminal :

{
  "message": "Salut! :)"
}
{
  "message": "Salut! :)"
}

Conclusion

Dans cet article, un aperçu de gRPC a été donné, avec ses avantages et ses inconvénients - principalement par rapport à REST. De plus, quelques exemples de son utilisation avec IRIS ont été testés, en adaptant certains échantillons pour Python, présentés dans le répertoire officiel de gRPC.

Comme nous l'avons dit précédemment, gRPC a été utilisé dans certains cas, et l'interopérabilité est l'un des cas possibles. La création d'un adaptateur d'interopérabilité IRIS est donc une façon logique de penser à une utilisation pratique de gRPC dans IRIS.

Cependant, cela demandera plus d'efforts, donc ce sera le sujet d'un autre article. =)

J'espère que vous avez trouvé les informations présentées ici utiles ! A bientôt !

Références

https://grpc.io/docs/what-is-grpc/introduction/https://developers.google.com/protocol-buffershttps://en.wikipedia.org/wiki/GRPChttps://www.imaginarycloud.com/blog/grpc-vs-rest/https://www.vineethweb.com/post/grpc/https://www.capitalone.com/tech/software-engineering/grpc-framework-for-microservices-communication/https://www.altexsoft.com/blog/what-is-grpc/

0
0 1197
Article Lorenzo Scalese · Juil 25, 2022 2m read

J'aimerais partager avec vous un exemple de la manière dont la nouvelle fonctionnalité d'Embedded Python d'IRIS m'a aidé dans mes tâches quotidiennes.

Dans le cadre de ma participation au projet iris-kaggle-socrata-generator avec Henrique Dias, j'ai dû dézipper des jeux de données de Kaggle afin de les importer.

Une telle tâche a été facilement réalisée en utilisant la librairie de zipfile en Python (ce code a été copié à partir de stackoverflow):

Method UnZip(pZipFileName As %String, pExtractToDir As %String) As %DynamicArray [ Language = python ]
{
    import zipfile
    import iris
    with zipfile.ZipFile(pZipFileName, 'r') as zip_ref:
        zip_ref.extractall(pExtractToDir)
        fileList = zip_ref.namelist()

    dynarray = iris.cls("%DynamicArray")._New()
    for file in fileList:
        dynarray._Push(file)
    return dynarray
}

Notez le modificateur de méthode [Language = python]. Cette fonctionnalité a été testée dans l'image IRIS intersystemsdc/iris-ml-community:2021.2.0.617.0-zpm. Consultez l'application OEX correspondante pour un exemple fonctionnel.

Vous pouvez voir qu'il s'agit d'un code Embedded Python dans une méthode ObjectScript. Vous pouvez donc l'utiliser dans tout code ObjectScript. Par exemple :

Method HttpDownloadZIP(pHttpParams As %DynamicObject) As %DynamicObject
{
    Set retorno = {
        "warnings": []
    }
    …
    Set fileName = ..SaveRequestToFile(request)
    …
    Set fileList = ..UnZip(fileName, zipTempDir)
    If (fileList.%Size() > 1) {
        Do retorno.warnings.%Push("Il y a plus d'un fichier dans le fichier zip. J'utilise le premier.")
    }
    …
    Return retorno
}

Vous pouvez également accéder au code complet ici.

Notez que la méthode Embedded Python utilise la librairie Python zipfile et renvoie les résultats de la méthode dans un %DynamicArray, utilisé dans les méthodes ObjectScript. Cela signifie qu'un objet créé dans un contexte Python peut être accédé par un contexte ObjectScript de manière transparente.

Vous pouvez en savoir plus sur la façon d'utiliser Embedded Python ici.

Et voilà, c'est tout ! J'espère que cela pourra vous être utile pour vos projets. A bientôt !

0
0 86
Annonce Irène Mykhailova · Juil 17, 2022

Salut la communauté,

Nous sommes ravis d'annoncer que les rencontres de développeurs InterSystems sont enfin de retour en personne !

Le premier meetup lié au Python aura lieu le 21 juillet à 18h00 à Democracy Brewing, Boston, MA. Il y aura 2-3 courtes présentations liées au Python, des questions-réponses, des sessions de réseautage ainsi que de la bière gratuite avec des collations et des visites de la brasserie.

AGENDA:

0
0 48
Article Robert Cemper · Juil 1, 2022 8m read

Je m'intéresse particulièrement à l'utilisation des Globales avec Embedded Python.
Alors, j'ai commencé à consulter la documentation officielle.

#1 Introduction to Globals
Une tentative de description générique de ce qu'est une Globale. Pointant ensuite vers:

#2 A Closer Look at ObjectScript
Mais où puis-je trouver Embedded Python ?
Plus bas, se trouve:

#3 Embedded Python

3.1 Embedded Python Overview
3.1.1 Work with Globals

Idéal si vous n'avez jamais vu une Globale.
Sinon ce n'est qu'un exemple primitif choquant
3.2 Using Embedded Python
Dernier espoir: >>> Mais, absolument RIEN de visible.

3
0 139
Article Guillaume Rongier · Juil 6, 2022 11m read

1. intersystems-iris-docker-rest-template

Il s'agit d'un modèle d'une application REST API intégrée en python dans InterSystems IRIS. Elle possède également une spécification OPEN API et peut être développée avec Docker et VSCode.

1. intersystems-iris-docker-rest-template

2. Conditions préalables

3. Installation

3.1. Installation pour le développement

3.2. Portail de gestion et et VSCode

3.3. Avoir le répertoire ouvert à l'intérieur du conteneur

4. Comment cela fonctionne

5. Comment l'utiliser

5.1. Requête POST

5.1.1. Test de la requête POST

5.1.2. Comment fonctionne la requête POST

5.2. Requête GET

0
0 52
Annonce Robert Bira · Juin 13, 2022

 Un webinaire a été organisé le mardi 8 juin pour vous faire découvrir l'usage de Python dans InterSystems IRIS ® et InterSystems IRIS for Health™ version 2022.1.

Dans cette présentation technique, nous détaillons l'étendue du support dont bénéficient les développeurs Python™ en utilisant la plateforme de gestion de données InterSystems IRIS®, notamment :

0
0 54
Article Lorenzo Scalese · Mai 18, 2022 14m read

La recherche d'images comme celle de Google est une fonctionnalité intéressante qui m'émerveille - comme presque tout ce qui est lié au traitement des images.

Il y a quelques mois, InterSystems a publié un aperçu de Python Embedded. Comme Python dispose de nombreuses librairies pour le traitement d'images, j'ai décidé de lancer ma propre tentative pour jouer avec une sorte de recherche d'images - une version beaucoup plus modeste en fait :-)


--- #### Un peu de théorie 🤓

Pour réaliser un système de recherche d'images, il faut d'abord sélectionner un ensemble de caractéristiques à extraire des images - ces caractéristiques sont également appelées descripteurs. L'étendue de chaque composante de ces descripteurs crée ce qu'on appelle un espace de caractéristiques, et chaque instance de cet espace est appelée un vecteur. Le nombre d de composantes nécessaires pour décrire les vecteurs, définit la dimension de l'espace des caractéristiques et des vecteurs, appelé d-dimensionnel.


Figure 1 - Un espace caractéristique tridimensionnel et un vecteur descripteur dans cet espace.
Credits: https://tinyurl.com/ddd76dln
---

Une fois l'ensemble des descripteurs définis, tout ce que vous avez à faire pour rechercher une image dans la base de données est d'extraire les mêmes descripteurs d'une image à rechercher et de les comparer aux descripteurs des images de la base de données - qui ont été précédemment extraits.

Dans ce travail, on a simplement utilisé la couleur dominante de l'image comme descripteur (j'ai dit que c'était une version modeste...). Comme une représentation RVB des couleurs a été utilisée, l'espace caractéristique est un espace tridimensionnel - 3d en abrégé. Chaque vecteur dans un tel espace a 3 composantes - (r,g,b), dans la gamme [0, 255].


Figure 2 - L'espace tridimensionnel des caractéristiques RVB
Credits: https://www.baslerweb.com/fp-1485687434/media/editorial/content_images/faqs/faq_RGB_1.gif
---

En traitement du signal, il est très fréquent d'avoir des espaces à n dimensions avec des valeurs de n bien supérieures à 3. En fait, vous pouvez combiner un grand nombre de descripteurs dans un même vecteur afin d'obtenir une meilleure précision. C'est ce qu'on appelle la sélection de caractéristiques et c'est une étape très importante dans les tâches de classification/reconnaissance.

Il est également courant de normaliser la plage de dimensions en [0, 1], mais pour des raisons de simplicité, ce travail utilise la plage par défaut [0, 255].

L'avantage de modéliser des caractéristiques sous forme de vecteurs est la possibilité de les comparer à travers des métriques de distance. Il existe de nombreuses distances, chacune ayant ses avantages et ses inconvénients, selon que l'on recherche la performance ou la précision. Dans ce travail, j'ai choisi des distances faciles à calculer - manhattan et chebyshev, qui sont essentiellement des différences absolues avec une précision raisonnable.


Figure 3 - Représentation de certains paramètres de distance
Credits: https://i0.wp.com/dataaspirant.com/wp-content/uploads/2015/04/cover_post_final.png
---

Index fonctionnel

Mais il ne s'agit que des outils nécessaires pour comparer les images en fonction de leur contenu. Si vous ne disposez pas d'un langage de requête comme SQL, vous vous retrouverez avec des méthodes et des paramètres de recherche fastidieux... De plus, en utilisant SQL, vous pouvez combiner cet index avec d'autres opérateurs bien connus, créant ainsi des requêtes complexes.

C'est ici où Functional Index d'InterSystems est très utile.

Un index fonctionnel est une classe qui implémente la classe abstraite %Library.FunctionalIndex qui implémente certaines méthodes afin de gérer la tâche d'indexation dans une instruction SQL. Ces méthodes traitent essentiellement les insertions, les suppressions et les mises à jour.

/// Indexation fonctionnelle permettant d'optimiser les requêtes sur les données d'image
Class dc.multimodel.ImageIndex.Index Extends %Library.FunctionalIndex [ System = 3 ]
{

/// Cardinalité de l'espace des caractéristiques
/// Comme cette classe est destinée à indexer l'image dans l'espace RVB, sa cardinalité est de 3
Paramètre Cardinalité = 3 ;

/// Cette méthode est invoquée lorsqu'une instance existante d'une classe est supprimée.
ClassMethod DeleteIndex(pID As %CacheString, pArg... As %Binary) [ CodeMode = generator, ServerOnly = 1 ]
{
	If (%mode '= "method") {
		$$$GENERATE("Set indexer = ##class(dc.multimodel.ImageIndex.Indexer).GetInstance("""_%class_""", """_%property_""")")
		$$$GENERATE("Set indexer.Cardinality = "_..#Cardinality)
		$$$GENERATE("Do indexer.Delete(pID, pArg...)")
	}
	Return $$$OK
}

ClassMethod Find(pSearch As %Binary) As %Library.Binary [ CodeMode = generator, ServerOnly = 1, SqlProc ]
{
	If (%mode '= "method") {
		$$$GENERATE("Set result = """"")
		$$$GENERATE("Set result = ##class(dc.multimodel.ImageIndex.SQLFind).%New()")
		$$$GENERATE("Set indexer = ##class(dc.multimodel.ImageIndex.Indexer).GetInstance("""_%class_""", """_%property_""")")
		$$$GENERATE("Set indexer.Cardinality = "_..#Cardinality)
		$$$GENERATE("Set result.Indexer = indexer")
		$$$GENERATE("Do result.PrepareFind(pSearch)")
		$$$GENERATE("Return result")
	}
	Return $$$OK
}

/// Cette méthode est invoquée lorsqu'une nouvelle instance d'une classe est insérée dans la base de données.
ClassMethod InsertIndex(pID As %CacheString, pArg... As %Binary) [ CodeMode = generator, ServerOnly = 1 ]
{
	If (%mode '= "method") {
		$$$GENERATE("Set indexer = ##class(dc.multimodel.ImageIndex.Indexer).GetInstance("""_%class_""", """_%property_""")")
		$$$GENERATE("Set indexer.Cardinality = "_..#Cardinality)
		$$$GENERATE("Do indexer.Insert(pID, pArg...)")
	}
	Return $$$OK
}

ClassMethod PurgeIndex() [ CodeMode = generator, ServerOnly = 1 ]
{
	If (%mode '= "method") {
		$$$GENERATE("Set indexer = ##class(dc.multimodel.ImageIndex.Indexer).GetInstance("""_%class_""", """_%property_""")")
		$$$GENERATE("Set indexer.Cardinality = "_..#Cardinality)
		$$$GENERATE("Set indexGbl = indexer.GetIndexLocation()")
		$$$GENERATE("Do indexer.Purge()")
	}
	Return $$$OK
}

/// Cette méthode est invoquée lorsqu'une instance existante d'une classe est mise à jour.
ClassMethod UpdateIndex(pID As %CacheString, pArg... As %Binary) [ CodeMode = generator, ServerOnly = 1 ]
{
	If (%mode '= "method") {
		$$$GENERATE("Set indexer = ##class(dc.multimodel.ImageIndex.Indexer).GetInstance("""_%class_""", """_%property_""")")
		$$$GENERATE("Set indexer.Cardinality = "_..#Cardinality)
		$$$GENERATE("Do indexer.Update(pID, pArg...)")
	}
	Return $$$OK
}

}

J'ai caché une partie du code d'implémentation pour des raisons de lisibilité ; vous pouvez consulter le code dans le lien OpenExchange.

Une autre classe abstraite doit être implémentée, c'est %SQL.AbstractFind, afin de rendre disponible l'utilisation de l'opérateur %FIND pour demander au moteur SQL d'utiliser votre index personnalisé.

Une explication beaucoup plus détaillée et conviviale des index fonctionnels est donnée par @alexander-koblov qui constitue également un excellent exemple d'index fonctionnel. Je vous recommande vivement de le lire.

Si vous souhaitez aller plus loin, vous pouvez jouer avec le code source des index %iFind et %UIMA index.

Dans ce travail, j'ai configuré une classe de test de persistance simple, où le chemin des images est stocké, et un index personnalisé pour la recherche d'images est défini pour ce champ.

Class dc.multimodel.ImageIndex.Test Extends %Persistent
{

Property Name As %String;

Property ImageFile As %String(MAXLEN = 1024);

Index idxName On Name [ Type = bitmap ];

Index idxImageFile On (ImageFile) As dc.multimodel.ImageIndex.Index;

Notez que idxImageFile est un index personnalisé (dc.multimodel.ImageIndex.Index) pour le champ Image (qui stocke le chemin de l'image).

Le tour de Python (et COS) !

Ainsi, les classes abstraites d'index fonctionnel vous donneront les points d'entrée où vous pourrez effectuer l'extraction de caractéristiques et la recherche lors de l'exécution des instructions SQL. Maintenant, c'est au tour de Python !

Vous pouvez importer et exécuter le code Python dans un contexte COS en utilisant Python intégré. Par exemple, pour extraire la couleur dominante d'une image :

Method GetDominantColorRGB(pFile As %String, ByRef pVector) As %Status
{
  Set sc = $$$OK
  Try {
    Set json = ##class(%SYS.Python).Import("json")
    Set fastcolorthief = ##class(%SYS.Python).Import("fast_colorthief")
    Set imagepath = pFile
    Set dominantcolor = fastcolorthief."get_dominant_color"(imagepath, 1)
    Set vector = {}.%FromJSON(json.dumps(dominantcolor))
    Set n = ..Cardinality - 1
    For i = 0:1:n {
      Set pVector(i) = vector.%Get(i)
    }
  } Catch(e) {
    Set sc = e.AsStatus()
  }
  Return sc
}

Dans cette méthode, deux librairies Python sont importées (json et fast_colorthief). La librairie fast_colorthief renvoie une représentation Python de type tableau 3-d avec les valeurs de RGB ; l'autre librairie - json, sérialise ce tableau dans un %DynamicArray.

La couleur dominante est extraite pour chaque enregistrement qui est inséré ou mis à jour - une fois que l'index fonctionnel lève les appels aux méthodes InsertIndex et UpdateIndex en réponse aux insertions et mises à jour dans le tableau. Ces caractéristiques sont stockées dans l'index global du tableau :

Method Insert(pID As %CacheString, pArgs... As %Binary)
{
	// pArgs(1) has the image path
	$$$ThrowOnError(..GetDominantColor(pArgs(1), .rgb))
	Set idxGbl = ..GetIndexLocation()
	Set @idxGbl@("model", pID) = ""
  	Merge @idxGbl@("model", pID, "rgb") = rgb
  	Set @idxGbl@("last-modification") = $ZTIMESTAMP
}

Method Update(pID As %CacheString, pArg... As %Binary)
{
	// pArgs(1) has the image path
  	Set idxGbl = ..GetIndexLocation()
  	Do ..GetDominantColor(pArg(1), .rgb)
  	Kill @idxGbl@("model", pID)
  	Set @idxGbl@("model", pID) = ""
  	Merge @idxGbl@("model", pID, "rgb") = rgb
  	Set @idxGbl@("last-modification") = $ZTIMESTAMP
}

De la même manière, lorsque des enregistrements sont supprimés, l'index fonctionnel lance des appels aux méthodes DeleteIndex et PurgeIndex. À leur tour, les fonctionnalités doivent être supprimées de l'index global du tableau :

Method Delete(pID As %CacheString, pArg... As %Binary)
{
  	Set idxGbl = ..GetIndexLocation()
  	Kill @idxGbl@("model", pID)
  	Set @idxGbl@("last-modification") = $ZTIMESTAMP
}

Method Purge(pID As %CacheString, pArg... As %Binary)
{
  	Set idxGbl = ..GetIndexLocation()
  	Kill @idxGbl
  	Set @idxGbl@("last-modification") = $ZTIMESTAMP
}

L'index global est récupéré par introspection dans la classe persistante :

Method GetIndexLocation() As %String
{
	Set storage = ##class(%Dictionary.ClassDefinition).%OpenId(..ClassName).Storages.GetAt(1).IndexLocation
	Return $NAME(@storage@(..IndexName))
}

Lorsque les utilisateurs utilisent l'index dans les clauses WHERE, la méthode Find() est activée par l'index de la fonction. Les instructions de la requête sont transmises afin que vous puissiez les analyser et décider de ce qu'il faut faire. Dans ce travail, les paramètres sont sérialisés en JSON afin de faciliter leur analyse. Les paramètres de la requête ont la structure suivante :

SELECT ImageFile
FROM dc_multimodel_ImageIndex.Test
WHERE ID %FIND search_index(idxImageFile, '{"color_similarity":{"image":"/data/img/test/161074693598711.jpg","first":5,"strategy":"knn"}}')

Dans cette instruction, vous pouvez voir l'utilisation de l'opérateur %FIND et de la fonction search_index. C'est ainsi que SQL accède à notre index personnalisé.

Les paramètres de search_index définissent l'index à rechercher - idxImageFile, dans ce cas ; et la valeur à envoyer à l'index. Ici, l'index attend un objet JSON, avec une configuration d'objet définissant : (i) le chemin de l'image, (ii) une limite pour les résultats, et (iii) une stratégie de recherche.

Une stratégie de recherche est simplement l'algorithme à utiliser pour effectuer la recherche. Actuellement, deux stratégies sont mises en œuvre : (i) fullscan et (ii) knn, qui correspond à k-proches voisins.

La stratégie fullscan consiste simplement en une recherche exhaustive mesurant la distance entre l'image recherchée et chaque image stockée dans la base de données.

Method FullScanFindStrategy(ByRef pSearchVector, ByRef pResult) As %Status
{
	Set sc = $$$OK
	Try {
		Set idxGbl = ..Indexer.GetIndexLocation()
		Set rankGbl = ..Indexer.GetRankLocation()

		Set id = $ORDER(@idxGbl@("model", ""))
		While (id '= "") {
			If ($ISVALIDNUM(id)) {
				Merge vector = @idxGbl@("model", id, "rgb")
				Set distance = ..Indexer.GetL1Distance(.pSearchVector, .vector)
				Set result(distance, id) = ""
			}
			Set id = $ORDER(@idxGbl@("model", id))
		}

		Kill @rankGbl@(..ImagePath, ..FindStrategy)
		If (..First '= "") {
			Set c = 0
			Set distance = $ORDER(result(""))
			While (distance '= "") && (c < ..First) {
				Merge resultTmp(distance) = result(distance)

				Set id = $ORDER(result(distance, ""))
				While (id '= "") {
					Set @rankGbl@(..ImagePath, ..FindStrategy, id) = distance
					Set id = $ORDER(result(distance, id))
				}

				Set c = c + 1
				Set distance = $ORDER(result(distance))
			}
			Kill result
			Merge result = resultTmp
		}

		Merge pResult = result
	}
	Catch ex {
		Set sc = ex.AsStatus()
	}
	Return sc
}

La stratégie KNN utilise une approche plus sophistiquée. Elle utilise une librairie Python pour créer une structure arborescente appelée Ball Tree. Une telle arborescence convient à une recherche efficace dans un espace à n dimensions.

Method KNNFindStrategy(ByRef pSearchVector, ByRef pResult) As %Status
{
	Do ..Log(" ------ KNNFindStrategy ------ ")
	Set sc = $$$OK
	Try {
		Set idxGbl = ..Indexer.GetIndexLocation()
		Set rankGbl = ..Indexer.GetRankLocation()

		Set json = ##class(%SYS.Python).Import("json")
		Set knn = ##class(%SYS.Python).Import("knn")

		Set first = ..First
		Set k = $GET(first, 5)

		Set n = ..Indexer.Cardinality - 1
		Set x = ""
		For i = 0:1:n {
			Set $LIST(x, * + 1) = pSearchVector(i)
		}
		Set x = "[["_$LISTTOSTRING(x, ",")_"]]"

		$$$ThrowOnError(..CreateOrUpdateKNNIndex())
		Set ind = knn.query(x, k, idxGbl)
		Set ind = {}.%FromJSON(json.dumps(ind.tolist()))
		Set ind = ind.%Get(0)

		Kill result
		Kill @rankGbl@(..ImagePath, ..FindStrategy)
		Set n = k - 1
		For i=0:1:n {
			Set id = ind.%Get(i)
			Set result(i, id) = ""
			Set @rankGbl@(..ImagePath, ..FindStrategy, id) = i
		}
		Merge pResult = result
	}
	Catch ex {
		Set sc = ex.AsStatus()
	}
	Return sc
}

Le code Python pour générer une arborescence Ball Tree est présenté ci-dessous :

from sklearn.neighbors import BallTree
import numpy as np
import pickle
import base64
import irisnative

def get_iris():
  ip = "127.0.0.1"
  port = 1972
  namespace = "USER"
  username = "superuser"
  password = "SYS"

  connection = irisnative.createConnection(ip,port,namespace,username,password)
  dbnative = irisnative.createIris(connection)

  return (connection, dbnative)

def release_iris(connection):
  connection.close()

def normalize_filename(filename):
  filename = filename.encode('UTF-8')
  return base64.urlsafe_b64encode(filename).decode('UTF-8')

def create_index(index_global, cardinality):
  connection, dbnative = get_iris()
  X = get_data(dbnative, index_global, cardinality)
  tree = BallTree(X, metric = "chebyshev")
  filename = f"/tmp/${normalize_filename(index_global)}.p"
  pickle.dump(tree, open(filename, "wb"))
  release_iris(connection)
  return tree

def get_data(dbnative, index_global, cardinality):
  X = []
  iter_ = dbnative.iterator(index_global, "model")
  for subscript, value in iter_.items():
    id_ = subscript
    v = []
    for i in range(cardinality):
      v.append(
        dbnative.get(index_global, "model", id_, "rgb", i) / 255
      )
    X.append(v)
  return X

def query(x, k, index_global):
  filename = f"/tmp/${normalize_filename(index_global)}.p"
  tree = pickle.load(open(filename, "rb"))
  x = eval(x)
  x_ = [xi / 255 for xi in x[0]]
  dist, ind = tree.query([x_], k)
  return ind

Lorsqu'une image est recherchée, l'index personnalisé appelle la méthode de requête de l'objet Ball Tree en Python. Vous pouvez également noter l'utilisation de l'API native d'IRIS afin d'accéder aux valeurs RVB globales de l'index pour la construction de l'arborescence Ball Tree.

Pour ordonner les images par similarité, il a été développé une procédure SQL qui traverse une globale stockant les distances précédemment calculées pour chaque image recherchée :

Method DiffRank(pSearch As %Binary, pId As %String) As %Float
{
	Set search = {}.%FromJSON(pSearch)
	If (search.%IsDefined("color_similarity")) {
		Set config = search.%Get("color_similarity")
		Set imagePath = config.%Get("image")
		If (config.%IsDefined("strategy")) {
			Set findStrategy = config.%Get("strategy")
		}
		Set rankGbl = ..Indexer.GetRankLocation()
		Set rank = $GET(@rankGbl@(imagePath, findStrategy, pId))
		Return rank
	}
	Return ""
}

Vous pouvez donc modifier l'instruction SQL pour classer les résultats par similarité :

SELECT ImageFile, dc_multimodel_ImageIndex.Test_idxImageFileDiffRank('{"color_similarity":{"image":"/data/img/test/161074693598711.jpg","first":5,"strategy":"knn"}}', id) AS DiffRank
FROM dc_multimodel_ImageIndex.Test
WHERE ID %FIND search_index(idxImageFile, '{"color_similarity":{"image":"/data/img/test/161074693598711.jpg","first":5,"strategy":"knn"}}')
ORDER BY DiffRank

Conclusion

L'objectif de ce travail était de montrer comment combiner la définition d'index fonctionnels dans COS avec des appels au code Python utilisant leurs étonnantes bibliothèques. De plus, en utilisant cette technique, vous pouvez accéder à des fonctionnalités complexes fournies par les librairies Python dans des instructions SQL, ce qui vous permet d'ajouter de nouvelles fonctionnalités à vos applications.

0
0 93
Article Guillaume Rongier · Mars 16, 2022 9m read

1. Démonstration d'IntegratedML

Ce dépôt est une démonstration d'IntegratedML et d'Embedded Python.

SideBySidePythonAndCos

2. Construction de la démo

Pour construire la démo, il suffit d'exécuter la commande :

docker compose up

2.1. Architecture

Deux conteneurs seront construits : un avec IRIS et un avec un serveur nginx.

containers

L'image IRIS utilisée contient Python embarqué. Après la construction, le conteneur exécutera un serveur wsgi avec l'API Flask.

Nous utilisons le paquet communautaire csvgen pour importer le jeu de données titanic dans iris. Pour le jeu de données noshow, nous utilisons une autre méthode personnalisée (la classmethod Load() de la classe Util.Loader). Pour que le conteneur ait accès aux fichiers csv, nous lions le répertoire local iris/ au répertoire /opt/irisapp/ dans le conteneur.

2.2. Construction du conteneur nginx

Afin de construire notre conteneur nginx, docker utilise une construction en plusieurs étapes. Tout d'abord, il crée un conteneur avec node. Il installe ensuite npm et copie tous nos fichiers dans ce conteneur. Il construit le projet avec la commande ng build, et le fichier de sortie est copié dans un nouveau conteneur qui ne contient que nginx.

Grâce à cette manœuvre, nous obtenons un conteneur très léger qui ne contient pas toutes les bibliothèques et outils nécessaires à la construction de la page Web.

Vous pouvez vérifier les détails de ce multi-build dans le fichier angular/Dockerfile. Nous avons également configuré les paramètres de notre serveur nginx grâce au fichier angular/nginx.default.conf.

3. Exécution de la démo

Il suffit d'aller à l'adresse : http://localhost:8080/ et c'est tout ! Profitez-en !

4. Backend Python

Le back-end est réalisé avec Python Flask. Nous utilisons Python embarqué afin d'appeler les classes iris et d'exécuter des requêtes depuis Python.

4.1. Python embarqué

4.1.1. Configuration du conteneur

Dans le dockerfile, nous devons d'abord expliciter deux variables d'environnement que Python embarqué utilisera :

ENV IRISUSERNAME "SuperUser"
ENV IRISPASSWORD $IRIS_PASSWORD

Avec $IRIS_PASSWORD configuré comme ceci dans le fichier docker-compose :

iris:
  build:
    args:
      - IRIS_PASSWORD=${IRIS_PASSWORD:-SYS}

(Le mot de passe transféré est celui configuré sur votre machine locale ou -s'il n'est pas configuré- sera par défaut « SYS »)

4.1.2. Utiliser Python embarqué

Afin d'utiliser Python embarqué, nous utilisons irispython comme intercepteur de python, et faisons :

import iris

Tout au début du fichier.

Nous serons alors en mesure d'exécuter des méthodes telles que :

flaskExample

Comme vous pouvez le voir, pour OBTENIR un passager avec un ID, nous exécutons simplement une requête et utilisons son jeu de résultats.

Nous pouvons également utiliser directement les objets IRIS :

flaskObjectExample

Ici, nous utilisons une requête SQL pour obtenir tous les ID du tableau, puis nous récupérons chaque passager du tableau avec la méthode %OpenId() de la classe Titanic.Table.Passenger (notez que % étant un caractère illégal en Python, nous utilisons _ à la place).

Grâce à Flask, nous implémentons toutes nos routes et méthodes de cette façon.

4.1.3. Comparaison côte à côte

Sur cette capture d'écran, vous avez une comparaison côte à côte entre une implémentation Flask et une implémentation ObjectScript.

Comme vous pouvez le voir, il y a beaucoup de similitudes.

SideBySidePythonAndCos

4.2. Démarrage du serveur

Pour lancer le serveur, nous utilisons gunicorn avec irispython.

Dans le fichier docker-compose, nous ajoutons la ligne suivante :

iris:
  command: -a "sh /opt/irisapp/flask_server_start.sh"

Cela lancera, après le démarrage du conteneur (grâce à l'indicateur -a), le script suivant :

#!/bin/bash

cd ${FLASK_PATH}

${PYTHON_PATH} /usr/irissys/bin/gunicorn --bind "0.0.0.0:8080" wsgi:app -w 4 2>&1

exit 1

Avec les variables d'environnement définies dans le dockerfile comme suit :

ENV PYTHON_PATH=/usr/irissys/bin/irispython
ENV FLASK_PATH=/opt/irisapp/python/flask

Nous aurons alors accès au back-end Flask via le port local 4040, puisque nous y avons lié le port 8080 du conteneur.

5. IntegratedML

5.1. Explorer les deux ensembles de données

Pour les deux ensembles de données, vous aurez accès à un CRUD complet, vous permettant de modifier à volonté les tableaux enregistrés.

Afin de passer d'un ensemble de données à l'autre, vous pouvez appuyer sur le bouton en haut à droite.

5.2. Managing models

5.2.1. Création d’un modèle

Une fois que vous avez découvert les données, vous pouvez créer un modèle prédisant la valeur que vous souhaitez.

En cliquant dans le menu de navigation latéral Gestionnaire de modèles, dans la liste des modèles, vous aurez accès à la page suivante (ici dans le cas de l'ensemble de données NoShow) :

modelList

Vous pouvez choisir la valeur que vous voulez prédire, le nom de votre modèle, et avec quelles variables vous voulez prédire.

Dans le menu latéral, vous pouvez activer Voir les requêtes SQL ? pour voir comment les modèles sont gérés dans IRIS.

Après avoir créé un modèle, vous devriez voir ceci :

modelCreated

Comme vous pouvez le voir, la création d'un modèle ne prend qu'une seule requête SQL. Les informations que vous avez sont toutes les informations que vous pouvez récupérer d'IRIS.

Dans la colonne actions, vous pouvez supprimer un modèle ou le purger. La purge d'un modèle supprimera tous ses cycles de formation (et leurs cycles de validation), à l'exception du dernier.

5.2.2. Entraînement d'u modèle

Dans l'onglet suivant, vous pourrez entraîner vos modèles.

Vous avez le choix entre 3 fournisseurs. AutoML d'InterSystems, H2O, une solution open-source, et DataRobot, dont vous pouvez avoir un essai gratuit de 14 jours si vous vous enregistrez sur leur site.

Vous pouvez sélectionner le pourcentage de l'ensemble de données que vous souhaitez utiliser pour entraîner votre modèle. Étant donné que l'entraînement de grands ensembles de données peut prendre beaucoup de temps, il est possible, pour les besoins des démonstrations, de prendre un ensemble de données plus petit.

Ici, nous avons entraîné un modèle en utilisant l'ensemble des données Titanic :

modelTrained

Le bouton dans la colonne actions vous permettra de voir le log. Pour l'AutoML, vous verrez ce que l'algorithme a réellement fait : comment il a préparé les données et comment il a choisi le modèle à utiliser.

L'entraînement d'un modèle ne nécessite qu'une seule requête SQL, comme vous pouvez le voir dans la section des messages du menu de navigation latéral.

Gardez à l'esprit que dans ces deux onglets, vous ne verrez que les modèles qui concernent l'ensemble de données que vous utilisez réellement.

5.2.3. Validation d'un modèle

Enfin, vous pouvez valider un modèle dans le dernier onglet. En cliquant sur une exécution de validation, une boîte de dialogue s'ouvre avec les métriques associées à la validation. Là encore, vous pouvez choisir un pourcentage de l'ensemble de données à utiliser pour la validation.

modelValidated

Une fois de plus, il suffit d'une seule requête SQL.

5.2.4. Effectuer des prédictions

Dans le menu Faire des prédictions, dernier onglet, vous pouvez faire des prédictions en utilisant vos modèles nouvellement formés.

Il vous suffit de rechercher un passager / patient et de le sélectionner, de choisir l'un des modèles entraînés et d'appuyer sur prédire.

Dans le cas d'un modèle de classification (comme dans cet exemple, pour prédire la survie), la prédiction sera associée à la probabilité d'être dans la classe prédite.

Dans le cas de Mme Fatima Masselmani, le modèle a correctement prédit qu'elle a survécu, avec une probabilité de 73 %. Juste en dessous de cette prédiction, vous pouvez voir les données utilisées par le modèle :

prédiction

Une fois encore, il faut une requête pour obtenir la prédiction et une pour la probabilité.

6. Utilisation de COS

La démonstration fournit en fait deux API. Nous utilisons l'API Flask avec Python embarqué, mais un service REST dans COS a également été configuré lors de la construction du conteneur.

En appuyant sur le bouton en haut à droite « Passer à l'API COS », vous pourrez utiliser ce service.

Remarquez que rien ne change. Les deux API sont équivalentes et fonctionnent de la même manière.

7. Plus d'explicabilité avec DataRobot

Si vous voulez plus d'explicabilité (plus que ce que le journal peut vous offrir), nous vous suggérons d'utiliser le fournisseur DataRobot.

Pour cela, vous devez vous rendre à l'adresse de votre instance DataRobot, et chercher les outils de développement pour obtenir votre jeton. Lors de l'entraînement de votre modèle, la page Web vous demandera votre jeton.

Une fois l'entraînement commencé, vous pouvez accéder à votre instance DataRobot pour en savoir beaucoup plus sur votre ensemble de données et vos modèles :

DRdata

Ici, nous pouvons voir que les champs sexe et nom de chaque passager sont les valeurs les plus importantes pour prédire la survie. Nous pouvons également voir que le champ tarif contient des valeurs aberrantes.

Une fois les modèles entraînés, vous pouvez avoir accès à de nombreux détails, en voici un aperçu :

DRmodelDetails

8. Conclusion

Grâce à cette démonstration, nous avons pu voir à quel point il était facile de créer, d'entraîner et de valider un modèle ainsi que de prédire des valeurs à l'aide de très peu de requêtes SQL.

Nous avons fait cela en utilisant une API RESTful avec Python Flask, en utilisant Python embarqué, et nous avons fait une comparaison avec une API COS.

Le front-end a été réalisé avec Angular.

9. Remerciements

À Théophile, le stagiaire qui a construit cette belle démo pendant l'été 2021.

0
0 838