#Python

0 Seguidores · 111 Postagens

Python é uma linguagem de programação interpretada de alto nível para programação de uso geral. Criado por Guido van Rossum e lançado pela primeira vez em 1991, o Python tem uma filosofia de design que enfatiza a legibilidade do código, principalmente usando significativos espaços em branco

Site oficial.

InterSystems Python BindingDocumentação.

Artigo Danusa Calixto · Abr. 16, 2024 26m read

1. interoperability-embedded-python

Esta prova de conceito busca mostrar como o framework de interoperabilidade do iris pode ser usado com o embedded python.

1.1. Índice

1.2. Exemplo

from grongier.pex import BusinessOperation,Message

class MyBusinessOperation(BusinessOperation):
    
    def on_init(self):
        #Esse método é chamado quando o componente está se tornando ativo na produção

        self.log_info("[Python] ...MyBusinessOperation:on_init() is called")

        return

    def on_teardown(self):
        #Esse método é chamado quando o componente está se tornando inativo na produção

        self.log_info("[Python] ...MyBusinessOperation:on_teardown() is called")

        return

    def on_message(self, message_input:MyRequest):
        #É chamado do serviço/processo/operação, a mensagem é do tipo MyRequest com a propriedade request_string

        self.log_info("[Python] ...MyBusinessOperation:on_message() is called with message:"+message_input.request_string)

        response = MyResponse("...MyBusinessOperation:on_message() echos")

        return response

@dataclass
class MyRequest(Message):

    request_string:str = None

@dataclass
class MyResponse(Message):

    my_string:str = None

1.3. Registrar um componente

Graças ao método grongier.pex.Utils.register_component():

Inicie um shell do embedded python:

/usr/irissys/bin/irispython

Em seguida, use esse método de classe para adicionar uma classe do python à lista de componentes para interoperabilidade.

from grongier.pex import Utils

Utils.register_component(<ModuleName>,<ClassName>,<PathToPyFile>,<OverWrite>,<NameOfTheComponent>)

Por exemplo:

from grongier.pex import Utils

Utils.register_component("MyCombinedBusinessOperation","MyCombinedBusinessOperation","/irisdev/app/src/python/demo/",1,"PEX.MyCombinedBusinessOperation")

Isso é um truque, e não serve para produção.

2. Demonstração

A demonstração pode ser encontrada em src/python/demo/reddit/ e é composta de:

  • Um arquivo adapter.py com um RedditInboundAdapter que, dado um serviço, buscará postagens recentes do Reddit.

  • Um arquivo bs.py com três services que faz a mesma coisa: ele chamará nosso Process e enviará a postagem do Reddit. Um funciona sozinho, um usa o RedditInBoundAdapter que mencionamos antes e o último usa um inbound adapter do Reddit codificado em ObjectScript.

  • Um arquivo bp.py com um processo FilterPostRoutingRule que analisará nossas postagens do Reddit e as enviará para nossas operations se tiverem determinadas palavras.

  • Um arquivo bo.py com:

    • Duas operações de e-mail que enviarão um e-mail para uma empresa específica dependendo das palavras analisadas antes. Uma funciona sozinha e a outra funciona com um OutBoundAdapter.
    • Duas operações de arquivo que escreverão em um arquivo de texto dependendo das palavras analisadas antes. Uma funciona sozinha e outra funciona com um OutBoundAdapter.

Novo trace json para mensagens nativas do python: json-message-trace

3. Pré-requisitos

Verifique se você tem o git e o Docker desktop instalados.

4. Instalação

4.1. Com Docker

Faça o git pull/clone do repositório em qualquer diretório local

git clone https://github.com/grongierisc/interpeorability-embedded-python

Abra o terminal nesse diretório e execute:

docker-compose build

Execute o contêiner IRIS com seu projeto:

docker-compose up -d

4.2. Sem Docker

Instale o grongier_pex-1.2.4-py3-none-any.whl na sua instância iris local:

/usr/irissys/bin/irispython -m pip install grongier_pex-1.2.4-py3-none-any.whl

Em seguida, carregue as classes ObjectScript:

do $System.OBJ.LoadDir("/opt/irisapp/src","cubk","*.cls",1)

4.3. Com ZPM

zpm "install pex-embbeded-python" 

4.4. Com PyPI

pip3 install iris_pex_embedded_python

Importe as classes ObjectScript, abra um shell do embedded python e execute:

from grongier.pex import Utils
Utils.setup()

4.4.1. Problemas conhecidos

Se o módulo não estiver atualizado, remova a versão antiga:

pip3 uninstall iris_pex_embedded_python

ou remova manualmente a pasta grongier em <iris_installation>/lib/python/

ou force a instalação com o pip:

pip3 install --upgrade iris_pex_embedded_python --target <iris_installation>/lib/python/

5. Como executar a amostra

5.1. Contêineres Docker

Para ter acesso às imagens da InterSystems, é necessário acessar o seguinte URL: http://container.intersystems.com. Depois de se conectar com as credenciais da InterSystems, obtenha a senha para se conectar ao registro. No complemento VSCode do docker, na guia de imagens, ao pressionar "connect registry" e inserir o mesmo url de antes (http://container.intersystems.com) como um registro genérico, será solicitado as credenciais. O login é o habitual, mas a senha é a obtida do site.

Em seguida, será possível criar e compor os contêineres (com os arquivos docker-compose.yml e Dockerfile fornecidos).

5.2. Portal de Gerenciamento e VSCode

Esse repositório está pronto para o VS Code.

Abra a pasta interoperability-embedeed-python clonada localmente no VS Code.

Se solicitado (canto inferior direito), instale as extensões recomendadas.

IMPORTANTE: quando solicitado, reabra a pasta dentro do contêiner, para que você possa usar os componentes do python dentro dele. Na primeira vez que você fizer isso, o contêiner pode levar vários minutos para ficar pronto.

Ao abrir a pasta remota, você permite que o VS Code e quaisquer terminais abertos dentro dele usem os componentes do python dentro do contêiner. Configure-os para usar /usr/irissys/bin/irispython

PythonInterpreter

5.3. Abra a produção

Para abrir a produção, você pode acessar a [produção] (http://localhost:52773/csp/irisapp/EnsPortal.ProductionConfig.zen?PRODUCTION=PEX.Production).
Você também pode clicar na parte inferior no botão 127.0.0.1:52773[IRISAPP], selecionar Open Management Portal(Abrir Portal de Gerenciamento), clicar nos menus [Interoperability] e [Configure] e, depois, em [productions] e [Go].

A produção já tem uma amostra de código.

Aqui podemos ver a produção e nossos serviços e operações de python puro: interop-screenshot


Novo trace json para mensagens nativas do python: json-message-trace

6. O que está dentro do repositório

6.1. Dockerfile

Um dockerfile que instala algumas dependências do python (pip, venv) e sudo no contêiner para conveniência. Em seguida, ele cria o diretório dev e copia nele esse repositório git.

Ele inicia o IRIS e ativa %Service_CallIn para o Shell do Python. Use o docker-compose.yml relacionado para configurar facilmente parâmetros adicionais, como número da porta e onde você mapeia chaves e hospeda pastas.

Esse dockerfile termina com a instalação dos requisitos para os módulos do python.

Use o arquivo .env/ para ajustar o dockerfile usado em docker-compose.

6.2. .vscode/settings.json

Arquivo de configurações para você programar imediatamente no VSCode com o [plugin ObjectScript do VSCode] (https://marketplace.visualstudio.com/items?itemName=daimor.vscode-objectscript)

6.3. .vscode/launch.json

Arquivo de configurações se você quiser depurar com o ObjectScript do VSCode

Leia sobre todos os arquivos neste artigo

6.4. .vscode/extensions.json

Arquivo de recomendação para adicionar extensões se você quiser executar com o VSCode no contêiner.

Mais informações aqui

Archiecture

Isso é muito útil para trabalhar com o embedded python.

6.5. src folder

src
├── Grongier
│   └── PEX // Classes ObjectScript que envolvem código em python
│       ├── BusinessOperation.cls
│       ├── BusinessProcess.cls
│       ├── BusinessService.cls
│       ├── Common.cls
│       ├── Director.cls
│       ├── InboundAdapter.cls
│       ├── Message.cls
│       ├── OutboundAdapter.cls
│       ├── Python.cls
│       ├── Test.cls
│       └── _utils.cls
├── PEX // Alguns exemplos de classes envolvidas
│   └── Production.cls
└── python
    ├── demo // código em python real para executar essa demonstração
    |   `-- reddit
    |       |-- adapter.py
    |       |-- bo.py
    |       |-- bp.py
    |       |-- bs.py
    |       |-- message.py
    |       `-- obj.py
    ├── dist // Wheel usado para implementar componentes de interoperabilidade do python
    │   └── grongier_pex-1.2.4-py3-none-any.whl
    ├── grongier
    │   └── pex // Classes helper para implementar componentes de interoperabilidade
    │       ├── _business_host.py
    │       ├── _business_operation.py
    │       ├── _business_process.py
    │       ├── _business_service.py
    │       ├── _common.py
    │       ├── _director.py
    │       ├── _inbound_adapter.py
    │       ├── _message.py
    │       ├── _outbound_adapter.py
    │       ├── __init__.py
    │       └── _utils.py
    └── setup.py // configuração para criar o wheel

7. Como funciona

7.1. Arquivo __init__.py

Esse arquivo nos permite criar as classes para importar no código.
Ele obtém as classes de vários arquivos vistos antes e as transforma em classes callable. Assim, quando você quiser criar uma operação de negócios, por exemplo, pode só executar:

from grongier.pex import BusinessOperation

7.2. Classe common

A classe common não deve ser chamada pelo usuário. Ela define quase todas as outras classes.
Essa classe define:

on_init: esse método é chamado quando o componente é inicializado.
Use o método on_init() para inicializar qualquer estrutura necessária para o componente.

on_tear_down: é chamado antes de encerrar o componente.
Use para liberar qualquer estrutura.

on_connected: esse método é chamado quando o componente é conectado ou reconectado após ser desconectado.
Use o método on_connected() para inicializar qualquer estrutura necessária para o componente.

log_info: grava uma entrada de log do tipo "info". As entradas de log podem ser visualizadas no portal de gerenciamento.

log_alert: grava uma entrada de log do tipo "alert". As entradas de log podem ser visualizadas no portal de gerenciamento.

log_warning: grava uma entrada de log do tipo "warning". As entradas de log podem ser visualizadas no portal de gerenciamento.

log_error: grava uma entrada de log do tipo "error". As entradas de log podem ser visualizadas no portal de gerenciamento.

7.3. Classe business_host

A classe de host de negócios não deve ser chamada pelo usuário. É a classe base para todas as classes de negócios.
Essa classe define:

send_request_sync: envia a mensagem específica à operação ou ao processo de negócios alvo de maneira síncrona.
Parâmetros:

  • target: uma string que especifica o nome da operação ou do processo de negócios a receber a solicitação.
    O alvo é o nome do componente conforme especificado na propriedade Item Name na definição da produção, e não o nome da classe do componente.
  • request: especifica a mensagem a enviar ao alvo. A solicitação é uma instância de uma classe que é IRISObject ou subclasse da classe Message.
    Se o alvo é um componente ObjectScript integrado, você deve usar a classe IRISObject. A classe IRISObject permite que o framework PEX converta a mensagem para uma classe compatível com o alvo.
  • timeout: um inteiro opcional que especifica o número de segundos de espera antes de tratar a solicitação de envio como uma falha. O valor padrão é -1, que significa "esperar para sempre".
    description: um parâmetro de string opcional que define uma propriedade da descrição no cabeçalho da mensagem. O padrão é None.

Retorna: o objeto de resposta do alvo.

Gera: TypeError: se a solicitação não é do tipo Message ou IRISObject.


send_request_async: envia a mensagem específica ao processo ou operação de negócios alvo de maneira assíncrona. Parâmetros:

  • target: uma string que especifica o nome da operação ou do processo de negócios a receber a solicitação.
    O alvo é o nome do componente conforme especificado na propriedade Item Name na definição da produção, e não o nome da classe do componente.
  • request: especifica a mensagem a enviar ao alvo. A solicitação é uma instância de IRISObject ou de uma subclasse de Message.
    Se o alvo é um componente ObjectScript integrado, você deve usar a classe IRISObject. A classe IRISObject permite que o framework PEX converta a mensagem para uma classe compatível com o alvo.
  • description: um parâmetro de string opcional que define uma propriedade da descrição no cabeçalho da mensagem. O padrão é None.

Gera: TypeError: se a solicitação não é do tipo Message ou IRISObject.


get_adapter_type: nome do adaptador registrado.

7.4. Classe inbound_adapter

Inbound Adapter no Python é uma subclasse de grongier.pex.InboundAdapter no Python, que herda de todas as funções da classe common.
Essa classe é responsável por receber os dados do sistema externo, validar os dados e enviá-los ao serviço de negócios ao chamar o método process_input de BusinessHost. Essa classe define:

on_task: é chamado pelo framework de produção a intervalos determinados pela propriedade CallInterval do serviço de negócios.
A mensagem pode ter qualquer estrutura que esteja de acordo com o inbound adapter e serviço de negócios.

Exemplo de um inbound adapter (localizado no arquivo src/python/demo/reddit/adapter.py):

from grongier.pex import InboundAdapter
import requests
import iris
import json

class RedditInboundAdapter(InboundAdapter):
    """
    Esse adaptador usa solicitações para buscar postagens self.limit como dados da API Reddit
    antes de chamar o process_input para cada postagem.
    """
    def on_init(self):
        
        if not hasattr(self,'feed'):
            self.feed = "/new/"
        
        if self.limit is None:
            raise TypeError('no Limit field')
        
        self.last_post_name = ""
        
        return 1

    def on_task(self):
        self.log_info(f"LIMIT:{self.limit}")
        if self.feed == "" :
            return 1
        
        tSC = 1
        # Solicitação HTTP
        try:
            server = "https://www.reddit.com"
            request_string = self.feed+".json?before="+self.last_post_name+"&limit="+self.limit
            self.log_info(server+request_string)
            response = requests.get(server+request_string)
            response.raise_for_status()

            data = response.json()
            updateLast = 0

            for key, value in enumerate(data['data']['children']):
                if value['data']['selftext']=="":
                    continue
                post = iris.cls('dc.Reddit.Post')._New()
                post._JSONImport(json.dumps(value['data']))
                post.OriginalJSON = json.dumps(value)
                if not updateLast:
                    self.LastPostName = value['data']['name']
                    updateLast = 1
                response = self.BusinessHost.ProcessInput(post)
        except requests.exceptions.HTTPError as err:
            if err.response.status_code == 429:
                self.log_warning(err.__str__())
            else:
                raise err
        except Exception as err: 
            self.log_error(err.__str__())
            raise err

        return tSC

7.5. Classe outbound_adapter

Outbound Adapter no Python é uma subclasse de grongier.pex.OutboundAdapter no Python, que herda de todas as funções da classe common.
Essa classe é responsável por enviar os dados para o sistema externo.

O Outbound Adapter possibilita à operação ter uma noção dos batimentos cardíacos. Para ativar essa opção, o parâmetro CallInterval do adaptador precisa ser estritamente maior que 0.

image

Exemplo de um outbound adapter (localizado no arquivo src/python/demo/reddit/adapter.py):

class TestHeartBeat(OutboundAdapter):

    def on_keepalive(self):
        self.log_info('beep')

    def on_task(self):
        self.log_info('on_task')

7.6. Classe business_service

Essa classe é responsável por receber os dados do sistema externo e enviá-los aos processos ou operações de negócios na produção.
O serviço de negócios pode usar um adaptador para acessar o sistema externo, que é especificado sobrescrevendo o método get_adaptar_type.
Há três maneiras de implementar um serviço de negócios:

  • Serviço de negócios de sondagem com um adaptador - O framework de produção chama a intervalos regulares o método OnTask() do adaptador, que envia os dados recebidos ao método ProcessInput() do serviço de negócios, que, por sua vez, chama o método OnProcessInput com seu código.

  • Serviço de negócios de sondagem que usa o adaptador padrão - Nesse caso, o framework chama o método OnTask do adaptador padrão sem dados. Em seguida, o método OnProcessInput() realiza o papel do adaptador e é responsável por acessar o sistema externo e receber os dados.

  • Serviço de negócios sem sondagem - O framework de produção não inicia o serviço de negócios. Em vez disso, o código personalizado em um processo longo ou um que é inicializado a intervalos regulares inicia o serviço de negócios ao chamar o método Director.CreateBusinessService().

O serviço de negócios no Python é uma subclasse de grongier.pex.BusinessService no Python, que herda de todas as funções do host de negócios.
Essa classe define:

on_process_input: recebe a mensagem do inbound adapter pelo método ProcessInput e é responsável por encaminhá-la aos processos ou operações de negócios alvo.
Se o serviço de negócios não especifica um adaptador, o adaptador padrão chama esse método sem mensagem e o serviço de negócios é responsável por receber os dados do sistema externo e validá-los. Parâmetros:

  • message_input: uma instância de IRISObject ou subclasse de Message com os dados que o inbound adapter transmite.
    A mensagem pode ter qualquer estrutura que esteja de acordo com o inbound adapter e serviço de negócios.


Exemplo de um serviço de negócios (localizado no arquivo src/python/demo/reddit/bs.py):

from grongier.pex import BusinessService

import iris

from message import PostMessage
from obj import PostClass

class RedditServiceWithPexAdapter(BusinessService):
    """
    This service use our python Python.RedditInboundAdapter to receive post
    from reddit and call the FilterPostRoutingRule process.
    """
    def get_adapter_type():
        """
        Name of the registred Adapter
        """
        return "Python.RedditInboundAdapter"

    def on_process_input(self, message_input):
        msg = iris.cls("dc.Demo.PostMessage")._New()
        msg.Post = message_input
        return self.send_request_sync(self.target,msg)

    def on_init(self):
        
        if not hasattr(self,'target'):
            self.target = "Python.FilterPostRoutingRule"
        
        return

7.7. Classe business_process

Geralmente, contém a maior parte da lógica em uma produção.
Um processo de negócios pode receber mensagens de um serviço, processo ou operação de negócios.
Ele pode modificar a mensagem, convertê-la para um formato diferente ou roteá-la com base no conteúdo dela.
O processo de negócios pode rotear uma mensagem para uma operação ou outro processo de negócios.
Os processos de negócios no Python são uma subclasse de grongier.pex.BusinessProcess no Python, que herda de todas as funções do host de negócios.
Essa classe define:

on_request: processa as solicitações enviadas ao processo de negócios. Uma produção chama esse método quando uma solicitação inicial de um processo de negócios específico chega na fila apropriada e é atribuída a um trabalho em que deve ser executada.
Parâmetros:

  • request: uma instância de IRISObject ou subclasse de Message que contém a mensagem de solicitação enviada ao processo de negócios.

Retorna: Uma instância de IRISObject ou subclasse de Message que contém a mensagem de resposta que esse processo de negócios pode retornar ao componente de produção que enviou a mensagem inicial.


on_response: processa as respostas enviadas ao processo de negócios em resposta às mensagens ele que enviou ao alvo.
Uma produção chama esse método sempre que uma resposta a um processo de negócios específico chega na fila apropriada e é atribuída a um trabalho em que deve ser executada.
Geralmente, isso é uma resposta a uma solicitação assíncrona feita pelo processo de negócios onde o parâmetro responseRequired tem um valor "true".
Parâmetros:

  • request: uma instância de IRISObject ou subclasse de Message que contém a mensagem de solicitação inicial enviada ao processo de negócios.
  • response: uma instância de IRISObject ou subclasse de Message que contém a mensagem de resposta que esse processo de negócios pode retornar ao componente de produção que enviou a mensagem inicial.
  • callRequest: uma instância de IRISObject ou subclasse de Message que contém a solicitação que o processo de negócios enviou ao seu alvo.
  • callResponse: uma instância de IRISObject ou subclasse de Message que contém a resposta de entrada.
  • completionKey: uma string que contém a completionKey especificada no parâmetro completionKey do método SendAsync() de saída.

Retorna: Uma instância de IRISObject ou subclasse de Message que contém a mensagem de resposta que esse processo de negócios pode retornar ao componente de produção que enviou a mensagem inicial.


on_complete: é chamado depois que o processo de negócios recebeu e processou todas as respostas às solicitações que enviou aos alvos.
Parâmetros:

  • request: uma instância de IRISObject ou subclasse de Message que contém a mensagem de solicitação inicial enviada ao processo de negócios.
  • response: uma instância de IRISObject ou subclasse de Message que contém a mensagem de resposta que esse processo de negócios pode retornar ao componente de produção que enviou a mensagem inicial.

Retorna: Uma instância de IRISObject ou subclasse de Message que contém a mensagem de resposta que esse processo de negócios pode retornar ao componente de produção que enviou a mensagem inicial.


Exemplo de um processo de negócios (localizado no arquivo src/python/demo/reddit/bp.py):

from grongier.pex import BusinessProcess

from message import PostMessage
from obj import PostClass

class FilterPostRoutingRule(BusinessProcess):
    """
    This process receive a PostMessage containing a reddit post.
    It then understand if the post is about a dog or a cat or nothing and
    fill the right infomation inside the PostMessage before sending it to
    the FileOperation operation.
    """
    def on_init(self):
        
        if not hasattr(self,'target'):
            self.target = "Python.FileOperation"
        
        return

    def on_request(self, request):

        if 'dog'.upper() in request.post.selftext.upper():
            request.to_email_address = 'dog@company.com'
            request.found = 'Dog'
        if 'cat'.upper() in request.post.selftext.upper():
            request.to_email_address = 'cat@company.com'
            request.found = 'Cat'

        if request.found is not None:
            return self.send_request_sync(self.target,request)
        else:
            return

7.8. Classe business_operation

Essa classe é responsável por enviar os dados a um sistema externo ou local como um banco de dados do iris.
A operação de negócios pode usar opcionalmente um adaptador para processar a mensagem de saída que é especificada sobrescrevendo o método get_adapter_type.
Se a operação de negócios tiver um adaptador, ela usa o adaptador para enviar a mensagem ao sistema externo.
O adaptador pode ser um adaptador PEX, ObjectScript ou python.
A operação de negócios no Python é uma subclasse de grongier.pex.BusinessOperation no Python, que herda de todas as funções do host de negócios.

7.8.1. Sistema de despacho

Em uma operação de negócios, é possível criar qualquer número de funções semelhante ao método on_message que aceitam como argumento uma solicitação tipada como esse my_special_message_method(self,request: MySpecialMessage).

O sistema de despacho analisará automaticamente qualquer solicitação que chegar à operação e despachará as solicitações dependendo do tipo delas. Se o tipo de solicitação não for reconhecido ou não estiver especificado em qualquer função semelhante a on_message, o sistema de despacho a enviará para a função on_message.

7.8.2. Métodos

Essa classe define:

on_message: é chamado quando a operação de negócios recebe uma mensagem de outro componente da produção que não pode ser despachada a outra função.
Geralmente, a operação envia a mensagem ao sistema externo ou a encaminha para um processo ou outra operação de negócios. Se a operação tiver um adaptador, ela usa o método Adapter.invoke() para chamar o método no adaptador que envia a mensagem ao sistema externo. Se a operação estiver encaminhando a mensagem a outro componente da produção, ela usa o método SendRequestAsync() ou SendRequestSync().
Parâmetros:

  • request: uma instância de IRISObject ou subclasse de Message que contém a mensagem de entrada para a operação de negócios.

Retorna: O objeto de resposta

Exemplo de uma operação de negócios (localizada no arquivo src/python/demo/reddit/bo.py):

from grongier.pex import BusinessOperation

from message import MyRequest,MyMessage

import iris

import os
import datetime
import smtplib
from email.mime.text import MIMEText

class EmailOperation(BusinessOperation):
    """
    This operation receive a PostMessage and send an email with all the
    important information to the concerned company ( dog or cat company )
    """

    def my_message(self,request:MyMessage):
        sender = 'admin@example.com'
        receivers = 'toto@example.com'
        port = 1025
        msg = MIMEText(request.toto)

        msg['Subject'] = 'MyMessage'
        msg['From'] = sender
        msg['To'] = receivers

        with smtplib.SMTP('localhost', port) as server:
            server.sendmail(sender, receivers, msg.as_string())
            print("Successfully sent email")

    def on_message(self, request):

        sender = 'admin@example.com'
        receivers = [ request.to_email_address ]


        port = 1025
        msg = MIMEText('This is test mail')

        msg['Subject'] = request.found+" found"
        msg['From'] = 'admin@example.com'
        msg['To'] = request.to_email_address

        with smtplib.SMTP('localhost', port) as server:
            
            # server.login('username', 'password')
            server.sendmail(sender, receivers, msg.as_string())
            print("Successfully sent email")

Se essa operação for chamada usando uma mensagem MyRequest, a função my_message será chamada graças ao dispatcher. Caso contrário, a função on_message será chamada.

7.9. Classe director

A classe Director é usada para serviços de negócios sem sondagem, ou seja, serviços de negócios que não são chamados automaticamente pelo framework da produção (pelo inbound adapter) no intervalo da chamada.
Em vez disso, esses serviços de negócios são criados por um aplicativo personalizado ao chamar o método Director.create_business_service().
Essa classe define:

create_business_service: esse método inicia o serviço de negócios especificado.
Parâmetros:

  • connection: um objeto IRISConnection que especifica a conexão a uma instância IRIS para Java.
  • target: uma string que especifica o nome do serviço de negócios na definição da produção.

Retorna: um objeto que contém uma instância de IRISBusinessService

start_production: esse método inicia a produção.
Parâmetros:

  • production_name: uma string que especifica o nome da produção a iniciar.

stop_production: esse método interrompe a produção.
Parâmetros:

  • production_name: uma string que especifica o nome da produção a interromper.

restart_production: esse método reinicia a produção.
Parâmetros:

  • production_name: uma string que especifica o nome da produção a reiniciar.

list_productions: esse método retorna um dicionário de nomes das produções que estão sendo executadas no momento.

7.10. Os objects

Vamos usar dataclass para armazenar informações em nossas mensagens em um arquivo obj.py.

Exemplo de um objeto (localizado no arquivo src/python/demo/reddit/obj.py):

from dataclasses import dataclass

@dataclass
class PostClass:
    title: str
    selftext : str
    author: str
    url: str
    created_utc: float = None
    original_json: str = None

7.11. As messages

As mensagens conterão um ou mais objetos, localizados no arquivo obj.py.
Mensagens, solicitações e respostas herdam da classe grongier.pex.Message.

Essas mensagens nos permitem transferir informações entre qualquer serviço/processo/operação de negócios.

Exemplo de uma mensagem (localizada no arquivo src/python/demo/reddit/message.py):

from grongier.pex import Message

from dataclasses import dataclass

from obj import PostClass

@dataclass
class PostMessage(Message):
    post:PostClass = None
    to_email_address:str = None
    found:str = None

WIP Vale destacar que é necessário usar tipos ao definir um objeto ou uma mensagem.

7.12. Como registrar um componente

Você pode registrar um componente no iris de várias formas:

  • Somente um componente com register_component
  • Todos os componentes em um arquivo com register_file
  • Todos os componentes em um arquivo com register_folder

7.12.1. register_component

Inicie um shell do embedded python:

/usr/irissys/bin/irispython

Em seguida, use esse método de classe para adicionar um novo arquivo py à lista de componentes para interoperabilidade.

from grongier.pex import Utils
Utils.register_component(<ModuleName>,<ClassName>,<PathToPyFile>,<OverWrite>,<NameOfTheComponent>)

Por exemplo:

from grongier.pex import Utils
Utils.register_component("MyCombinedBusinessOperation","MyCombinedBusinessOperation","/irisdev/app/src/python/demo/",1,"PEX.MyCombinedBusinessOperation")

7.12.2. register_file

Inicie um shell do embedded python:

/usr/irissys/bin/irispython

Em seguida, use esse método de classe para adicionar um novo arquivo py à lista de componentes para interoperabilidade.

from grongier.pex import Utils
Utils.register_file(<File>,<OverWrite>,<PackageName>)

Por exemplo:

from grongier.pex import Utils
Utils.register_file("/irisdev/app/src/python/demo/bo.py",1,"PEX")

7.12.3. register_folder

Inicie um shell do embedded python:

/usr/irissys/bin/irispython

Em seguida, use esse método de classe para adicionar um novo arquivo py à lista de componentes para interoperabilidade.

from grongier.pex import Utils
Utils.register_folder(<Path>,<OverWrite>,<PackageName>)

Por exemplo:

from grongier.pex import Utils
Utils.register_folder("/irisdev/app/src/python/demo/",1,"PEX")

7.12.4. migrate

Inicie um shell do embedded python:

/usr/irissys/bin/irispython

Em seguida, use esse método estático para migrar o arquivo de configurações para o framework iris.

from grongier.pex import Utils
Utils.migrate()

7.12.4.1. Arquivo setting.py

Esse arquivo é usado para armazenar as configurações dos componentes de interoperabilidade.

Ele tem duas seções:

  • CLASSES: essa seção é usada para armazenar as classes dos componentes de interoperabilidade.
  • PRODUCTIONS: essa seção é usada para armazenar as classes dos componentes de interoperabilidade.

Por exemplo:

import bp
from bo import *
from bs import *

CLASSES = {
    'Python.RedditService': RedditService,
    'Python.FilterPostRoutingRule': bp.FilterPostRoutingRule,
    'Python.FileOperation': FileOperation,
    'Python.FileOperationWithIrisAdapter': FileOperationWithIrisAdapter,
}

PRODUCTIONS = [
    {
        'dc.Python.Production': {
        "@Name": "dc.Demo.Production",
        "@TestingEnabled": "true",
        "@LogGeneralTraceEvents": "false",
        "Description": "",
        "ActorPoolSize": "2",
        "Item": [
            {
                "@Name": "Python.FileOperation",
                "@Category": "",
                "@ClassName": "Python.FileOperation",
                "@PoolSize": "1",
                "@Enabled": "true",
                "@Foreground": "false",
                "@Comment": "",
                "@LogTraceEvents": "true",
                "@Schedule": "",
                "Setting": {
                    "@Target": "Host",
                    "@Name": "%settings",
                    "#text": "path=/tmp"
                }
            },
            {
                "@Name": "Python.RedditService",
                "@Category": "",
                "@ClassName": "Python.RedditService",
                "@PoolSize": "1",
                "@Enabled": "true",
                "@Foreground": "false",
                "@Comment": "",
                "@LogTraceEvents": "false",
                "@Schedule": "",
                "Setting": [
                    {
                        "@Target": "Host",
                        "@Name": "%settings",
                        "#text": "limit=10\nother<10"
                    }
                ]
            },
            {
                "@Name": "Python.FilterPostRoutingRule",
                "@Category": "",
                "@ClassName": "Python.FilterPostRoutingRule",
                "@PoolSize": "1",
                "@Enabled": "true",
                "@Foreground": "false",
                "@Comment": "",
                "@LogTraceEvents": "false",
                "@Schedule": ""
            }
        ]
    }
    }
]
7.12.4.1.1. Seção CLASSES

Essa seção é usada para armazenar as produções dos componentes de interoperabilidade.

Ela visa ajudar a registrar os componentes.

Esse dicionário tem a seguinte estrutura:

  • Chave: nome do componente
  • Valor:
    • Classe do componente (você precisa importar antes)
    • Módulo do componente (você precisa importar antes)
    • Outro dicionário com a seguinte estrutura:
      • module : nome do módulo do componente (opcional)
      • class : nome da classe do componente (opcional)
      • path : caminho do componente (obrigatório)

Por exemplo:

Quando o valor é uma classe ou módulo:

import bo
import bp
from bs import RedditService

CLASSES = {
    'Python.RedditService': RedditService,
    'Python.FilterPostRoutingRule': bp.FilterPostRoutingRule,
    'Python.FileOperation': bo,
}

Quando o valor é um dicionário:

CLASSES = {
    'Python.RedditService': {
        'module': 'bs',
        'class': 'RedditService',
        'path': '/irisdev/app/src/python/demo/'
    },
    'Python.Module': {
        'module': 'bp',
        'path': '/irisdev/app/src/python/demo/'
    },
    'Python.Package': {
        'path': '/irisdev/app/src/python/demo/'
    },
}
7.12.4.1.2. Seção de produções

Essa seção é usada para armazenar as produções dos componentes de interoperabilidade.

Ela visa ajudar a registrar uma produção.

Essa lista tem a seguinte estrutura:

  • Uma lista do dicionário com a seguinte estrutura:
    • dc.Python.Production: nome da produção
      • @Name: nome da produção
      • @TestingEnabled: enabled de teste da produção
      • @LogGeneralTraceEvents: eventos de rastreamento gerais do log da produção
      • Description: descrição da produção
      • ActorPoolSize: tamanho do pool de atores da produção
      • Item: lista dos itens da produção
        • @Name: nome do item
        • @Category: categoria do item
        • @ClassName: nome da classe do item
        • @PoolSize: tamanho do pool do item
        • @Enabled: enabled do item
        • @Foreground: foreground do item
        • @Comment: comentário do item
        • @LogTraceEvents: eventos de rastreamento do log do item
        • @Schedule: programação do item
        • Setting: lista de configurações do item
          • @Target: alvo da configuração
          • @Name: nome da configuração
          • #text: valor da configuração

A estrutura mínima de uma produção é:

PRODUCTIONS = [
        {
            'UnitTest.Production': {
                "Item": [
                    {
                        "@Name": "Python.FileOperation",
                        "@ClassName": "Python.FileOperation",
                    },
                    {
                        "@Name": "Python.EmailOperation",
                        "@ClassName": "UnitTest.Package.EmailOperation"
                    }
                ]
            }
        } 
    ]

Você também pode definir em @ClassName um item da seção CLASSES.

Por exemplo:

from bo import FileOperation
PRODUCTIONS = [
        {
            'UnitTest.Production': {
                "Item": [
                    {
                        "@Name": "Python.FileOperation",
                        "@ClassName": FileOperation,
                    }
                ]
            }
        } 
    ]

Como a produção é um dicionário, você pode adicionar no valor do dicionário de produção uma variável de ambiente.

Por exemplo:

import os

PRODUCTIONS = [
        {
            'UnitTest.Production': {
                "Item": [
                    {
                        "@Name": "Python.FileOperation",
                        "@ClassName": "Python.FileOperation",
                        "Setting": {
                            "@Target": "Host",
                            "@Name": "%settings",
                            "#text": os.environ['SETTINGS']
                        }
                    }
                ]
            }
        } 
    ]

7.13. Uso direto de Grongier.PEX

Se você não quiser usar o utilitário register_component, é possível adicionar um componente Grongier.PEX.BusinessService diretamente no portal de gerenciamento e configurar as propriedades:

  • %module:
    • Nome do módulo do seu código em python
  • %classname:
    • Nome da classe do seu componente
  • %classpaths:
    • Caminho onde está seu componente.
      • Pode ser um ou mais caminhos de classe (separados pelo caractere '|') necessários além de PYTHON_PATH

Por exemplo: component-config

8. Linha de comando

Desde a versão 2.3.1, você pode usar a linha de comando para registrar seus componentes e produções.

Para usá-la, você precisa utilizar o seguinte comando:

iop 

saída:

usage: python3 -m grongier.pex [-h] [-d DEFAULT] [-l] [-s START] [-k] [-S] [-r] [-M MIGRATE] [-e EXPORT] [-x] [-v] [-L]
optional arguments:
  -h, --help            display help and default production name
  -d DEFAULT, --default DEFAULT
                        set the default production
  -l, --lists           list productions
  -s START, --start START
                        start a production
  -k, --kill            kill a production (force stop)
  -S, --stop            stop a production
  -r, --restart         restart a production
  -M MIGRATE, --migrate MIGRATE
                        migrate production and classes with settings file
  -e EXPORT, --export EXPORT
                        export a production
  -x, --status          status a production
  -v, --version         display version
  -L, --log             display log

default production: PEX.Production

8.1. help

O comando help exibe a ajuda e o nome da produção padrão.

iop -h

saída:

usage: python3 -m grongier.pex [-h] [-d DEFAULT] [-l] [-s START] [-k] [-S] [-r] [-M MIGRATE] [-e EXPORT] [-x] [-v] [-L]
...
default production: PEX.Production

8.2. default

O comando default define a produção padrão.

Sem argumento, ele exibe a produção padrão.

iop -d

saída:

default production: PEX.Production

Com argumento, ele define a produção padrão.

iop -d PEX.Production

8.3. lists

O comando lists lista produções.

iop -l

saída:

{
    "PEX.Production": {
        "Status": "Stopped",
        "LastStartTime": "2023-05-31 11:13:51.000",
        "LastStopTime": "2023-05-31 11:13:54.153",
        "AutoStart": 0
    }
}

8.4. start

O comando start inicia uma produção.

Para sair do comando, pressione CTRL+C.

iop -s PEX.Production

saída:

2021-08-30 15:13:51.000 [PEX.Production] INFO: Starting production
2021-08-30 15:13:51.000 [PEX.Production] INFO: Starting item Python.FileOperation
2021-08-30 15:13:51.000 [PEX.Production] INFO: Starting item Python.EmailOperation
...

8.5. kill

O comando kill encerra uma produção à força.

O comando kill é igual ao comando stop, mas força o encerramento.

O comando kill não aceita argumentos, porque só pode ser executada uma produção.

iop -k 

8.6. stop

O comando stop interrompe uma produção.

O comando stop não aceita argumentos, porque só pode ser executada uma produção.

iop -S 

8.7. restart

O comando restart reinicia uma produção.

O comando restart não aceita argumentos, porque só pode ser executada uma produção.

iop -r 

8.8. migrate

O comando migrate migra uma produção e classes com o arquivo de configurações.

O comando migate precisa receber o caminho absoluto do arquivo de configurações.

O arquivo de configurações precisa estar na mesma pasta que o código em python.

iop -M /tmp/settings.py

8.9. export

O comando export exporta uma produção.

Se nenhum argumento for fornecido, o comando export exporta a produção padrão.

iop -e

Se um argumento for fornecido, o comando export exporta a produção indicada no argumento.

iop -e PEX.Production

saída:

{
    "Production": {
        "@Name": "PEX.Production",
        "@TestingEnabled": "true",
        "@LogGeneralTraceEvents": "false",
        "Description": "",
        "ActorPoolSize": "2",
        "Item": [
            {
                "@Name": "Python.FileOperation",
                "@Category": "",
                "@ClassName": "Python.FileOperation",
                "@PoolSize": "1",
                "@Enabled": "true",
                "@Foreground": "false",
                "@Comment": "",
                "@LogTraceEvents": "true",
                "@Schedule": "",
                "Setting": [
                    {
                        "@Target": "Adapter",
                        "@Name": "Charset",
                        "#text": "utf-8"
                    },
                    {
                        "@Target": "Adapter",
                        "@Name": "FilePath",
                        "#text": "/irisdev/app/output/"
                    },
                    {
                        "@Target": "Host",
                        "@Name": "%settings",
                        "#text": "path=/irisdev/app/output/"
                    }
                ]
            }
        ]
    }
}

8.10. status

O comando status dá o status de uma produção.

O comando status não aceita argumentos, porque só pode ser executada uma produção.

iop -x 

saída:

{
    "Production": "PEX.Production",
    "Status": "stopped"
}

O status pode ser:

  • stopped (interrompido)
  • running (em execução)
  • suspended (suspenso)
  • troubled (com problema)

8.11. version

O comando version exibe a versão.

iop -v

saída:

2.3.0

8.12. log

O comando log exibe o log.

Para sair do comando, pressione CTRL+C.

iop -L

saída:

2021-08-30 15:13:51.000 [PEX.Production] INFO: Starting production
2021-08-30 15:13:51.000 [PEX.Production] INFO: Starting item Python.FileOperation
2021-08-30 15:13:51.000 [PEX.Production] INFO: Starting item Python.EmailOperation
...

9. Créditos

A maior parte do código foi retirada do PEX para Python, de Mo Cheng e Summer Gerry.

Funciona somente a partir do IRIS 2021.2

0
1 72
Artigo Danusa Calixto · Mar. 13, 2024 3m read
   _________ ___ ____  
  |__  /  _ \_ _|  _ \ 
    / /| |_) | || |_) |
   / /_|  __/| ||  __/ 
  /____|_|  |___|_|    

Desde a versão 2021.1, o InterSystems IRIS começou a enviar com um ambiente de execução do python no kernel do motor. No entanto, não havia como instalar pacotes de dentro da instância. A principal vantagem do python é seu enorme ecossistema de pacotes. Com isso em mente, apresento meu projeto pessoal zpip, um wrapper de pip que pode ser chamado a partir do terminal iris.

O que é o zpip?

zpip é um wrapper para o pip do python que permite aos desenvolvedores adicionar pacotes rapidamente a uma instância pelo terminal do InterSystems IRIS.

Recursos

  • wrapper de pip do python para o InterSystems IRIS
  • Instalar/desinstalar pacotes do python
  • A instalação adiciona a palavra-chave zpip ao idioma

Instalando zpip

%SYS> zpm "install zpip"

Lista de tarefas

  • API chamável com status retornados

Usando zpip

Todos os comandos pip* são compatíveis. Porém, qualquer comando interativo exigirá que você use a versão não interativa do comando. Por exemplo, para desinstalar um pacote, você precisará usar o -y no comando para confirmar o processo.

Instalar pacotes do python com zpip

// Instale vários pacotes
// bibliotecas beautiful soup e requests
%SYS> zpip "install requests bs4"

... em ação:

%SYS>zpip "install emoji"

Processing /home/irisowner/.cache/pip/wheels/ae/80/43/3b56e58669d65ea9ebf38b9574074ca248143b61f45e114a6b/emoji-2.1.0-py3-none-any.whl
Installing collected packages: emoji
Successfully installed emoji-2.1.0

%SYS>

Especifique um diretório de instalação diferente:

// Instale em algum outro destino de pacote do python
$SYS> zpip "install --target '/durable/iconfig/lib/python' emoji"

Desinstalar um pacote do python

// Requer o -y!
%SYS>zpip "uninstall -y emoji"
Found existing installation: emoji 2.1.0
Uninstalling emoji-2.1.0:
  Successfully uninstalled emoji-2.1.0

Outros comandos úteis do pip

listar pacotes

// Listar pacotes
%SYS> zpip "list"
Package                      Version    
---------------------------- -----------
absl-py                      1.1.0      
argon2-cffi                  21.3.0     
argon2-cffi-bindings         21.2.0     
asttokens                    2.0.5      
astunparse                   1.6.3      
attrs                        21.4.0     
backcall                     0.2.0      
beautifulsoup4               4.11.1     
bleach                       5.0.0      
bs4                          0.0.1   
...

Limitações

  • Os comandos interativos não são compatíveis
    • use -y para desinstalações
  • A busca pode não funcionar dependendo da configuração do sistema
  • Usa a infraestrutura do pip do os subjacente, então sua instalação depende da versão do pip do os.
0
0 96
Artigo Danusa Calixto · Mar. 12, 2024 9m read

Neste GitHub, você pode encontrar todas as informações sobre como usar um modelo de machine learning / IA do HuggingFace no Framework IRIS usando python.

1. iris-huggingface

Uso de modelos de machine learning no IRIS usando Python. Para modelos text-to-text, text-to-image e image-to-image.

Modelos de exemplo:

2. Instalação

2.1. Iniciando a produção

Na pasta iris-local-ml, abra um terminal e insira:

docker-compose up

Na primeira vez, pode levar alguns minutos para criar a imagem corretamente e instalar todos os módulos necessários para o Python.

2.2. Acesse a produção

Seguindo este link, acesse a produção: Acesse a produção

2.3. Encerrando a produção

docker-compose down

Como funciona

Por enquanto, alguns modelos talvez não funcionem com essa implementação, já que tudo é feito automaticamente, ou seja, não importa o modelo de entrada, tentaremos fazer com que funcione usando a biblioteca transformerspipeline.

Pipeline é uma ferramenta poderosa da equipe HuggingFace que analisa a pasta em que o modelo foi baixado e entende qual biblioteca deve usar entre PyTorch, Keras, Tensorflow ou JAX. Em seguida, ela carrega esse modelo usando AutoModel.
Então, ao inserir a tarefa, o pipeline sabe o que fazer com o modelo, tokenizer ou até extrator de características nessa pasta e gerencia a entrada automaticamente, tokeniza, processa, transfere para o modelo e retorna um resultado decodificado que podemos usar diretamente.

3. API do HuggingFace

Primeiro, você precisa iniciar a demonstração, usando o botão verde Start, ou use Stop e Start novamente para aplicar as mudanças nas configurações.

Em seguida, ao clicar na operação Python.HFOperation escolhida e selecionar na guia à direita action, você pode aplicar test à demonstração.

Na janela de test, selecione:

Tipo de solicitação: Grongier.PEX.Message

Em classname, insira:

msg.HFRequest

Para json, veja um exemplo de uma chamada para GPT2:

{
    "api_url":"https://api-inference.huggingface.co/models/gpt2",
    "payload":"Can you please let us know more details about your ",
    "api_key":"----------------------"
}

Agora, você pode clicar em Visual Trace para ver nos detalhes o que aconteceu e visualizar os registros.

OBSERVE que você precisa ter uma chave de API do HuggingFace antes de usar essa Operação (as chaves de API são gratuitas, basta fazer a inscrição na HF)

OBSERVE que você pode mudar o URL para testar outros modelos do HuggingFace. Talvez seja necessário mudar o payload.

Veja este exemplo:
sending hf reqhf reqhf resp

4. Use qualquer modelo da web

Nesta seção, vamos ensinar você a usar praticamente qualquer modelo da internet, HuggingFace ou não.

4.1. PRIMEIRO CASO: VOCÊ TEM SEU PRÓPRIO MODELO

Nesse caso, você precisa copiar e colar seu modelo, com config, tokenizer.json etc. dentro de uma pasta na pasta do modelo.
Caminho: src/model/yourmodelname/

Depois, você precisa acessar os parâmetros de Python.MLOperation.
Clique em Python.MLOperation e vá para settings na guia à direita. Depois, na parte Python e na parte %settings. Aqui, você pode inserir ou modificar quaisquer parâmetros (não se esqueça de pressionar apply depois de terminar).
Veja a configuração padrão para esse caso:
%settings

name=yourmodelname
task=text-generation

OBSERVE que qualquer configuração que não for name ou model_url entrará nas configurações PIPELINE.

Agora você pode clicar duas vezes na operação Python.MLOperation e executar o start. Você precisa ver na parte Log a inicialização do modelo.

Em seguida, criamos um PIPELINE utilizando transformers que usam o arquivo config na pasta como vimos antes.

Para chamar esse pipeline, clique na operação Python.MLOperation e selecione action na guia à direita. Você pode aplicar test à demonstração.

Na janela de test, selecione:

Tipo de solicitação: Grongier.PEX.Message

Em classname, insira:

msg.MLRequest

Para json, você precisa inserir todos os argumentos necessários para o modelo.
Aqui está um exemplo de uma chamada para GPT2:

{
    "text_inputs":"Unfortunately, the outcome",
    "max_length":100,
    "num_return_sequences":3
}

Clique em Invoke Testing Service (Invocar serviço de teste) e aguarde a operação do modelo.

Veja este exemplo:
sending ml req

Agora, você pode clicar em Visual Trace para ver nos detalhes o que aconteceu e visualizar os registros.

Veja este exemplo:
ml req

ml resp

4.2. SEGUNDO CASO: VOCÊ QUER BAIXAR UM MODELO DO HUGGINGFACE

Nesse caso, você precisa encontrar o URL do modelo no HuggingFace.

4.2.1. Configurações

Depois, você precisa acessar os parâmetros de Python.MLOperation.
Clique em Python.MLOperation e vá para settings na guia à direita. Depois, na parte Python e na parte %settings. Aqui, você pode inserir ou modificar quaisquer parâmetros (não se esqueça de pressionar apply depois de terminar).
Veja um exemplo de configuração para alguns modelos que encontramos no HuggingFace:

%settings para gpt2

model_url=https://huggingface.co/gpt2
name=gpt2
task=text-generation

%settings para camembert-ner

name=camembert-ner
model_url=https://huggingface.co/Jean-Baptiste/camembert-ner
task=ner
aggregation_strategy=simple

%settings para bert-base-uncased

name=bert-base-uncased
model_url=https://huggingface.co/bert-base-uncased
task=fill-mask

%settings para detr-resnet-50

name=detr-resnet-50
model_url=https://huggingface.co/facebook/detr-resnet-50
task=object-detection

%settings para detr-resnet-50-protnic

name=detr-resnet-50-panoptic
model_url=https://huggingface.co/facebook/detr-resnet-50-panoptic
task=image-segmentation

OBSERVE que qualquer configuração que não for name ou model_url entrará nas configurações PIPELINE. Então, no segundo exemplo, o pipeline camembert-ner requer a especificação de aggregation_strategy e task, enquanto gpt2 requer apenas uma task.

Veja este exemplo:
settings ml ope2

Agora você pode clicar duas vezes na operação Python.MLOperation e executar o start.
Você precisa ver na parte Log a inicialização e o download do modelo.
OBSERVAÇÃO Você pode atualizar esses logs a cada x segundos para ver o avanço dos downloads. dl in real time

Em seguida, criamos um PIPELINE utilizando transformers que usam o arquivo config na pasta como vimos antes.

4.2.2. Testes

Para chamar esse pipeline, clique na operação Python.MLOperation e selecione action na guia à direita. Você pode aplicar test à demonstração.

Na janela de test, selecione:

Tipo de solicitação: Grongier.PEX.Message

Em classname, insira:

msg.MLRequest

Para json, você precisa inserir todos os argumentos necessários para o modelo.
Aqui está um exemplo de uma chamada para GPT2 (Python.MLOperation):

{
    "text_inputs":"George Washington lived",
    "max_length":30,
    "num_return_sequences":3
}

Aqui está um exemplo de uma chamada para Camembert-ner (Python.MLOperation2):

{
    "inputs":"George Washington lived in washington"
}

Aqui está um exemplo de uma chamada para bert-base-uncased (Python.MLOperation3):

{
    "inputs":"George Washington lived in [MASK]."
}

Aqui está um exemplo de uma chamada para detr-resnet-50 usando um URL online (Python.MLOperationDETRRESNET):

{
    "url":"http://images.cocodataset.org/val2017/000000039769.jpg"
}

Aqui está um exemplo de uma chamada para detr-resnet-50-panoptic usando um URL como caminho (Python.MLOperationDetrPanoptic):

{
    "url":"/irisdev/app/misc/000000039769.jpg"
}

Clique em Invoke Testing Service (Invocar serviço de teste) e aguarde a operação do modelo.
Agora, você pode clicar em Visual Trace para ver nos detalhes o que aconteceu e visualizar os registros.

OBSERVE que, após fazer pela primeira vez o download de um modelo, a produção não fará o download novamente, mas usará os arquivos em cache encontrados em src/model/TheModelName/.
Se alguns arquivos estiverem ausentes, a Produção fará o download novamente.

Veja este exemplo:
sending ml reqml reqml resp

Veja este exemplo:
sending ml reqml resp

5. Solução de problemas

Se você tiver problemas, a leitura é o primeiro conselho que podemos dar a você. A maioria dos erros são facilmente compreendidos apenas ao ler os logs, pois quase todos os erros são capturados por um try/catch e registrados.

Se você precisar instalar um novo módulo ou dependência do Python, abra um terminal dentro do contêiner e digite, por exemplo: "pip install new-module"
Há várias formas de abrir um terminal,

  • Se você usa os plugins da InterSystems, pode clicar na barra abaixo no VSCode, que se parece com docker:iris:52795[IRISAPP], e selecionar Open Shell in Docker (Abrir Shell no Docker).
  • Em qualquer terminal local, digite: docker-compose exec -it iris bash
  • No Docker-Desktop, encontre o contêiner IRIS e clique em Open in terminal (Abrir no terminal)

Alguns modelos talvez exijam algumas alterações no pipeline ou nas configurações, por exemplo. É sua responsabilidade adicionar as informações corretas nas configurações e na solicitação.

6. Conclusão

Depois disso, você poderá usar qualquer modelo que de precisa ou possui no IRIS.
OBSERVE que você pode criar uma Python.MLOperation para cada um dos seus modelos e ter todos ativados ao mesmo tempo.

0
0 166
Artigo Rochael Ribeiro · jan 24, 2024 6m read

No cenário de dados atual, as empresas enfrentam vários desafios diferentes. Um deles é fazer análises sobre uma camada de dados unificada e harmonizada disponível para todos os consumidores. Uma camada que possa oferecer as mesmas respostas às mesmas perguntas, independentemente do dialeto ou da ferramenta usada. A Plataforma de Dados InterSystems IRIS responde a isso com um complemento de Análise Adaptativa que pode fornecer essa camada semântica unificada. Há muitos artigos no DevCommunity sobre como usá-lo por ferramentas de BI. Este artigo abordará como consumi-lo com IA e também como recuperar alguns insights. Vamos ir por etapas...

O que é a Análise Adaptativa?

Você pode facilmente encontrar uma definição [no site developer community] ​(https://community.intersystems.com/tags/adaptive-analytics) Resumindo, ela pode fornecer dados de forma estruturada e harmonizada para diversas ferramentas de sua escolha para consumo e análise posterior. Ela oferece as mesmas estruturas de dados a várias ferramentas de BI. Mas... Ela também pode oferecer as mesmas estruturas de dados para suas ferramentas de IA/ML!

A Análise Adaptativa tem um componente adicional chamado AI-LINK que constrói essa ponte entre a IA e a BI.

O que é exatamente o AI-Link ?

É um componente Python criado para permitir a interação programática com a camada semântica para os fins de otimizar os principais estágios do fluxo de trabalho do aprendizado de máquina (ML) (por exemplo, engenharia de características).

Com o AI-Link, você pode:

  • acessar programaticamente características do seu modelo de dados analítico;
  • fazer consultas, explorar dimensões e medidas;
  • alimentar pipelines de ML; ... e enviar os resultados de volta à camada semântica para que sejam consumidos novamente por outras (por exemplo, pelo Tableau ou Excel).

Como é uma biblioteca Python, ela pode ser usada em qualquer ambiente Python. Incluindo Notebooks. Neste artigo, darei um exemplo simples de como alcançar a solução de Análise Adaptativa a partir do Jupyter Notebook com a ajuda do AI-Link.

Aqui está o repositório git com o Notebook completo como exemplo: https://github.com/v23ent/aa-hands-on

Pré-requisitos

Para os passos a seguir, presume-se que você concluiu os pré-requisitos:

  1. Solução de Análise Adaptativa em funcionamento (com a Plataforma de Dados IRIS como armazém de dados)
  2. Jupyter Notebook em funcionamento
  3. Conexão entre 1. e 2. pode ser estabelecida

Etapa 1: configuração

Primeiro, vamos instalar os componentes necessários em nosso ambiente. Isso baixará alguns pacotes que são necessários para que as próximas etapas funcionem. 'atscale' - nosso pacote principal para a conexão 'prophet' - pacote de que precisaremos para fazer previsões

pip install atscale prophet

Em seguida, precisamos importar as principais classes que representam alguns conceitos importantes da nossa camada semântica. Client - classe que usaremos para estabelecer uma conexão com a Análise Adaptativa; Project - classe para representar projetos dentro da Análise Adaptativa; DataModel - classe que representará nosso cubo virtual;

from atscale.client import Client
from atscale.data_model import DataModel
from atscale.project import Project
from prophet import Prophet
import pandas as pd 

Etapa 2: conexão

Agora, deve estar tudo pronto para estabelecer uma conexão com nossa origem de dados.

client = Client(server='http://adaptive.analytics.server', username='sample')
client.connect()

Vá em frente e especifique os detalhes de conexão da sua instância da Análise Adaptativa. Quando for solicitada a organização, responda na caixa de diálogo e insira sua senha da instância da AtScale.

Com a conexão estabelecida, você precisará selecionar seu projeto da lista de projetos publicados no servidor. Você verá a lista de projetos como um prompt interativo, e a resposta deve ser o ID inteiro do projeto. O modelo de dados será selecionado automaticamente se for o único.

project = client.select_project()   
data_model = project.select_data_model()

Etapa 3: explore seu conjunto de dados

Há vários métodos preparados pela AtScale na biblioteca de componentes do AI-Link. Eles permitem explorar seu catálogo de dados, consultar dados e até ingerir alguns dados de volta. A documentação da AtScale tem uma vasta referência da API, descrevendo tudo o que está disponível. Primeiro, vamos ver qual é o nosso conjunto de dados ao chamar alguns métodos de "data_model":

data_model.get_features()
data_model.get_all_categorical_feature_names()
data_model.get_all_numeric_feature_names()

A saída será algo assim image

Depois de olhar um pouco, podemos consultar os dados em que realmente temos interesse usando o método "get_data". Ele retornará um DataFrame do pandas com os resultados da consulta.

df = data_model.get_data(feature_list = ['Country','Region','m_AmountOfSale_sum'])
df = df.sort_values(by='m_AmountOfSale_sum')
df.head()

Que mostrará seu dataframe: image

Vamos preparar um conjunto de dados e exibi-lo rapidamente no gráfico

import matplotlib.pyplot as plt

# Estamos pegando as vendas para cada data
dataframe = data_model.get_data(feature_list = ['Date','m_AmountOfSale_sum'])

# Crie um gráfico de linhas
plt.plot(dataframe['Date'], dataframe['m_AmountOfSale_sum'])

# Adicione rótulos e um título
plt.xlabel('Days')
plt.ylabel('Sales')
plt.title('Daily Sales Data')

# Exiba o gráfico
plt.show()

Saída: image

Etapa 4: previsão

A próxima etapa seria obter um valor da ponte do AI-Link - vamos fazer algumas previsões simples!

# Carregue os dados históricos para treinar o modelo
data_train = data_model.get_data(
    feature_list = ['Date','m_AmountOfSale_sum'],
    filter_less = {'Date':'2021-01-01'}
    )
data_test = data_model.get_data(
    feature_list = ['Date','m_AmountOfSale_sum'],
    filter_greater = {'Date':'2021-01-01'}
    )

Obtemos 2 conjuntos de dados diferentes aqui: para treinar e testar nosso modelo.

# Para a ferramenta, escolhemos fazer a previsão "Prophet", em que precisamos especificar 2 colunas: "ds" e "y"
data_train['ds'] = pd.to_datetime(data_train['Date'])
data_train.rename(columns={'m_AmountOfSale_sum': 'y'}, inplace=True)
data_test['ds'] = pd.to_datetime(data_test['Date'])
data_test.rename(columns={'m_AmountOfSale_sum': 'y'}, inplace=True)

# Inicialize e ajuste o modelo Prophet
model = Prophet()
model.fit(data_train)

Em seguida, criamos outro dataframe para acomodar nossa previsão e exibi-la no gráfico

# Crie um dataframe futuro para previsão
future = pd.DataFrame()
future['ds'] = pd.date_range(start='2021-01-01', end='2021-12-31', freq='D')

# Faça previsões
forecast = model.predict(future)
fig = model.plot(forecast)
fig.show()

Saída: image

Etapa 5: writeback

Depois de obter a previsão, podemos colocá-la de volta no armazém de dados e adicionar uma agregação ao nosso modelo semântico para que reflita para outros consumidores. A previsão estaria disponível por qualquer outra ferramenta de BI para usuários empresariais e analistas de BI. A previsão em si será colocada em nosso armazém de dados e armazenada lá.

from atscale.db.connections import Iris
db = Iris(
    username,
    host,
    host,
    driver,
    schema, 
    schema,
    password=None, 
    warehouse_id=None
    )

data_model.writeback(dbconn=db,
                    table_name= 'SalesPrediction',
                    DataFrame = forecast)

data_model.create_aggregate_feature(dataset_name='SalesPrediction',
                                    column_name='SalesForecasted',
                                    name='sum_sales_forecasted',
                                    aggregation_type='SUM')

Fim

É isso! Boa sorte com suas previsões!

0
0 65
Artigo Danusa Calixto · jan 11, 2024 6m read

Fui desafiado a criar um aplicativo de bot do Azure que possa recuperar e publicar dados no IRIS for Health.

 

Os dados de um paciente já foram registrados no repositório FHIR do IRIS for Health.

O MRN do paciente é 1001. O nome dele é Taro Yamad. (em japonês: 山田 太郎)

Esse bot pode publicar novas leituras de oxímetro como um recurso de observação associado ao paciente.

Visão geral de como o aplicativo de bot funciona abaixo:

 

(1) Em um aplicativo como o Teams, um usuário fala "Hello" (Olá).

O Teams envia a mensagem ao "Serviço de Canal Bot Framework" , hospedado pela Microsoft.

(2) O Serviço de Canal Bot Framework pergunta ao Bot.

O serviço pergunta ao Bot "Where is the endpoint?"(Onde está o endpoint?)

(3) O bot retorna as informações sobre o endpoint ao serviço.

O Bot conhece o endpoint.

(4) O serviço faz a solicitação do usuário ao endpoint.

O endpoint é um web application que é publicado no Azure web app.

(Minha amostra está escrita em Python.)  

​​(5) O endpoint cria a resposta e a envia ao serviço.

(6) O serviço recebe a resposta do endpoint e a passa ao usuário.

O IRIS não apareceu no fluxo acima.

Adicionei uma chamada do web application em python para a interoperabilidade do IRIS for Health. E preparei o repositório FHIR no IRIS assim:

 

A amostra de interoperabilidade do IRIS está aqui 👉 https://github.com/iijimam/iris-teams-interop

Você pode compilar e iniciar o contêiner com "docker-compose up -d". (Ele inclui os dados do paciente de amostra (MRN=1001) e as configurações necessárias.) 

Observação: o web application em python precisa chamar aplicativos via https. Mas removi os arquivos de certificado do meu repositório para evitar violar a política do git.

O web application em Python chama a classe dispatch REST do IRIS usando este URL "https://webserveraddress/production/request".

Confira as instruções abaixo para criar um aplicativo de Bot do Azure.

(1) Crie um grupo de recursos no Azure.

(2) Crie um Bot do Azure no grupo de recursos.

Você precisará confirmar seu "MicrosoftAppId" e "MicrosoftAppPassword". Vou explicar isso depois.

Se você quiser usar o bot do Teams, você precisa adicionar o canal do Teams ao bot. (O gif acima usa o canal do Teams.)

(3) Crie um web application no mesmo grupo de recursos que o bot no Azure.

Você obterá o endereço do servidor da web depois de criá-lo.

(4) Defina o endpoint na configuração do bot.

(5) Implante seu código para o web application.

Você pode escolher o repositório Git.

(6) Teste!

Vamos ver os detalhes!

(1) Crie um grupo de recursos no Azure.

Você pode criar um novo grupo de recursos a partir desta página (Grupos de recurso - Microsoft Azure)

Criei um novo recurso "202312ISJBotRG".

(2) Crie um Bot do Azure no grupo de recursos.

Depois de mover o grupo de recursos, você pode selecionar o menu "Create" (Criar).

   

Depois de criar o bot, você precisa definir "New client secret" (Novo segredo do cliente) na página [Configuration] da seguinte maneira:

Anote o caractere secreto e o valor do "Microsoft App ID".

Essas informações são necessárias para definir seu web application.

Se você quiser usar o bot do Teams, adicione o canal do Teams ao seu bot.

Você pode adicionar o canal do Teams usando o menu [Channels] no seu bot. (basta clicar no ícone do Teams.)

 

(3) Crie um web application no mesmo grupo de recursos que o bot no Azure.

Crie um web application para seu grupo de recursos. 

Volte para a página do grupo de recursos. E crie um web application assim:

 

Você pode selecionar sua "Runtime stack" (pilha de camada) favorita (o exemplo é Python 3.8), "Region" (região) e "Pricing plan" (plano de preços).

Depois de criar o aplicativo, você pode obter o endereço do servidor web na página do web application.

Precisamos definir o endereço para o código de aplicativo python. Então anote essa string. (O exemplo é "202312isjbotwebapp.azurewebsites.net")

Em seguida, precisamos definir o id do bot, o segredo e os detalhes de implantação.

Acesse a página [Configuration] na página do web application.

Você precisa adicionar "MicrosoftAppId" e "MicrosoftAppPassword" de "New application setting" (Configuração do novo aplicativo) a "Application settings" (Configurações do aplicativo).

(MicrosoftAppPassword é o segredo do bot que você copia na etapa (2).)

 

Em seguida, precisamos definir as "General settings" (Configurações gerais) para implantação.

 

Usei este código de exemplo 👉https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/python/44.prompt-for-user-input

O exemplo é simples, e é fácil de entender o que devo escrever sobre a comunicação do web application com o bot.

Atualizei alguns códigos nos diretórios bots e data_models.

Se você for executar o código no ambiente local, não precisa atualizá-lo.

Mas, se você quiser executá-lo no Azure web application, precisa atualizar o código de app.py assim:

definit_func(argv):
    APP = web.Application(middlewares=[aiohttp_error_middleware])
    APP.router.add_post("/api/messages", messages)
    return APP

ifname == "main": try: #web.run_app(APP, host="localhost", port=CONFIG.PORT) web.run_app(APP, host="0.0.0.0", port=CONFIG.PORT) except Exception as error: raise error

Adicionei esta chamada "python3.8 -m aiohttp.web -H 0.0.0.0 -P 8000 app:init_func" para iniciar um web application em Python no Azure.

Se você quiser saber os detalhes, consulte este URL👉https://stackoverflow.com/questions/60507967/running-an-python-app-as-an-azure-web-app

Meu código está aqui 👉 https://github.com/iijimam/teams-bot

Para executá-lo, você precisa atualizar partes do código.

  • Linha  15 e 16 de config.py: você precisa atualizar as informações do seu bot.
  • Linha 10 de GoIRIS.py : você precisa atualizar o endpoint que é o servidor REST do IRIS.

(4) Defina o endpoint na configuração do bot.

Acesse a página Configuration do bot.

Defina o endereço do servidor web + "/api/messages" como "Messaging endpoint".

 

(5) Implante seu código para o web application.

Eu uso o repositório git. É fácil implantar para o web application no Azure!!

Você pode definir as informações na página [Deployment Center] (Centro de implantação) do web application.

Após a implantação, você pode começar a testar!

(6) Teste!

Você pode testar seu aplicativo usando o "Test in Web Chat" (Testar no web chat) na página do bot.

 

Se você adicionar o canal do Teams, pode testar no Teams.

 

Depois de clicar em [Open in Teams] (Abrir no Teams), você pode abrir seu chat do teams e testar.😀

Bônus:

Você pode testar sem implantar seu aplicativo como um web application do Azure.

Quando o aplicativo em python e a interoperabilidade do IRIS estiverem prontos, você pode publicar esse aplicativo usando ngrok assim:

ngrok http 8000 --host-header=172.18.28.1:8000

ngrok pode oferecer um serviço de tunelamento.  Posso acessar minha porta local diretamente da área pública usando o serviço de tunelamento ngrok. 

E precisamos mudar o endpoint na configuração do bot assim:

Depois de configurado, posso testar usando o web chat.

0
0 120
Artigo Danusa Calixto · jan 10, 2024 5m read

Um cliente perguntou recentemente se o IRIS era compatível com o OpenTelemetry, pois queria medir o tempo que os serviços SOAP implementados pelo IRIS levavam para serem concluídos. O cliente já tem diversas outras tecnologias compatíveis com o OpenTelemetry para o tracing de processos.  No momento, o InterSystems IRIS (IRIS) não oferece suporte nativo ao OpenTelemetry.  

É verdade que a plataforma de dados do IRIS tem várias maneiras de capturar, registrar e analisar o desempenho de uma instância em execução. Essas informações não saem do IRIS por outros componentes do OpenTelemetry, como Agentes ou Coletores, em uma arquitetura do OpenTelemetry implementada.  Várias tecnologias já são compatíveis com o OpenTelemetry, que parece estar se tornando um padrão para a Observabilidade.

Embora haja desenvolvimento contínuo para oferecer suporte nativo a esse recurso em versões futuras do IRIS, este artigo explica como, com a ajuda do Embedded Python e das bibliotecas correspondentes do Python,  os desenvolvedores de aplicativos IRIS podem começar a publicar eventos de Trace para seus back-ends do OpenTelemetry com pouco esforço.  Mais importantemente, meu cliente pode começar a adotar algo hoje mesmo. 

Observabilidade. 

A observabilidade geralmente é composta de três aspectos principais:

  • Captura de métricas, que é a captura de medidas quantitativas sobre o desempenho e o comportamento de um sistema, parecido com o que o IRIS publica pela /api/monitor/metrics api
  • Registros, que envolvem capturar e armazenar informações relevantes geradas por um aplicativo ou sistema, como o que aparece nas saídas de Logs do Sistema ou o arquivo messages.log gerado por instâncias do IRIS.
  • Tracing: que envolve o rastreamento do fluxo de uma transação ou solicitação de serviço enquanto se move por vários componentes de uma solução. O tracing distribuído permite que você siga o caminho de uma solicitação em vários serviços, oferecendo uma representação visual de todo o fluxo da transação.

Este artigo e o aplicativo complementar encontrado aqui focam apenas no Tracing dServiços SOAP.

Um Trace identifica uma operação dentro de uma solução que, de fato, pode ser atendida por várias tecnologias em uma arquitetura, como navegador, balanceador de carga, servidor web, servidor de banco de dados etc.
Um Span representa uma única unidade de trabalho, como uma atualização ou uma consulta de banco de dados. Um span é o componente essencial de um Trace, e um Trace começa com um Span raiz e spans irmãos ou opcionalmente aninhados.

Nessa implementação, que só usa o IRIS como a tecnologia para gerar telemetria, um trace e um Span raiz são iniciados com a inicialização do Serviço SOAP.

Abordagem de implementação:

Transforme a classe %SOAP.WebService do IRIS com a lógica de implementação do OpenTelemetry e as funções das bibliotecas Python em uma nova classe chamada SOAP.WebService. Inclua Macros que possam ser usados no código do usuário para contribuir mais com a observabilidade e o tracing. Devem ser necessárias mudanças mínimas na implementação do SOAP (troque o uso de %SOAP.WebService pelo SOAP.WebService como a superclasse de serviço web para implementar o SOAP.
O diagrama abaixo ilustra essa abordagem:

 

Recursos dessa implementação:

  • Por padrão, todo Serviço SOAP será rastreado e relatará informações de traces.
  • Quando um Serviço SOAP é usado pela primeira vez, a implementação inicializará um objeto Tracer do OpenTelemetry. Uma combinação do nome do servidor IRIS e instância é fornecida como a origem da telemetria, e a ação SOAP é usada como o nome do span raiz padrão que rastreia o serviço soap.
  • Os traces de telemetria e o span padrão serão encerrados automaticamente quando a chamada do método SOAP for finalizada
  • Depois da criação, os pares de chave/valor dos atributos podem ser adicionados ao span raiz padrão, como o ID da sessão do CSP ou o número do trabalho
  • Os usuários podem usar o $$$OTELLog(...), para adicionar registros manuais a um span, usando uma string simples ou um array de pares chave-valor 
  • Os usuários podem usar o $$$OTELPushChildSpan(...)/$$$OTELPopChildSpan(...) para criar spans não raiz em torno de seções do código que desejam identificar de maneira independente com sua lógica

Instalação e teste

  • Faça o git pull/clone do repositório em qualquer diretório local
$ git clone https://github.com/pisani/opentelemetry-trace-soap.git
  • Abra uma janela de terminal nesse diretório e digite o seguinte para criar as imagens IRIS com código de amostra:
$ docker-compose build
  • Depois de criar a imagem iris, no mesmo diretório, digite o seguinte para iniciar os contêineres Jaeger e IRIS:
$ docker-compose up -d

Isso iniciará dois contêineres: o contêiner back-end de destino do OpenTelemetry Jaeger (também expondo uma interface do usuário) e uma instância do IRIS que servirá como o endpoint do servidor dos serviços web SOAP.  Três serviços web simples foram desenvolvidos na instância do IRIS para testar a solução.

  • Usando seu navegador, acesse as informações SOAP e páginas de teste por esse URL. Faça login como um superusuário/SYS quando solicitado:
http://localhost:52773/csp/irisapp/SOAP.MyService.cls

(Observação: essas páginas não são ativadas por padrão, e a segurança na instância do IRIS em execução precisa ser relaxada para ativar esse recurso, facilitando o teste)

Selecione cada um dos métodos da web que você quer testar, para gerar a atividade SOAP.  Para ver essa implementação gerar um Erro nos traces observados, use zero (0) como o segundo número no método SOAP Divide(), forçando um erro <DIVDE>.

  • Abra outra guia do navegador e acesse a IU do Jaeger pelo seguinte URL
http://localhost:16686
  • A página de destino resultante mostra todos os serviços que contribuem para as leituras de telemetria e deve ser parecida com a captura de tela abaixo: 

 

Conclusão

Resumindo, este artigo mostra como o Embedded Python pode ser usado para adicionar mais recursos ao IRIS, no meu caso, implementar o tracing de Observabilidade para os serviços SOAP.  As opções disponíveis pelo Python e a capacidade do IRIS de aproveitar as bibliotecas Python são vastas.

Reconheço que é possível trabalhar para criar uma classe mais genérica de suporte ao OpenTelemetry que implemente o mesmo para os serviços REST, além de ampliar as assinaturas de Class Method para rastrear o timing de qualquer Class method por esse framework.

0
0 94
Artigo Danusa Calixto · jan 10, 2024 12m read

Temos um delicioso conjunto de dados com receitas escritas por vários usuários do Reddit, porém, a maioria das informações é texto livre, como o título ou a descrição de um post. Vamos descobrir como carregar o conjunto de dados facilmente, extrair algumas características e analisá-las usando recursos do modelo de linguagem grande da OpenAI no Embedded Python e no framework Langchain.

Carregando o conjunto de dados

Primeiro de tudo, precisamos carregar o conjunto de dados ou podemos só nos conectar a ele?

Há diferentes maneiras de alcançar isso: por exemplo, é possível usar o CSV Record Mapper em uma produção de interoperabilidade ou até em aplicativos legais do OpenExchange, como csvgen.

Vamos usar o Foreign Tables. Um recurso bastante útil para projetar dados armazenados fisicamente em outro lugar no IRIS SQL. Podemos usar isso para ter uma primeira visão dos arquivos do conjunto de dados.

Criamos um Foreign Server:

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

E, em seguida, uma Foreign Table que se conecta ao arquivo CSV:

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

É isso, imediatamente podemos executar consultas SQL em "dataset.Recipes": image

## Quais dados são necessários? O conjunto de dados é interessante e estamos com fome. No entanto, se quisermos decidir uma receita para cozinhar, será preciso mais algumas informações que possamos usar para análise.

Vamos trabalhar com duas classes persistentes (tabelas):

  • yummy.data.Recipe: uma classe que contém o título e a descrição da receita e algumas outras propriedades que queremos extrair e analisar, por exemplo, Score, Difficulty, Ingredients, CuisineType, PreparationTime (nota, dificuldade, ingredientes, tipo de culinária, tempo de preparo)
  • yummy.data.RecipeHistory: uma classe simples para registrar o que estamos fazendo com a receita

Podemos agora carregar nossas tabelas "yummy.data*" com o conteúdo do conjunto de dados:

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

Parece bom, mas ainda precisamos descobrir como vamos gerar os dados para os campos Score, Difficulty, Ingredients, PreparationTime e CuisineType.

## Analise as receitas Queremos processar cada título e descrição de receita e:

  • Extrair informações como Difficulty, Ingredients, CuisineType etc.
  • Criar nossa própria nota com base em nossos critérios para que possamos decidir o que queremos cozinhar.

Vamos usar o seguinte:

LLM (modelos de linguagem grande) são realmente uma ótima ferramenta para processar linguagem natural.

LangChain está pronto para uso no Python, então podemos usá-lo diretamente no InterSystems IRIS usando o Embedded Python.

A classe "SimpleOpenAI" completa fica assim:

/// Análise simples da OpenAI para receitas
Class yummy.analysis.SimpleOpenAI Extends Analysis
{

Property CuisineType As %String;

Property PreparationTime As %Integer;

Property Difficulty As %String;

Property Ingredients As %String;

/// Execute
/// Você pode tentar isto a partir de um terminal:
/// set a = ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8))
/// do a.Run()
/// zwrite a
Method Run()
{
    try {
        do ..RunPythonAnalysis()

        set reasons = ""

        // meus tipos de culinária favoritos
        if "spanish,french,portuguese,italian,korean,japanese"[..CuisineType {
            set ..Score = ..Score + 2
            set reasons = reasons_$lb("It seems to be a "_..CuisineType_" recipe!")
        }

        // não quero passar o dia todo cozinhando :)
        if (+..PreparationTime < 120) {
            set ..Score = ..Score + 1
            set reasons = reasons_$lb("You don't need too much time to prepare it") 
        }
        
        // bônus para ingredientes favoritos!
        set favIngredients = $listbuild("kimchi", "truffle", "squid")
        for i=1:1:$listlength(favIngredients) {
            set favIngred = $listget(favIngredients, i)
            if ..Ingredients[favIngred {
                set ..Score = ..Score + 1
                set reasons = reasons_$lb("Favourite ingredient found: "_favIngred)
            }
        }

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

    } catch ex {
        throw ex
    }
}

/// Atualize a receita com o resultado da análise
Method UpdateRecipe()
{
    try {
        // chame a implementação de classe mãe primeiro
        do ##super()

        // adicione resultados de análises da OpenAI específicos
        set ..Recipe.Ingredients = ..Ingredients
        set ..Recipe.PreparationTime = ..PreparationTime
        set ..Recipe.Difficulty = ..Difficulty
        set ..Recipe.CuisineType = ..CuisineType

    } catch ex {
        throw ex
    }
}

/// Execute a análise usando o embedded Python + Langchain
/// do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8)).RunPythonAnalysis(1)
Method RunPythonAnalysis(debug As %Boolean = 0) [ Language = python ]
{
    # load OpenAI APIKEY from env
    import os
    from dotenv import load_dotenv, find_dotenv
    _ = load_dotenv('/app/.env')

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

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

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

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

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

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

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

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

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

    return 1
}

}

O método "RunPythonAnalysis" é onde tudo relacionado a OpenAI acontece :). Você pode executá-lo diretamente no terminal para uma receita específica:

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

Vamos obter uma saída assim:

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

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

Tudo bem. Parece que nosso prompt da OpenAI é capaz de retornar algumas informações úteis. Vamos executar toda a classe da análise no terminal:

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

## Analisando todas as receitas! Naturalmente, você gostaria de executar a análise em todas as receitas que carregamos.

Você pode analisar uma variedade de IDs de receitas desta forma:

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

Depois disso, verifique novamente sua tabela de receitas e confira os resultados

select * from yummy_data.Recipe

image

Acho que eu poderia tentar a pizza de abóbora ou o tofu com kimchi coreano e porco :). Vou precisar conferir novamente em casa de qualquer forma :)

Observações finais

Encontre o exemplo completo em https://github.com/isc-afuentes/recipe-inspector

Com esse exemplo simples, aprendemos a usar as técnicas de LLM para adicionar recursos ou analisar partes dos seus dados no InterSystems IRIS.

Com esse ponto de partida, você pode pensar em:

  • Usar a BI do InterSystems para explorar e navegar pelos seus dados usando cubos e painéis.
  • Criar um webapp e fornecer IU (por exemplo, Angular). Para isso, você pode aproveitar pacotes como RESTForms2 para gerar automaticamente as APIs REST para suas classes persistentes.
  • Armazenar as receitas independentemente de gostar delas e, depois, tentar determinar se uma nova receita gostará de você? Você pode tentar uma abordagem com o IntegratedML ou até mesmo um LLM, fornecendo alguns dados de exemplo e criando um caso de uso de RAG (Geração Aumentada de Recuperação).

O que mais você pode tentar? Me diga o que você acha!

0
0 110
Artigo Danusa Calixto · Dez. 5, 2023 5m read

Deixe-me apresentar meu novo projeto, que é o irissqlcli, um REPL (Read-Eval-Print Loop)  para o InterSystems IRIS SQL 

  • Destaque de sintaxe
  • Sugestões (tabelas, funções)
  • 20+ formatos de saída
  • Suporte ao stdin
  • Saída em arquivos 

Instale com o pip

pipinstallirissqlcli

Ou execute com o docker

dockerrun-itcaretdev/irissqlcliirissqlcliiris://_SYSTEM:SYS@host.docker.internal:1972/USER

Conecte ao 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
+----------+-------------------+------------------------------------------------------------+
| Command  | Shortcut          | Description                                                |
+----------+-------------------+------------------------------------------------------------+
| .exit    | \q                | Exit.                                                      |
| .mode    | \T                | Change the table format used to output results.            |
| .once    | \o [-o] filename  | Append next result to an output file (overwrite using -o). |
| .schemas | \ds               | List schemas.                                              |
| .tables  | \dt [schema]      | List tables.                                               |
| \e       | \e                | Edit command with editor (uses $EDITOR).                   |
| help     | \?                | Show this help.                                            |
| nopager  | \n                | Disable pager, print to stdout.                            |
| notee    | notee             | Stop writing results to an output file.                    |
| pager    | \P [command]      | Set PAGER. Print the query results via PAGER.              |
| prompt   | \R                | Change prompt format.                                      |
| quit     | \q                | Quit.                                                      |
| tee      | tee [-o] filename | Append all results to an output file (overwrite using -o). |
+----------+-------------------+------------------------------------------------------------+
Time: 0.012s
[SQL]_SYSTEM@localhost:USER>

$ irissqlcli--helpUsage: irissqlcli[OPTIONS][URI][USERNAME]

Options: -h, --hostTEXTHostaddressoftheIRISinstance. -p, --portINTEGERPortnumberatwhichtheIRISinstanceislistening. -U, --usernameTEXTUsernametoconnecttotheIRISinstance. -u, --userTEXTUsernametoconnecttotheIRISinstance. -W, --passwordForcepasswordprompt. -v, --versionVersionofirissqlcli. -n, --nspaceTEXTnamespacenametoconnectto. -q, --quietQuietmode, skipintroonstartupandgoodbyeonexit. -l, --logfileFILENAMELogeveryqueryanditsresultstoafile. --irissqlclircFILELocationofirissqlclircfile. --auto-vertical-outputAutomaticallyswitchtoverticaloutputmodeiftheresultiswiderthantheterminalwidth. --row-limitINTEGERSetthresholdforrowlimitprompt. Use 0 todisableprompt. -t, --tableDisplaybatchoutputintableformat. --csvDisplaybatchoutputinCSVformat. --warn / --no-warnWarnbeforerunningadestructivequery. -e, --executeTEXTExecutecommandandquit. --helpShowthismessageandexit.

ou no modo Python Embedded (%Service_CallIn precisa estar ativada)

$ irissqlcliiris+emb:///USERServer:  IRISforUNIX (UbuntuServerLTSforARM64Containers) 2022.2 (Build 368U) FriOct 21 2022 16:39:41EDTVersion: 0.1.0[SQL]irisowner@/usr/irissys/:USER>

Ele é compatível com o stdin, então você pode criar um pipe de um arquivo SQL com várias consultas SQL e comandos irissqcli. Por exemplo, este comando produzirá 3 arquivos em formatos diferentes (de mais de 20 formatos disponíveis)

$ cat <<EOF | irissqlcli iris://_SYSTEM:SYS@localhost:1972/USER

.mode csv; tee -o test.csv; 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

Além disso, é possível executar um terminal da Web com o 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/

E com 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

Por favor, vote no projeto no concurso

0
0 103
Artigo Danusa Calixto · Nov. 28, 2023 8m read

Olá, Comunidade,
Neste artigo, vou apresentar meu aplicativo iris-mlm-explainer

Esse aplicativo da Web se conecta ao InterSystems Cloud SQL para criar, treinar, validar e prever modelos de ML, fazer previsões e mostrar um painel com todos os modelos treinados e uma explicação sobre o funcionamento de um modelo de machine learning ajustado. O painel fornece plotagens interativas de desempenho do modelo, importância do recurso, contribuições do recurso para previsões individuais, plotagens de dependência parcial, valores SHAP (interação), visualização de árvores de decisões individuais etc.

Pré-requisitos

  • Você precisa ter uma conta no InterSystems Cloud SQL.
  • Você precisa ter o <a book="" en="" getting-started-installing-git="" git-scm.com="" https:="" v2="">Git</a> instalado localmente.
  • Você precisa ter o <a downloads="" https:="" www.python.org="">Python3</a> instalado localmente.  

Primeiros Passos

Vamos seguir as etapas abaixo para criar e visualizar o painel explicativo de um modelo:

  • Etapa 1 : fazer o git pull/clone do repositório

  • Etapa 2 : fazer login no portal de serviços do InterSystems Cloud SQL

    • Etapa 2.1 : adicionar e gerenciar arquivos
    • Etapa 2.2 : importar DDL e arquivos de dados
    • Etapa 2.3 : criar modelo
    • Etapa 2.4 : treinar modelo
    • Etapa 2.5 : validar modelo
  • Etapa 3 : ativar o ambiente virtual do Python

  • Etapa 4 : executar o aplicativo da Web para previsão

  • Etapa 5 : explorar e o painel explicativo

Etapa 1 : fazer o git pull/clone do repositório

Então, vamos começar com a primeira etapa

Crie uma pasta e faça o git pull/clone do repositório em qualquer diretório local

git clone https://github.com/mwaseem75/iris-mlm-explainer.git

 

Etapa 2 : fazer login no portal de serviços do InterSystems Cloud SQL

Faça login no InterSystems Cloud Service Portal
image

 

 

Selecione a implantação executada

image

 

Etapa 2.1 : adicionar e gerenciar arquivos

Clique em "Add and Manage Files" (Adicionar e gerenciar arquivos)

image

O repositório contém os arquivos USA_Housing_tables_DDL.sql(DDL para criar tabelas), USA_Housing_train.csv(dados de treinamento) e USA_Housing_validate.csv(para validação) na pasta de conjuntos de dados. Selecione o botão de upload para adicionar esses arquivos.

Adicionar arquivos

Etapa 2.2 : importar DDL e arquivos de dados

Clique em "Import files" (Importar arquivos), no botão de opção das declarações DDL ou DML e no botão "Next" (Próximo)

Importar DDL

Clique no botão de opção do Intersystems IRIS e em "Next"

IsIRIS

Selecione o arquivo USA_Housing_tables_DDL.sql e pressione o botão para importar arquivos

Importar arquivo DDL

Clique em "Import" na caixa de diálogo de confirmação para criar a tabela

confirmar importação

importação concluída

Clique nas ferramentas da Query em SQL para verificar se as tabelas foram criadas

conferir se as tabelas foram criadas

Importar arquivos de dados

Clique em "Import files" (Importar arquivos), no botão de opção dos dados CSV e no botão "Next" (Próximo)

csv1

Selecione o arquivo USA_Housing_train.csv e clique no botão "Next"

csv2

 

Selecione o arquivo USA_Housing_train.csv na lista suspensa, marque para importar o arquivo como linha de cabeçalho, selecione "Field names in header row match column names in selected table" (Os nomes dos campos na linha de cabeçalho correspondem aos nomes das colunas na tabela selecionada) e clique em "Import files"

csv3

Clique em "Import" na caixa de diálogo de confirmação

csv4

Confira se 4000 linhas foram atualizadas

csv5

Repita as mesmas etapas para importar o arquivo USA_Housing_validate.csv, que contém 1500 registros

csv6

Etapa 2.3 : criar modelo

Clique em "IntegratedML tools" (ferramentas do IntegratedML) e selecione "Create Panel" (Criar painel).

Insira "USAHousingPriceModel" no campo de nome do modelo, selecione a tabela "usa_housing_train" e "Price" no menu suspenso "Field to predict" (Campo para prever). Clique no botão "Create model" para criar o modelo

criar modelo

 

Etapa 2.4 : treinar modelo

Selecione "Train Panel" (Treinar painel), escolha "USAHousingPriceModel" na lista suspensa "Model to train" (Modelo a treinar) e insira "USAHousingPriceModel_t1" no campo de nome do modelo a treinar

TREINAR1

O modelo será treinado após a conclusão do status de execução

TREINAR2

 

Etapa 2.5 : validar modelo

Selecione "Validate Panel" (Validar painel), escolha "USAHousingPriceModel_t1" na lista suspensa "Trained model to validate" (Modelo treinado a validar), selecione "usa_houseing_validate" na lista suspensa "Table to validate model from" (Tabela para validar o modelo) e clique no botão "Validate model" (Validar modelo)

image

Clique em "Show validation metrics" para ver as métricas

mostrar validação

Clique no ícone de gráfico para ver o gráfico "Prediction VS Actual" (Previsão x Real)

gráfico de validação

 

Etapa 3 : ativar o ambiente virtual do Python

O repositório já contém uma pasta de ambiente virtual do python (venv) com as bibliotecas necessárias.

Tudo o que precisamos fazer é ativar o ambiente
No Unix ou MacOS:

$source venv/bin/activate

No Windows:

venv\scripts\activate

###Etapa 4 : definir parâmetros de conexão do InterSystems Cloud SQL

O repositório contém o arquivo config.py. Basta abrir e definir os parâmetros
image
Coloque os mesmos valores usados no InterSystems Cloud SQL
image

 

Etapa 4 : executar o aplicativo da Web para previsão

Execute o comando abaixo no ambiente virtual para iniciar nosso aplicativo principal

python app.py

image

Acesse http://127.0.0.1:5000/ para executar o aplicativo

image

Insira "Age of house" (Idade da casa), "No of rooms" (Nº de cômodos), "No of bedroom" (Nº de quartos) e "Area population" (População da área) para obter a previsão

image

Etapa 5 : explorar e o painel explicativo

Por fim, execute o comando abaixo no ambiente virtual para iniciar nosso aplicativo principal

python expdash.py

imageimage
image

Acesse http://localhost:8050/ para executar o aplicativo
image

O aplicativo listará todos os modelos treinados com nosso "USAHousingPriceModel". Clique no hyperlink "Go to dashboard" (Acessar painel) para visualizar a explicação sobre o modelo

Importância dos recursos. Quais recursos tiveram o maior impacto?
image

Métricas quantitativas para o desempenho do modelo, Qual é a diferença entre o valor previsto e o observado?
image

Previsão e Como cada recurso contribuiu para a previsão?
image

Ajuste os valores dos recursos para mudar a previsão
image

Resumo Shap, Ordenamento dos recursos por valores shap
image

Resumo de interações, Ordenamento dos recursos por valor de interação shap
image

Árvores de decisões, Exibição de árvores de decisões individuais dentro do Random Forest
image

Obrigado

0
0 87
Artigo Danusa Calixto · Nov. 8, 2023 5m read

principal
 

Olá, comunidade

Neste artigo, vou apresentar meu aplicativo IRIS-FlaskBlog.

O IRIS-FlaskBlog é um aplicativo real que usa a funcionalidade de framework da Web Flask, o ORM SQLALchemy e o InterSystems IRIS. O aplicativo contém a inscrição e autenticação de usuários com a ajuda da biblioteca python Flask-Login, uma interface de usuário responsiva para criar e editar posts.
 

Recursos do aplicativo

  • Inscrição e autenticação de usuários
  • Criação de uma estrutura de dados com classes do modelo SQLALcemy
  • Interface de usuário responsiva para criar, editar e excluir posts
  • Capacidade de curtir e adicionar comentários ao post
  • Pesquisa com base no usuário e em tags

##Tecnologias usadas

  • Flask: um micro framework da Web para Python que permite desenvolver aplicativos da Web com rapidez e eficiência.
  • SQLAlchemy: uma biblioteca Object-Relational Mapping (ORM) que fornece uma interface pythônica de alto nível para interagir com bancos de dados.
  • InterSystems IRIS: uma plataforma de dados de alto desempenho que combina um banco de dados poderoso com integração, análise e recursos de IA.

##Instalação

  1. Faça o git pull/clone do repositório em qualquer diretório local
git clone https://github.com/mwaseem75/IRIS-FlaskBlog.git
  1. Abra um terminal Docker nesse diretório e execute:
docker-compose build
  1. Execute o contêiner IRIS:
docker-compose up -&lt;span class="hljs-keyword">d&lt;/span>

##Fluxo do aplicativo

No docker, a ENTRYPOINT [ "python", "app.py" ] é definida, que iniciará o aplicativo  (A instrução ENTRYPOINT é usada para configurar os executáveis que sempre serão executados após a inicialização do contêiner)  

# app.py# import create_app from website packagefrom website import create_app
# import configuration parametersfrom website.myconfig import *

if __name__ == "__main__":
    # Get db parameters and creating to create_app in order to create DB
    database_uri = f'iris://{DB_USER}:{DB_PASS}@{DB_URL}:{DB_PORT}/{DB_NAMESPACE}'
    app = create_app(database_uri)
    app.run('0.0.0.0', port="4040", debug=False)

O código acima invoca a função create_app() e executa o aplicativo na porta 4040

create_app() function is defined in \__init__.py file

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from .myconfig import *

db = SQLAlchemy()

defcreate_app(database_uri):#Import flask application
    app = Flask(__name__)
    app.config['SECRET_KEY'] = "iris-FlaskBlogKey"# assigning db parameters
    app.config['SQLALCHEMY_DATABASE_URI'] = database_uri
    #create and push an application context onto the application context stack
    app.app_context().push()

    from .views import views
    from .auth import auth
    from .models import User
    app.register_blueprint(views, url_prefix="/")
    app.register_blueprint(auth, url_prefix="/")

    db.init_app(app)

    with app.app_context():
        #Create database
        db.create_all()

    # Assign Login View
    login_manager = LoginManager()
    login_manager.login_view = "auth.login"
    login_manager.init_app(app)
    
    @login_manager.user_loaderdefload_user(id):return User.query.get(int(id))

    return app

O código acima cria o banco de dados ao invocar a função SQLAlchemy create_all(), que criará a estrutura do banco de dados definida no arquivo models.py

from . import db
from flask_login import UserMixin
from sqlalchemy.sql import func

#creates tags_accosication table
tags_table = db.Table(
    'tags_association',
    db.Column('post_id', db.ForeignKey('post.id'), primary_key=True),
    db.Column('tag_id', db.ForeignKey('tag.id'), primary_key=True),
)

#Users tableclassUser(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(150), unique=True)
    username = db.Column(db.String(150), unique=True)
    password = db.Column(db.String(150))
    date_created = db.Column(db.DateTime(timezone=True), default=func.now())
    posts = db.relationship('Post', backref='user', passive_deletes=True)
    comments = db.relationship('Comment', backref='user', passive_deletes=True)
    likes = db.relationship('Like', backref='user', passive_deletes=True)

    def__repr__(self):returnf'{self.username}'#Posts TableclassPost(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(250))
    content = db.Column(db.Text)
    date_created = db.Column(db.DateTime(timezone=True), default=func.now())
    author = db.Column(db.Integer, db.ForeignKey(
        'user.id', ondelete="CASCADE"), nullable=False)
    comments = db.relationship('Comment', backref='post', passive_deletes=True)
    likes = db.relationship('Like', backref='post', passive_deletes=True)
    tags = db.relationship('Tag', secondary=tags_table,
                           backref=db.backref('posts', lazy='dynamic'))

    def__repr__(self):
        user = User.query.filter_by(id=self.author).first()
        returnf'{user.username}'#Tags tableclassTag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(150))

    def__repr__(self):returnf'{self.name}'#Comments tableclassComment(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    text = db.Column(db.String(500), nullable=False)
    date_created = db.Column(db.DateTime(timezone=True), default=func.now())
    author = db.Column(db.Integer, db.ForeignKey(
        'user.id', ondelete="CASCADE"), nullable=False)
    post_id = db.Column(db.Integer, db.ForeignKey(
        'post.id', ondelete="CASCADE"), nullable=False)

#Like tableclassLike(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    date_created = db.Column(db.DateTime(timezone=True), default=func.now())
    author = db.Column(db.Integer, db.ForeignKey(
        'user.id', ondelete="CASCADE"), nullable=False)
    post_id = db.Column(db.Integer, db.ForeignKey(
        'post.id', ondelete="CASCADE"), nullable=False)

Banco de dados do aplicativo

O SQLALchemy criará as tabelas abaixo:

  • user: para armazenar as informações do usuário
  • post: para armazenar os dados relacionados a posts
  • tags: para armazenar as tags com o post
  • tags_association: para armazenar os links entre posts e tags
  • comments: para salvar os comentários dos posts pelos usuários
  • like: para armazenar os detalhes das curtidas dos usuários

Para ver os detalhes da tabela, acesse http://localhost:52775/csp/sys/exp/%25CSP.UI.Portal.SQL.Home.zen?$NAMESPACE=USER#
imagem

Para mais detalhes, acesse a página do aplicativo IRIS-FlaskBlog
Obrigado

0
0 85
Artigo Heloisa Paiva · Mar. 17, 2023 2m read

Por que decidi escrever isso

No meu último artigo, falei sobre devolver valores com Python. Mas a devolução é muito simples, o que pode complicá-la é o que vou falar hoje: onde é tratado o valor.
 

Objeto Python em IRIS

Seguindo o exemplo do último artigo, temos o método:

Class python.returnTest [ Abstract ]
{

ClassMethod returnSomething(pValue... As%String) As%Integer [ Language = python ]
{
	return pValue
}

}

 

Assim, temos devolvido um objeto de Python, que para IRIS é a classe %SYS.Python. Então, por ejemplo, se chamo o método com dois valores, tenho:
 

2
0 82
Artigo Danusa Calixto · Set. 19, 2023 7m read

Ao começar a desenvolver com o IRIS, temos um kit de distribuição ou, no caso do Docker, extraímos a imagem do Docker e, geralmente, precisamos inicializá-lo e configurar o ambiente de desenvolvimento. Talvez seja necessário criar bancos de dados, namespaces, ativar/desativar serviços e criar recursos. Com frequência, precisamos importar código e dados na instância do IRIS e executar código personalizado para inicializar a solução.

<img alt="Lajos Simicska declara guerra contra Viktor Orban: "É ele ou eu!" - The Budapest Beacon" src="https://budapestbeacon.com/wp-content/uploads/2015/02/there-can-be-only-one.jpg">

Há vários modelos no Open Exchange onde sugerimos como inicializar o REST, Interoperability, Analytics, Fullstack e vários outros modelos com ObjectScript. E se só quisermos usar o Python para configurar o ambiente de desenvolvimento para o projeto do Embedded Python com o IRIS?

Então, a versão recente do modelo do Embedded Python é o boilerplate de python puro que pode ser um ponto de partida para os desenvolvedores que criam projetos em python, sem precisar usar e aprender o ObjectScript. Este artigo demonstra como esse modelo pode ser usado para inicializar o IRIS. Vamos lá!

Ok!

Criando o banco de dados e namespace

Geralmente, precisamos criar o banco de dados e namespace. Isso pode ser feito com a funcionalidade cpf merge do IRIS, e este é o arquivo merge que faz isso:

[Actions]

CreateResource:Name=%DB_IRISAPP_DATA,Description="IRISAPP_DATA database"

CreateDatabase:Name=IRISAPP_DATA,Directory=/usr/irissys/mgr/IRISAPP_DATA

CreateResource:Name=%DB_IRISAPP_CODE,Description="IRISAPP_CODE database"

CreateDatabase:Name=IRISAPP_CODE,Directory=/usr/irissys/mgr/IRISAPP_CODE

CreateNamespace:Name=IRISAPP,Globals=IRISAPP_DATA,Routines=IRISAPP_CODE,Interop=1

ModifyService:Name=%Service_CallIn,Enabled=1,AutheEnabled=48

ModifyUser:Name=SuperUser,PasswordHash=a31d24aecc0bfe560a7e45bd913ad27c667dc25a75cbfd358c451bb595b6bd52bd25c82cafaa23ca1dd30b3b4947d12d3bb0ffb2a717df29912b743a281f97c1,0a4c463a2fa1e7542b61aa48800091ab688eb0a14bebf536638f411f5454c9343b9aa6402b4694f0a89b624407a5f43f0a38fc35216bb18aab7dc41ef9f056b1,10000,SHA512

O cpf Merge contém Ações que, nesse caso, criam os bancos de dados IRISAPP_DATA e IRISAPP_CODE para dados e código, respectivamente, o namespace IRISAPP relacionado para acessá-los e os recursos %IRISAPP_DATA e %IRISAPP_CODE" para gerenciar o acesso.

Esse cpf Merge também permite que o serviço Callin faça o Embedded Python funcionar através da ação ModifyService:

ModifyService:Name=%Service_CallIn,Enabled=1,AutheEnabled=48

A última ação é um exemplo de como você pode definir a senha do usuário com segurança, usando a ação ModifyUser:

ModifyUser:Name=SuperUser,PasswordHash=a31d24aecc0bfe560a7e45bd913ad27c667dc25a75cbfd358c451bb595b6bd52bd25c82cafaa23ca1dd30b3b4947d12d3bb0ffb2a717df29912b743a281f97c1,0a4c463a2fa1e7542b61aa48800091ab688eb0a14bebf536638f411f5454c9343b9aa6402b4694f0a89b624407a5f43f0a38fc35216bb18aab7dc41ef9f056b1,10000,SHA512

O hash da senha pode ser gerado por outra imagem do docker da InterSystems, por exemplo, com a seguinte linha chamada em um terminal (saiba mais sobre o passwordhash):

docker run --rm -it containers.intersystems.com/intersystems/passwordhash:1.1 -algorithm SHA512 -workfactor 10000

O comando merge pode ser chamado no Dockerfile desta maneira:

iris merge IRIS merge.cpf && \

Outros tipos de ações merge e diferentes configurações disponíveis para IRIS podem ser encontrados na documentação.

Executando um código de inicialização arbitrária

Outro caso de uso popular é executar um código personalizado arbitrário para carregar dados, inicializar variáveis e carregar código do repositório, por exemplo, em forma de pacote IPM. Geralmente, nos modelos IRIS, há um arquivo iris.script especial que contém esse código na forma de ObjectScript. Mantive o arquivo iris.script no repositório só para fazer a demonstração de como será usado com o ObjectScript. 

Neste modelo do python, você pode encontrar o exemplo desse código de inicialização em um arquivo iris_script.py. Vamos ver o que ele contém:

import glob

import os

import iris

import pandas as pd

from sqlalchemy import create_engine

from iris import ipm

# switch namespace to the %SYS namespace
iris.system.Process.SetNamespace("%SYS")

# set credentials to not expire
iris.cls('Security.Users').UnExpireUserPasswords("*")

# switch namespace to IRISAPP built by merge.cpf
iris.system.Process.SetNamespace("IRISAPP")

# load ipm package listed in module.xml#iris.cls('%ZPM.PackageManager').Shell("load /home/irisowner/dev -v")assert ipm('load /home/irisowner/dev -v')

# load demo data
engine = create_engine('iris+emb:///')

# list all csv files in the demo data folderfor files in glob.glob('/home/irisowner/dev/data/*.csv'):

# get the file name without the extension
table_name = os.path.splitext(os.path.basename(files))[0]

# load the csv file into a pandas dataframe
df = pd.read_csv(files)

# write the dataframe to IRIS
df.to_sql(table_name, engine, if_exists='replace', index=False, schema='dc_demo')

O código é bastante autoexplicativo e demonstra como mudar namespaces, carregar código de respositório na forma de módulo IPM e importar arquivos csv contidos na pasta /data do repositório usando a biblioteca sqlalchemy.

O que o modelo faz?

Basicamente, esse modelo fornece exemplos de como o Embedded Python pode ser usado com o IRIS. 

1. Chamar o Python a partir do ObjectScript

Para a demonstração, temos várias classes do ObjectScript no repositório.

Para testar a abordagem, abra o terminal IRIS e execute:

IRISAPP>d ##class(dc.python.test).Hello()

World

Esse teste demonstra como você pode usar as funções no código sample.py no IRIS usando o Embedded Python.

2. Fazer referência ao IRIS no Python

Para testar isso, você pode executar irisapp.py, que realiza diferentes operações com o banco de dados IRIS. Para executar todo o teste, inicie a sessão do terminal no docker:

# attach to the running IRIS container

docker-compose exec iris bash

# run the script

$ irispython ./python/irisapp.py

3. Criar uma API REST do Flask em Python que funcione com o IRIS

Também há um exemplo de API REST em Python criada com o Flask que você pode usar nos seus aplicativos encontrado em /python/flask/app.py

Para iniciar a API REST do Flask, execute:

$ docker-compose exec iris bash

# irispython /python/flask/app.py

Em seguida, você pode testar o servidor em http://localhost:55030/.

E aqui estão dois exemplos de como adicionar um novo registro no IRIS e ler um registro:

POST http://localhost:55030/persistentclass

Content-Type: application/json

Accept: application/json

{

"test": "toto"

}

Result:

json

{

"id": 1,

"test": "toto"

}


GET http://localhost:55030/persistentclass/1

Accept: application/json

Result:

json

{

"id": 1,

"test": "toto"

}

4. Implantação

A implantação da solução nos sistemas IRIS pode ser realizada por IPM. Para esse fim, temos um module.xml de IPM que entregará os arquivos ObjectScript e Python ao sistema IRIS de destino a partir de um registro de IPM em que você publicará o módulo.

Obrigado, @Guillaume Rongier e @Dmitry Maslennikov, que ajudaram muito a criar o modelo e possibilitar a abordagem "Python puro".

Há abordagens mais "radicais" na organização desses modelos — aqui está a amostra "no dockerfile" e "no objedctscript" de @Dmitry Maslennikov.

Aqui está o modelo "embedded python" de @Guillaume.Rongier7183.

0
0 67
Artigo Danusa Calixto · Set. 1, 2023 4m read

Visão geral

Com o cross-skilling do objectScript do IRIS para o Python, ficam claras algumas diferenças fascinantes na sintaxe.

Uma dessas áreas é como o Python retorna Tuplas de um método com desempacotamento automático.

Efetivamente, isso se apresenta como um método que retorna vários valores. Que invenção incrível :)

out1, out2 = some_function(in1, in2)

O ObjectScript tem uma abordagem diferente com o ByRef e os parâmetros de saída.

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

Onde:

  • inAndOut1 é o ByRef
  • out2 é a saída

O ponto à esquerda (".") na frente do nome da variável passa o ByRef e a saída.

A finalidade deste artigo é descrever como a comunidade do utilitário PyHelper foi otimizada para oferecer uma maneira pythônica de aproveitar o ByRef e os parâmetros de saída. Ele dá acesso a %objlasterror e tem uma abordagem para lidar com o tipo None do Python.
 

ByRef de exemplo

A invocação normal para o embedded python seria:

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

Quando a abertura desse método falha, a variável "oHL7" é uma string vazia.
Na assinatura desse método, há um parâmetro de status disponível para o object script que fornece uma explicação do problema exato.
Por exemplo:

  • O registro pode não existir
  • Não foi possível abrir o registro no modo de simultaneidade exclusiva padrão ("1"), dentro do tempo limite
ClassMethod %OpenId(id As %String = "", concurrency As %Integer = -1, ByRef sc As %Status = {$$$OK}) As %ObjectHandle

O método TupleOut pode ajudar no retorno do valor do argumento sc, de volta a um contexto python.
 

> oHL7,tsc=iris.cls("alwo.PyHelper").TupleOut("EnsLib.HL7.Message","%OpenId",['sc'],1,'er145999', 0)
> oHL7
''
> iris.cls("%SYSTEM.Status").DisplayError(tsc)
ERROR #5809: Object to Load not found, class 'EnsLib.HL7.Message', ID 'er145999'1
```

A lista ['sc'] contém um único item nesse caso. Ela pode retornar vários valores Byref, e na ordem especificada. O que é útil para descompactar automaticamente as variáveis pretendidas do Python.

Exemplo de como lidar com um parâmetro de saída

Código em Python:

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

A string retornada está vazia, mas isso se deve ao elemento realmente estar vazio OU algo que deu errado.
No object script, há também um parâmetro de status de saída (pStatus) que pode ser acessado para determinar essa condição.

Código em object script:

> write oHL7.GetValueAt("&lt;%MSH:9.1",,.pStatus)
''
> Do $System.Status.DisplayError(pStatus)
ERROR &lt;Ens>ErrGeneral: No segment found at path '&lt;%MSH'

Com TupleOut, a funcionalidade equivalente pode ser obtida ao retornar e descompactar ambos o valor de retorno do método E o parâmetro de saída do status.

Código em Python:

> hl7=iris.cls("EnsLib.HL7.Message")._OpenId(145,0)
> val, status = iris.cls("alwo.PyHelper").TupleOut(hl7,"GetValueAt",['pStatus'],1,"&lt;&$BadMSH:9.1")
> val==''
True
> iris.cls("%SYSTEM.Status").IsError(status)
1
> iris.cls("%SYSTEM.Status").DisplayError(status)
ERROR &lt;Ens>ErrGeneral: No segment found at path '&lt;&$BadMSH'1

###Variável especial %objlasterror

No objectscript, há acesso a variáveis de porcentagem no escopo do método.
Há cenários em que detectar ou acessar a variável especial %objlasterror é útil após chamar uma API de terceiros ou CORE
O método TupleOut permite o acesso a %objlasterror, como se tivesse sido definido como um parâmetro de saída, ao invocar métodos do Python

> del _objlasterror

> out,_objlasterror=iris.cls("alwo.PyHelper").TupleOut("EnsLib.HL7.Message","%OpenId",['%objlasterror'],1,'er145999', 0) 

> iris.cls("%SYSTEM.Status").DisplayError(_objlasterror)
ERROR #5809: Object to Load not found, class 'EnsLib.HL7.Message', ID 'er145999'1

Quando None não é uma String

O TupleOut lida com as referências a None do Python como objectscript indefinido. Isso permite que os parâmetros sejam padronizados e os métodos se comportem de forma consistente.
Isso é importante, por exemplo, com %Persistent::%OnNew, em que o método %OnNew não é acionado quando None é fornecido para initvalue, mas seria acionado se uma string vazia fosse fornecida.

No objectscript, a implementação pode dizer:

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

Observe a ausência de variáveis entre vírgulas.

O TupleOut facilita o mesmo comportamento com:

Python:

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

Outra maneira de considerar isso é poder ter uma implementação de uma linha de código de invocação, com um comportamento flexível dependendo da pré-configuração das variáveis:

Object Script:

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

O TupleOut facilita o mesmo comportamento com:

Python:

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

Lista e dicionários

Ao lidar com parâmetros para entrada, ByRef e saída, o TupleOut utiliza o mapeamento automático do PyHelper entre:
Listas do IRIS e do Python
Arrays do IRIS e do Python
Onde é preciso sempre usar strings para representar as chaves do dicionário ao passar de Arrays do IRIS para tipos Dict do Python.

Conclusão

Espero que este artigo ajude a inspirar novas ideias, discussões e sugestões sobre o Embedded Python.

Também espero que incentive a explorar a flexibilidade do IRIS em se adaptar facilmente para enfrentar novos desafios.

0
0 52
Artigo Danusa Calixto · Set. 1, 2023 3m read

O InterSystems IRIS 2022.2 tem um SDK Nativo para Python (https://docs.intersystems.com/iris20222/csp/docbook/Doc.View.cls?KEY=PAGE_python_native).

Sabemos como percorrer uma estrutura de dados global usando a função $Order do ObjectScript do IRIS.

SET key=""FOR  {
     SET key=$ORDER(^myglobal(key)) 
     QUIT:key=""WRITE !,^myglobal(key)
   }

Como fazer o mesmo no Python usando o SDK Nativo do IRIS para Python? Aqui está um exemplo de código:

import iris

args = {'hostname':'127.0.0.1', 'port':51772,
    'namespace':'USER', 'username':'_SYSTEM', 'password':'SYS'
    }

conn = iris.connect(**args)

# Create an iris object
irispy = iris.createIRIS(conn)

# Create a global array in the USER namespace on the server
irispy.set('A', 'root', 'foo', 'SubFoo')

irispy.set(123, 'root', 'bar', 'lowbar', 'UnderBar')
irispy.set(124, 'root', 'bar', 'lowbar', 'UnderBar2')
irispy.set("hi", 'root', 'bar', 'lowbar')
irispy.set("hi again", 'root', 'bar3')

# Read the values from the database and print them
subfoo_value = irispy.get('root', 'foo', 'SubFoo')
underbar_value = irispy.get('root', 'bar', 'lowbar', 'UnderBar')
underbar2_value = irispy.get('root', 'bar', 'lowbar', 'UnderBar2')
lowbar_value = irispy.get('root', 'bar', 'lowbar')
bar3_value = irispy.get('root', 'bar3')

print('Created two values: ')

print('   root("foo","SubFoo")=', subfoo_value)
print('   root("bar","lowbar","UnderBar")=', underbar_value)
print('   root("bar","lowbar","UnderBar2")=', underbar2_value)
print('   root("bar","lowbar")=', lowbar_value)
print('   root("bar3")=', bar3_value)

direction = 0# direction of iteration (boolean forward/reverse)
next_sub = chr(0) # start at first possible subscript
subs = []

print("\n Iterating root \n")

isDef = irispy.isDefined('root', *subs)

while isDef:

    next_sub = irispy.nextSubscript(False, 'root', *subs, next_sub) # get first subscriptif next_sub == None: # we finished iterating nodes on this tree branch, move a level upif len(subs) == 0: # no more things to iteratebreak
        next_sub = subs.pop(-1) # pop last subscript in order to continue iterating this levelif irispy.isDefined('root', *subs, next_sub) == 11:
            print('root(',*subs, next_sub, ')=',irispy.get('root', *subs, next_sub))
            continuecontinue
    isDef = irispy.isDefined('root', *subs, next_sub)

    if isDef in [10, 11]: # keep building subscripts for depth first search
        subs.append(next_sub)
        next_sub = chr(0)
        continueelif isDef == 1: # reached a leaf node, print it
        print('root(',*subs, next_sub, ')=',irispy.get('root', *subs, next_sub))
    else: # def 0 is not really expected
         print("error")
         irispy.kill('root')
         conn.close()
         exit(-1)

        

# Delete the global array and terminate
irispy.kill('root') # delete global array root
conn.close()
0
0 85
Artigo André Dienes Friedrich · Jul. 4, 2023 5m read

Olá, Criei este artigo inicialmente para demonstrar a usabilidade do Python dentro do Iris e sua funcionalidade, no código abaixo trago um exemplo de algo muito próximo que utilizamos para a solução de um problema, nele recebemos uma Global contendo um XML com mais de 7.000.000 de caractéres, onde fazemos as tratativas de limpeza, organização, conversão para um arquivo JSON e retornamos ele como resposta uma Global.

Class AXS.BP.Exemplo Extends (Ens.BusinessProcess, %XML.Adaptor) [ ClassType = persistent 

{

0
0 56
Artigo Henrique Dias · Jun. 10, 2023 9m read

20 anos. 

Agora em 2023 eu completo 20 anos trabalhando com tecnologia, desenvolvendo sistemas, implementando, criando novas soluções e posso afirmar que trabalhar com tecnologia InterSystems fez e faz parte da minha vida. Afinal, foram 18 anos trabalhando todos os dias com essa tecnologia.

Comecei a trabalhar com o Caché 4, lá atrás em 2003, vivenciei as mudanças para o Caché 5, Caché e Ensemble 2008, 2010, 2012, 2014, 2017, 2018 e até finalmente chegarmos no InterSystems IRIS. 

4
0 205
Pergunta Fabio Silva · jan 9, 2023

Olá pessoal,

Estou fazendo alguns testes com Python + IRIS e me deparei com uma situação de retorno de erro.

ClassMethod WhileTest() [ Language = python ]
{
venda input("Registre um produto ou vazio para sair: ")
vendas []

while venda != "":
        vendas.append(venda)
        venda input("\nRegistre um produto ou vazio para sair: ")

print("\nOs produtos cadastrados foram: {}".format(vendas))
}

Ao executar esse código, tenho o retorno de erro abaixo quando sai do while:

3
0 104
Artigo Robert Cemper · Maio 12, 2023 2m read

Seguindo um pacote do último concurso, encontrei um problema estranho.
Havia o requisito para instalar o jupyter-notebook.
Eu trabalho no Windows e havia algum Python antigo instalado.
Nenhuma grande surpresa: a instalação do jupyter-notebook falhou
Portanto, a versão mais recente do Python foi instalada rapidamente.

Grande decepção: a instalação do jupyter-notebook falhou novamente!
Porque a nova instalação do Python não atualizou a antiga.
E também a variável de ambiente PATH não foi limpa
Isso não ficou óbvio imediatamente e exigiu muito tempo e esforço.

0
0 152
Artigo Cristiano Silva · Maio 5, 2023 2m read

Apache Superset é uma plataforma moderna de exploração e visualização de dados. O Superset pode substituir ou trazer ganhos para as ferramentas proprietárias de business intelligence para muitas equipes. O Superset integra-se bem com uma variedade de fontes de dados.

E agora é possível usar também com o InterSystems IRIS.

Uma demo online está disponível e usa IRIS Cloud SQL como sua fonte de dados.

0
0 427
Artigo Heloisa Paiva · Mar. 9, 2023 3m read

Introdução

Em alguns dos últimos artigos, eu falei sobre tipos entre IRIS e Python, e ficou claro que não é tão fácil acessar objetos de um lado pelo outro.

Por sorte, o trabalho já foi feito para criar o SQLAlchemy-iris (clique no link para ver na Open Exchange), o que faz tudo muito mais fácil para o Python acessar os objetos do IRIS, e eu vou mostrar como começar.

Obrigada @Dmitry Maslennikov !

Instalando

Para instalar, simplesmente abra seu terminal com acesso do administrador e digite

pip install sqlalchemy-iris

 

Isso também vai instalar os pré-requisitos, se necessário.

Usando

2
0 1199
Artigo Heloisa Paiva · Fev. 22, 2023 2m read

Porque estou estou escrevendo isso?

Ano passado eu escrevi um artigo para iniciantes para utilizar o Python incorporado. Depois se iniciou uma pequena discussão sobre como retornar valores com Python e achei algumas observações interessantes que valem a pena fazer um artigo. Além disso, espero que possa alcançar mais pessoas assim.

Situações possíveis

Há duas coisas com as quais temos que nos preocupar ao retornar um valor com Python. A primeira é o tipo que você está tentando retornar e a segunda é onde está retornando.

0
0 324
Artigo Danusa Calixto · Out. 27, 2022 15m read

Por que eu amo ObjectScript e por que eu acho que poderia amar Python ainda mais

Eu estava olhando o tópico de mensagens sobre o assunto "Desempenho ao construir uma string separada por vírgulas" e comecei a escrever uma resposta. No entanto, me distraí, a página foi atualizada e perdi meu texto. Não podia gastar tempo reescrevendo minha resposta, então comecei a escrever este documento em vez disso.

Comecei a escrever na linguagem MUMPS no início da minha carreira. Eu escrevia blocos de código bastante concisos e densos em que exercícios como o exemplo da string eram verdadeiros desafios. O desempenho dos servidores VAX ou Digital DEC eram aproveitados até a última gota. Planejávamos onde globais importantes ficariam em um disco. Quando o Caché foi lançado, ainda estávamos trabalhando com M/SQL. Durante um período, estive envolvido em diversas comparações de desempenho entre o Caché e o Oracle, Sybase e SQL Server. Criávamos um esquema de algumas tabelas, preenchidas com milhões de registros, e executávamos várias pesquisas no banco de dados resultante. Eu costumava escrever duas versões de declaração SQL. Uma era uma declaração SQL pura, e a outra era uma consulta personalizada que eu escrevia na definição de classe. A maior parte da lógica está no "Fetch", e eu elaborava esse método para maximizar os indíces definidos e usar ^CachéTemp para qualquer junção complexa de resultados provisórios. Às vezes, eu criava jobs de uma ou mais subconsultas que gerariam os globais temporários e resolvia as junções após a conclusão de todos os processos de jobs. O resultado poderia ser resumido da seguinte maneira:

Inserir dados no banco de dados usando SQL ou Caché Objects sempre foi mais rápido do que qualquer outro DB. Usar COS puro e conjuntos globais diretos era uma ordem de grandeza mais rápida do que SQL, Objects e qualquer outro banco de dados. O banco de dados resultante teria aproximadamente metade do tamanho de um criado por qualquer banco de dados relacional.


Ao comparar o código que escrevi no meu método "Fetch" com o código gerado pelo Caché SQL Engine, vi que usei menos variáveis, 25% menos linhas e o código ficou mais legível.

O número de leituras de blocos de dados físicos seria praticamente o mesmo que o código gerado pelo M/SQL. No entanto, o número de leituras lógicas do pool de buffers globais seria 20% menor que o M/SQL.

Recorri a todos os truques possíveis de desenvolvedores MUMPS. Usei comandos como "execute", "job" (criando threads para lidar com subconsultas em paralelo de forma eficaz), indireção e pós-condições. Recomendamos aos desenvolvedores que não usem esses recursos de linguagem para escrever código legível e capaz de ser mantido por outros desenvolvedores.

Eu inicializaria variáveis desta forma:

set (a,b,c,d)="",(x,y,z)=0,p1=+$h,p2=...,pN=99


Espremi o máximo de expressões possível em uma linha de código. Acreditávamos que a leitura de cada linha de código no "buffer de execução" resultava em um custo. Portanto, o número de linhas de código executadas sempre teve um efeito direto e inverso no desempenho.

Quando trabalho com código escrito por outro desenvolvedor e noto blocos de código que consistem em um comando definido por cada linha, fico um pouco agitado e sempre condenso essas 30 linhas em uma. Me apaixonei por Caché Objects. Vinte e cinco anos depois, esse caso durou mais que dois relacionamentos sérios e um casamento. Definições de classe, com nomes de propriedade precisos e bastante legíveis, indexação de bitmap em tudo, a menos que a indexação de busca possa fazer melhor. Quando possível, relacionamentos pais-filhos em vez de um-muitos. Uso uma chave primária personalizada em tabelas de código quando a indexação de bitmap não é necessária porque set record=$g(^global(code)) sempre será mais rápido que

set record="",rowId=$o(^IndexGlobal("IndexName",code,"")) set:$l(rowId) record=^Global(rowId)

Havia algumas formas de declarações SQL select com que o M/SQL não era compatível ou executava mal. Em geral, o Caché era 2 a 3 vezes mais rápido do que qualquer outro banco de dados.


Ao longo dos anos, o mecanismo SQL melhorou significativamente. A indexação de bitmap e iFind foi lançada. Usamos a indexação iFind nos nomes e endereços de pacientes em um banco de dados de 15 milhões de pessoas. Todos os outros campos são indexados por bitmap. Quando recebemos uma Pesquisa de Paciente de FHIR com vários parâmetros, oferecemos suporte a todos os qualificadores e operadores de especificação FHIR. Construímos uma declaração SQL que começa com uma junção em todas as entidades do paciente de FHIR, que armazenamos em classes persistentes. Estou reivindicando o uso do repositório IRIS for Health para nossa próxima fase de desenvolvimento. O IRIS teve dois lançamentos e amadureceu desde a primeira vez que trabalhei com ele na versão 2019.1. A junção é seguida por qualquer cláusula iFind em Nomes e Endereços, se especificada nos critérios de pesquisa. As cláusulas AND/OR são adicionadas para os campos nos critérios de pesquisa que sabemos serem compatíveis com índices bitmap. As pesquisas determinísticas ou probabilísticas que realizamos são tão rápidas e precisas que ainda me fazem pular de animação (na minha idade!!!).


Preciso confessar que nunca gostei do SQL quando fazia parte de um grupo cada vez menor de desenvolvedores que escreviam código MUMPS no final dos anos 80. Meus colegas foram rápidos em aderir ao Oracle ou SQL Server. Às vezes, era difícil não entrar em um estado de desespero quando ouvia opositores gritando: "O MUMPS está morto".

Então, na conferência anual do MUMPS em Dublin, acordamos em determinada manhã com um bilhete enfiado debaixo das nossas portas anunciando que a InterSystems havia comprado a DTM. Em uma conferência realizada um ano depois em Birmingham, eu estava trabalhando para a InterSystems e estávamos apresentando formulários do Visual Basic usando o Caché de dll que adquirimos quando compramos Data Tree. A Micronetics estava no estande em frente ao nosso, e eles não tinham um dll. O sistema de som deles era mais alto, mas nós havíamos vencido. Levaria mais um ano para comprarmos DSM da Digital e, por fim, MSM da Micronetics. Não havia mais como recuar.  Lembro de mostrar o M/SQL a um cliente em Birmingham que escrevia um software de contabilidade. Um dos seus clientes era o Barings Bank, que havia acabado de perder 859.000.000 GBP devido ao trader desonesto Nick Leeson. Não pude deixar de configurar meu banco de dados de exemplo para poder executar uma consulta SQL que provavelmente não era mais complexa que "SELECT sum(Total) from Accounts WHERE .... and AccountNumber="666..". A conta tinha o mesmo número daquela usada por Nick Leeson para esconder as negociações que estava fazendo para recuperar sua situação, que piorava a cada toque do sino do pregão no mercado de ações de Singapura. Lembro de ficar rindo silenciosamente, em parte pela referência implícita ao colapso do Barings Bank, mas também porque a consulta foi realmente executada, forneceu a resposta correta e não levou mais de um minuto (nenhuma dessas coisas era uma certeza na época).

Essa é a única memória que tenho de gostar do SQL. Eu lidava com vários públicos de DBA do Oracle e SQL Server e demonstrava o Caché, Caché e VB, Caché Objects e Caché SQL. O Caché Objects me encantava: tão elegante, óbvio, maleável e legível. A sintaxe de objetos (em qualquer linguagem) é muito mais natural para mim do que qualquer declaração SQL. Quando tivemos a oportunidade de pegar o esquema do aplicativo de um cliente em potencial, executá-lo através do importador SQL e traduzir o conjunto de procedimentos armazenados que o cliente em potencial incluiria no esquema em Caché Objects ou Caché Globals puro, me familiarizei muito com a leitura do plano de execução SQL e a consulta armazenada gerada. Entrei em longas conversas com Ariel Klausner sobre a consulta SQL que o cliente em potencial me deu e não estava funcionando, e essa seria a diferença entre: ver os DBAs do Oracle saindo da sala de reuniões de volta para a segurança do ajuste do índice e as 6 horas garantidas de inatividade dos sistemas todos os dias durante os backups, onde eles poderiam trazer seus aplicativos relacionais de volta à vida em prontidão para os próximos dias de negociações, ou a emoção de conquistar um cliente que havíamos buscado por meses e estava mais interessado na velocidade do Caché, Orientação de Objetos, gateways .Net ou Java e a elegância simples do CSP de corretagem. Acredito que "por que escrever aplicativos DENTRO de um ambiente de DB?" não é uma pergunta. Primeiro, crio um banco de dados para meu código e outro para meus globais e, ali mesmo, tenho um ponto de separação. Todos nós crescemos nos últimos 25+ anos pensando em Classes, Objetos, ObjectScript e Globais como todos agrupados. Argumento que, no tempo de execução, o código sendo executado no buffer é OBJ. Basicamente, o código OBJ é uma mistura de código C compilado, código de máquina puro otimizado para a plataforma em que está sendo executado e alguns resquícios da definição de classe necessária se você estiver usando $classname, $classmethod, $property e outros fatores. Grande parte do "mecanismo" do Caché ou IRIS é escrito em ObjectScript, provando que essa é uma linguagem perfeita para trabalho. É uma linguagem que pode ser explícita, abreviada e muito compacta. Ela contém todos os operadores e as construções de qualquer linguagem moderna (if, ifelse, else, try - catch, while, for [para ser justo, nossa implementação de FOR é maravilhosa: | for i="apples","pears","Nigel","Fruit" {} | for {} | for i=$$$StartGValue():$$$Increment():$$$EndValue() | ]). Se um dos primeiros criadores do MUMPS tivesse chamado $order de "$next", ele seria imediatamente reconhecível como Next(), conforme encontrado em todas as outras linguagens que iteram por uma array. $PIECE é um pouco peculiar, mas só porque todos os outros bancos de dados usam campos de tamanho fixo. O conceito das strings delimitadas usadas como construção do banco de dados é estranho para um DBA do SQL. Ao analisar o código de máquina compilado de qualquer uma das formas de banco de dados, as instruções se movem pela string, caractere por caractere, e contando o número de caracteres ou fazendo isso enquanto procuram por um delimitador de campo específico.

$list conseguiu um desempenho um pouco melhor do que $piece, mas à custa de um ou dois bytes adicionais no início de cada campo. No entanto, ainda consumiu menos espaço do que os campos de comprimento fixo. O motivo pelo qual todo código de sistema é escrito em ObjectScript até hoje é porque a linguagem é muito eficiente e legível. Além disso, quando os principais desenvolvedores do Caché/IRIS, Scott Jones, Dave McCalldon e Mo Chung, precisavam de algo onde o ObjectScript fosse inadequado, eles escreviam em C e enterravam no Kernal.


Segundo, se eu tiver uma definição de tabela e campos que exigem alguma forma de formatação ou validação em cima e além das restrições óbvias de tipo e comprimento, então quero escrever esse código e mantê-lo bastante próximo à própria definição do campo. Por que eu entraria em outra linguagem, outro ambiente, para escrever essa validação? Os bancos de dados relacionais usam procedimentos e gatilhos armazenados para lidar com essa validação usando a linguagem SQL para expressar a lógica de validação. Se encontrar um programador que prefira usar SQL para escrever lógica complexa em vez de Basic, C#, C++, ObjectScript ou Python, eu compro uma cerveja para você na próxima vez que eu passar por Viena :-)


Na universidade, aprendi a programar em Fortran e Pascal. Pascal era uma linguagem utilizável e perfeitamente legível. Como um jovem de 19 anos que ficava facilmente empolgado, o fato de que o compilador Pascal pudesse ser escrito nessa linguagem me fascinou. Mais tarde, aprendi COBOL. WTF??? No entanto, tenho um amigo que é desenvolvedor da Sage Accounting e escreve nessa linguagem porque a Sage Accounting foi escrita em COBOL. Páginas e páginas da linguagem mais detalhada, ilegível e inutilizável que já vi. Na verdade, o COBOL ainda é muito usado.

Você poderia pensar que Pascal ultrapassaria facilmente COBAL e até Basic. Mas isso não aconteceu. Por quê? É simples. Essa linguagem não era usada nos aplicativos bancários (o COBOL foi amplamente adotado em grandes aplicativos de processamento em lotes de mainframe, como Accounting). Brincávamos dizendo que os bancos não queriam comprar o modelo Caché porque não era sofisticado o suficiente. Não era porque o ObjectScript não conseguisse fazer o processamento transacional desses aplicativos bancários. Nós éramos comprovadamente mais rápidos do que qualquer tecnologia que eles estivessem usando. O problema era que eles haviam gastado muito dinheiro nos sistemas que tinham e no hardware necessário para executar esses Lotes durante a noite para os bancos abrirem às 9h do dia seguinte. As salas caras dos servidores com gás radônio e sistemas de filtragem removem até mesmo as menores partículas de poeira para que não caiam em um disco ou uma fita magnética, causando a perda de um dia inteiro de transações de contas.


Pascal deveria ter vivido mais do que o Basic e talvez isso tivesse acontecido se a Microsoft não houvesse criado o Visual Basic e entrado em competição com Delphi e Borland. O IDE parecia mesmo com o VB, mas usava Pascal em vez de Basic. Tudo isso estava acontecendo enquanto a Microsoft lançava o C#, porque eles tinham que acomodar todos os programadores de C++ e certamente não conseguiriam conquistá-los com o Basic. Eles também estavam ameaçando lançar a própria versão do Java ou remover a compatibilidade com ele, porque o Java rodar em plataformas de hardware em que o Windows nunca seria capaz de rodar era algo que os incomodava. A Microsoft só recuou quando os avanços da tecnologia tornaram o conceito de máquinas virtuais ou containers uma opção de implantação realista. Então, Pascal e Delphi simplesmente desapareceram. Fiz uma pesquisa rápida no Google e há um interpretador de Pascal para Android, então ele ainda existe.

Considerando que Pascal era uma linguagem, ao contrário do Basic, que era apenas uma linguagem de certa forma. No entanto, ele foi usado pela Microsoft para os scripts de aplicativos como o Excel e uma conexão de propriedade com o SQL Server. Isso permitiu vincular dois ambientes que eram intrinsecamente inadequados sem o incômodo de atender aos padrões ODBC e JDBC. Os padrões eram fortemente apoiados pelo Oracle, Sybase e praticamente todos que precisavam fornecer um gateway para as versões proprietárias do SQL. Assim, o Basic sobreviveu. Estou feliz por ter começado minha carreira de programador com o Pascal. Depois, tive uma grande surpresa ao escrever programas COBOL por um ano trabalhando para uma empresa de seguros. Cheguei às costas úmidas e cinzentas do Reino Unido e comecei meu primeiro emprego, que, por acaso, usava MUMPS. Toda a evolução do MUMPS para CachéObjectScript, Objects, Object Gateways, .Net e Java, Caché Basic e MultiValueBasic e agora Python. O Python fecha um ciclo e, em certo sentido, prova que o ObjectScript não é uma aberração em uma tecnologia de banco de dados não relacional rejeitada.


Caché Globals são arrays esparsas multidimensionais tão convenientes para a própria natureza dos dados de saúde que não importa o esforço do Oracle e da Microsoft em consumir esse espaço no mercado. Ainda que tenham matado Pascal e Fortran, e até Basic, eles não conseguiram matar a InterSystems. Lembro de participar de um seminário do Oracle sobre "Oracle para a Saúde". A apresentadora falava sobre o Oracle na área da saúde que, ela nos garantiu, dominaria o mercado da saúde de uma vez por todas. Levantei minha mão e perguntei: "Não é isso o que vocês afirmam em todos os grandes lançamentos há anos? Vocês fracassaram antes. Por que fariam melhor desta vez?" Ela olhou para mim e perguntou, "Quem é você?". Respondi: "Sou da InterSystems. Dominamos o mercado da saúde há 35 anos. Conseguimos isso porque nossa tecnologia nasceu no Massachusetts General Hospital. E adivinhe. Eles ainda executam os sistemas centrais nas nossas tecnologias". Nessa hora, dois seguranças corpulentos me tiraram do auditório.


Então, o Oracle com o pSQL tem o Java. A Microsoft tem o SQL Server, C# e tSQL e, quando você precisa interagir com o Java, fica sujeito ao JDBC. Da mesma forma, com Java, se você precisa conversar com tabelas do SQL Server, é obrigado a usar ODBC. Onde ficamos? Bem, chegamos à grande ideia de ter wrappers para .Net e Java. Ao usar o ObjectScript, eu instancio uma instância da Classe A. Na verdade, não importa se a Classe A é uma classe .Net, Java ou ObjectScript, porque eu instancio esses objetos usando exatamente a mesma sintaxe em todos os casos. Depois, invoco os métodos de classe ou instância para manipular os objetos. O interior desses métodos não é importante, porque a sintaxe para interagir com essas classes e os métodos é basicamente idêntica, independentemente do que elas contenham.

Junto vem o Python, que compartilha vários recursos com o ObjectScript, pois é uma linguagem interpretada, e não compilada. É bastante legível e utilizável. Assim como o Caché ObjectScript encontrou um nicho nos dados não estruturados, o Python encontrou um nicho no mundo da modelagem matemática, ML, IA e muito, muito mais. Esse não é um mundo em que C# ou Java se sintam particularmente confortáveis. Aliás, nem o ObjectScript. Assim, a InterSystems foca em fornecer funcionalidades cada vez mais poderosas para manipular grandes quantidades de dados não estruturados, além de lançar iFind e iKnow, algumas técnicas de indexação muito inteligentes e algoritmos de correspondência de probabilidade. Então, você convida o Python para se aconchegar nas nossas arrays esparsas multidimensionais, trazendo milhões de bebês .py que fazem praticamente tudo o que você precisa. É a combinação perfeita. Por precaução, esqueci de mencionar que várias arquiteturas que dominam o mundo do desenvolvimento de páginas da Web são completamente baseadas em JS (Angular.js, REACT.js, Vue.js, Bootstrap — certo, não há JS, mas ele está em tudo menos no nome — e Node.js) e Arrays de JS. O JS não vai desaparecer tão cedo. No entanto, será interessante ver o que vai acontecer com a Golang, se é que você me entende. Vi entradas baseadas em arrays de JS nas últimas competições de código. Se existe uma tecnologia que entende arrays melhor do que qualquer outra, é a IRIS.


Penso naqueles dias, sentado no meu escritório na empresa onde trabalhava no coração de Londres. Em determinado momento, a empresa estava cheia de programadores MUMPS, mas ela os transformou em programadores SQL relacionais, que depois se tornaram redundantes. Lembro da sensação de começar a questionar se minha fé no MUMPS ser simplesmente a melhor linguagem que já havia encontrado poderia estar errada. A linguagem e as empresas que construíram interpretações dessa linguagem morreriam. Isso me deixou muito triste porque, até então, havia aprendido cinco outras linguagens de programação (APL, Basic, Fortran, COBOL e Pascal) antes de descobrir o MUMPS, e o MUMPS era tão simples. Fácil de escrever, ler e implantar. Em outras palavras, era tão natural para mim quanto o inglês, e tinha um ritmo parecido com os hinos que cantávamos na escola metodista que frequentei:

Onward, Christian soldiers!

Marching as to war,

With the cross of Jesus

Going on before.

Christ, the royal Master,

Leads against the foe;

Forward into battle,

See his banners go!

Mas ele não morreu. A música mudou um pouco:

 

A bordo, Nigel Saaalllm

Voando para a guerra

Com seu cartão de crédito internacional

Indo à frente.

John, o Mestre, McCormick

Lidera contra o inimigo (Microsoft)

Avançando na batalha

Veja seus duty frees partindo

CacheObjectScript era ainda melhor que o MUMPS se isso era possível. CacheObjects parecia tão legal quando demonstrado para uma audiência pelas primeiras vezes, e CacheSQL deixou seus dias de M/SQL para trás e melhorou muito ao longo dos anos. Ainda assim, eu particularmente não gosto de escrever muito em SQL, mas encontrei um bom equilíbrio entre Objects, SQL e referências globais diretas conforme relaxei. Meu código era bastante orientado para as referências globais diretas, com um pouco de OO e o mínimo de SQL. Quando vi com os produtos que o código gerado era compacto, elegante, eficiente e legível, o equilíbrio mudou novamente. Agora, uso nomes globais diretos raramente, muito Objects e uma quantidade razoável de SQL.

Para trabalhar com Python, minha mente precisará ver padrões diferentes do meu código ObjectScript. Há muitos "abc" e outras estruturas estranhas. No entanto, depois de escrever algumas páginas de código py e me afastar, como faço ao pintar a óleo, os padrões saltarão. Assim como vejo a música como sinestesia de cores, meus programas py codificados por cores fluindo pela página também começarão a parecer aquarela ou até mesmo uma pintura a óleo pesada. Ficarei encantado, e estará tudo bem.

1
0 174
Artigo Danusa Calixto · Out. 18, 2022 17m read

Olá a todos, sou um estudante francês que acabou de chegar em Praga para um intercâmbio acadêmico no meu quinto ano da faculdade de engenharia e aqui está minha participação no concurso de interoperabilidade.

Não tive muito tempo para programar desde a mudança da França para Praga e estou participando sozinho, então decidi criar um projeto que é mais um modelo do que um aplicativo.

Queria participar já que minha área (Ciência de Dados e IA) não é geralmente associada à sustentabilidade, e o concurso era uma maneira de me expressar nesse tema importante que é a sustentabilidade e o meio ambiente.

Como você sabe, a Inteligência Artificial está cada vez mais popular, com várias firmas conhecidas tentando seguir o movimento e vender ferramentas para criar, treinar e usar modelos de machine learning com facilidade. Tudo isso é prático e fácil, mas tem um custo, financeiro e também ambiental.

Treinar modelos enormes repetidamente pode exigir muitos recursos e produzir uma grande quantidade de CO2. Os supercomputadores são executados durante vários dias, e o período e o tamanho dos modelos estão aumentando exponencialmente, tomando mais espaço do que nunca. Todo esse esforço para ter alguma melhoria no desempenho, que em alguns casos não é nem garantida.

É claro que várias firmas precisam desse processo, em que até mesmo 0,1% de melhoria na precisão de um modelo pode salvar milhares de vidas. Por isso, esse modelo foi criado para usos mais comuns.

No entanto, como tive a oportunidade de trabalhar com Processamento de Linguagem Natural (PLN) ou Classificação de Imagens, percebi que alguns modelos e ferramentas já são quase utilizáveis no estado em que se encontram e podem nos ajudar a economizar centenas de horas de treinamento e, consequentemente, emissões de CO2 e consumo de eletricidade.

Por isso, decidi criar um modelo usando as tecnologias da InterSystems para desenvolver uma solução interoperável que resolvesse alguns problemas de sustentabilidade ao permitir facilmente que você, em alguns cliques, faça o download único de modelos pré-treinados da Internet, use de acordo com sua própria necessidade e, claro, ajuste esses modelos pré-treinados com novo conteúdo disponível na base de dados IRIS e adicione conteúdo ao modelo existente.

Dessa forma, no modelo, estamos pegando um modelo PLN, testando e treinando com dados para criar cinco novos rótulos no modelo para classificar a avaliação da internet.

Portanto, ao fazer isso, o resultado (se você tiver tempo e algum poder computacional) é um ótimo modelo que pode ser usado para prever a nota de avaliação da internet, que não tem custo e emite uma quantidade pequena de CO2.

Consulte o GitHub e a postagem do Open Exchange vinculada a este artigo.

Ou veja o ReadMe aqui:

1. Contest-Sustainability

Este modelo usa as tecnologias da InterSystems para desenvolver uma solução interoperável e resolver alguns problemas de sustentabilidade ao permitir facilmente que você, em alguns cliques, faça o download único de modelos pré-treinados da Internet, use de acordo com sua própria necessidade e, claro, ajuste esses modelos pré-treinados com novo conteúdo disponível na base de dados IRIS e adicione conteúdo ao modelo existente.

Neste exemplo, estamos pegando um modelo PLN, testando e treinando com dados para criar cinco novos rótulos no modelo para classificar a avaliação da internet. No processo, poupamos uma grande quantidade de recursos e emissões de CO2.

Veja alguns modelos de exemplo que você pode testar: https://huggingface.co/gpt2
https://huggingface.co/Jean-Baptiste/camembert-ner
https://huggingface.co/bert-base-uncased
https://huggingface.co/facebook/detr-resnet-50
https://huggingface.co/facebook/detr-resnet-50-panoptic

ÍNDICE:

2. Instalação

2.1. Início da produção

Na pasta "contest-sustainability", abra um terminal e insira:

docker-compose up

Na primeira vez, talvez leve alguns minutos para o build correto da imagem e a instalação de todos os módulos necessários para o Python.

2.2. Acesso à produção

Seguindo este link, acesse a produção: Acessar a produção

2.3. Encerramento da produção

docker-compose down

3. Como funciona

Por enquanto, alguns modelos talvez não funcionem com essa implementação, já que tudo é feito automaticamente, ou seja, não importa o modelo de entrada, tentaremos fazer com que funcione usando a biblioteca transformerspipeline.

Pipeline é uma ferramenta poderosa da equipe HuggingFace que analisa a pasta em que o modelo foi transferido e entende qual biblioteca deve usar entre PyTorch, Keras, Tensorflow ou JAX. Em seguida, ela carrega esse modelo usando AutoModel.
Então, ao inserir a tarefa, o pipeline sabe o que fazer com o modelo, tokenizer ou até extrator de características nessa pasta e gerencia a entrada automaticamente, tokeniza, processa, transfere para o modelo e retorna um resultado decodificado que podemos usar diretamente.

4. API HuggingFace

Algumas pessoas ou sistemas não conseguem fazer o download de modelos ou usá-los devido a restrições. Por isso, é possível usar a API HuggingFace e chamar alguns modelos diretamente através desse serviço.
Veja uma explicação mais simples:

Primeiro, você precisa iniciar a demonstração, usando o botão verde Start, ou use Stop e Start novamente para aplicar as mudanças nas configurações.

Em seguida, ao clicar na operação Python.HFOperation escolhida e selecionar na guia à direita action, você pode aplicar test à demonstração.

Na janela test, selecione:

Tipo de solicitação: Grongier.PEX.Message

Em classname, insira:

msg.HFRequest

Para json, veja um exemplo de uma chamada para GPT2:

{
    "api_url":"https://api-inference.huggingface.co/models/gpt2",
    "payload":"Can you please let us know more details about your ",
    "api_key":"----------------------"
}

Agora, você pode clicar em Visual Trace para ver nos detalhes o que aconteceu e visualizar os registros.

OBSERVE que você precisa ter uma chave de API do HuggingFace antes de usar esta Operação (as chaves de API são gratuitas, basta fazer a inscrição no HF)

OBSERVE que você pode mudar o URL para testar outros modelos do HuggingFace. Talvez seja necessário mudar o payload.

Veja este exemplo:
envio de hf reqhf reqhf resp

5. Use qualquer modelo da Web

Nesta seção, vamos ensinar você a usar praticamente qualquer modelo pré-treinado da internet, HuggingFace ou não, para poupar recursos ou simplesmente usar esses modelos dentro do IRIS.

5.1. PRIMEIRO EXEMPLO: VOCÊ TEM SEU PRÓPRIO MODELO

Nesse caso, você precisa copiar e colar seu modelo, com config, tokenizer.json etc. dentro de uma pasta na pasta do modelo.
Caminho: src/model/yourmodelname/

Em seguida, você precisa criar uma nova operação, chame-a como quiser e acesse os parâmetros dessa operação.
Então, acesse settings na guia à direita, na parte Python e na parte %settings. Aqui, você pode inserir ou modificar quaisquer parâmetros (não se esqueça de pressionar apply depois de terminar).
Veja a configuração padrão para esse caso:
%settings

name=yourmodelname
task=text-generation

OBSERVE que qualquer configuração que não for name ou model_url entrará nas configurações PIPELINE.

Agora você pode clicar duas vezes na operação e executar o start. Você precisa ver na parte Log a inicialização do modelo.

Em seguida, criamos um PIPELINE utilizando transformers que usam o arquivo config na pasta como vimos antes.

Para chamar esse pipeline, clique na operação e selecione na guia à direita action. Você pode aplicar test à demonstração.

Na janela test, selecione:

Tipo de solicitação: Grongier.PEX.Message

Em classname, insira:

msg.MLRequest

Para json, você precisa inserir todos os argumentos necessários para o modelo.
Veja um exemplo de uma chamada para GPT2:

{
    "text_inputs":"Unfortunately, the outcome",
    "max_length":100,
    "num_return_sequences":3
}

Clique em Invoke Testing Service e aguarde a operação do modelo.

Veja este exemplo:
envio de ml req

Agora, você pode clicar em Visual Trace para ver nos detalhes o que aconteceu e visualizar os registros.

Veja este exemplo :
ml req

ml resp

5.2. SEGUNDO EXEMPLO: VOCÊ QUER FAZER O DOWNLOAD DE UM MODELO DO HUGGINGFACE

Nesse caso, você precisa encontrar o URL do modelo no HuggingFace.
Encontre um modelo que faça o que você busca e use-o sem gastar recursos utilizando as tecnologias da InterSystems.

5.2.1. Configurações

Vá até os parâmetros do Hugging.
Clique na operação HuggingFace escolhida e acesse settings na guia à direita, na parte Python e na parte %settings. Aqui, você pode inserir ou modificar quaisquer parâmetros (não se esqueça de pressionar apply depois de terminar).
Veja um exemplo de configuração para alguns modelos que encontramos no HuggingFace:

%settings para gpt2

model_url=https://huggingface.co/gpt2
name=gpt2
task=text-generation

%settings para camembert-ner

name=camembert-ner
model_url=https://huggingface.co/Jean-Baptiste/camembert-ner
task=ner
aggregation_strategy=simple

%settings para bert-base-uncased

name=bert-base-uncased
model_url=https://huggingface.co/bert-base-uncased
task=fill-mask

%settings para detr-resnet-50

name=detr-resnet-50
model_url=https://huggingface.co/facebook/detr-resnet-50
task=object-detection

%settings para detr-resnet-50-protnic

name=detr-resnet-50-panoptic
model_url=https://huggingface.co/facebook/detr-resnet-50-panoptic
task=image-segmentation

OBSERVE que qualquer configuração que não for name ou model_url entrará nas configurações PIPELINE. Então, no segundo exemplo, o pipeline camembert-ner requer a especificação de aggregation_strategy e task, enquanto gpt2 requer apenas uma task.

Veja este exemplo:
configurações ml ope2

Agora você pode clicar duas vezes na operação e executar o start.
Você precisa ver na parte Log a inicialização e o download do modelo.
OBSERVAÇÃO: Você pode atualizar os registros a cada x segundos para ver o avanço dos downloads. dl em tempo real

Em seguida, criamos um PIPELINE utilizando transformers que usam o arquivo config na pasta como vimos antes.

5.2.2. Teste

Para chamar esse pipeline, clique na operação e selecione na guia à direita action. Você pode aplicar test à demonstração.

Na janela test, selecione:

Tipo de solicitação: Grongier.PEX.Message

Em classname, insira:

msg.MLRequest

Para json, você precisa inserir todos os argumentos necessários para o modelo.
Veja um exemplo de uma chamada para GPT2:

{
    "text_inputs":"George Washington lived",
    "max_length":30,
    "num_return_sequences":3
}

Aqui está um exemplo de uma chamada para Camembert-ner:

{
    "inputs":"George Washington lived in washington"
}

Aqui está um exemplo de uma chamada para bert-base-uncased:

{
    "inputs":"George Washington lived in [MASK]."
}

Aqui está um exemplo de uma chamada para detr-resnet-50 usando um URL online:

{
    "url":"http://images.cocodataset.org/val2017/000000039769.jpg"
}

Aqui está um exemplo de uma chamada para detr-resnet-50-panoptic usando um URL como caminho:

{
    "url":"/irisdev/app/misc/000000039769.jpg"
}

Clique em Invoke Testing Service e aguarde a operação do modelo.
Agora, você pode clicar em Visual Trace para ver nos detalhes o que aconteceu e visualizar os registros.

OBSERVE que, após fazer pela primeira vez o download de um modelo, a produção não fará o download novamente, mas usará os arquivos em cache encontrados em src/model/TheModelName/.
Se alguns arquivos estiverem ausentes, a Produção fará o download novamente.

Veja este exemplo:
envio de ml reqml reqml resp

Veja este exemplo:
envio de ml reqml resp

6. Ajuste dos modelos

Nesta parte, tentamos ajustar um modelo para reaproveitá-lo e torná-lo ainda melhor sem usar muitos recursos.

6.1. Personalização do modelo

6.1.1. Download do modelo

Para utilizar esse GitHub, você precisa ter um modelo do HuggingFace compatível com pipeline para usar e treinar, além de um conjunto de dados para treinar seu modelo.

Para ajudar, oferecemos a possibilidade de usar script Python em src/utils/download_bert.py. Ele fará o download para você do modelo "https://huggingface.co/bert-base-cased" e colocará dentro da pasta src/model/bert-base-cased se já não estiver lá.
Além disso, também disponibilizamos um conjunto de dados para treinar o modelo bert. Esse conjunto de dados já estava carregado dentro da base de dados IRIS e nenhuma ação adicional é necessária se você quiser usá-lo. (Para acessá-lo, vá até a parte SQL do portal, o namespace da base de dados iris e depois a tabela de revisão)

Para usar o script, se você estiver no contêiner, pode executá-lo sem se preocupar. Se você estiver no local, talvez seja necessário aplicar pip3 install requests e pip3 install beautifulsoup4

Veja o resultado: Download do resultado

6.1.2. Configurações

Se você quiser usar o modelo bert-base-cased e já fez o download usando o script, não é necessário adicionar mais nada às configurações, e você pode avançar para o treinamento do modelo.

Se você quiser treinar seu próprio modelo, clique em Python.TuningOperation e selecione Settings na guia à direita, depois Python e, na parte %settings, insira o caminho do modelo, o nome da pasta e o número do rótulo que você quer para o treinamento.

Exemplo:

path=/irisdev/app/src/model/
model_name=bert-base-cased
num_labels=5

6.1.3. Treinamento do modelo

Para treinar o modelo, você precisa acessar Production neste link:

http://localhost:52795/csp/irisapp/EnsPortal.ProductionConfig.zen?PRODUCTION=iris.Production

E conectar usando:
SuperUser como nome de usuário e SYS como senha.


Para chamar o treinamento, clique em Python.TuningOperation e selecione na guia à direita Actions. Você pode aplicar Test à demonstração.

Na janela de teste, selecione:

Tipo de solicitação: Grongier.PEX.Message

Em classname, insira:

msg.TrainRequest

Para json, você precisa inserir todos os argumentos necessários para o treinamento. Aqui está um exemplo que treina com as primeiras 20 linhas (não é um treinamento adequado, mas é rápido):

{
    "columns":"ReviewLabel,ReviewText",
    "table":"iris.Review",
    "limit":20,
    "p_of_train":0.8,

    "output_dir":"/irisdev/app/src/model/checkpoints",
    "evaluation_strategy":"steps",
    "learning_rate":0.01,
    "num_train_epochs":1
}

Veja este exemplo: Pedido de treinamento

Como pode ver, é preciso inserir

  • a table usada.
  • as columns usadas (primeiro é label e segundo é input para a tokenização)
  • o limit de linhas compreendidas (se você não especificar um número de linhas, todos os dados serão usados)
  • p_of_train, a porcentagem de dados de treinamento usados do conjunto de dados, e 1 - p_of_train, a porcentagem de dados de teste usados do conjunto de dados.

Depois disso, os outros parâmetros cabem a você e podem variar de acordo com os parâmetros https://huggingface.co/docs/transformers/main_classes/trainer.

OBSERVE que o tamanho do lote para treinamento e teste será calculado automaticamente se não for inserido na solicitação. (É o maior divisor do número de linhas que é menor do que a raiz quadrada do número de linhas e do que 32)

Clique em "Invoke Testing Service" e feche a janela de teste sem esperar.
Agora acesse Python.TuningOperation e selecione na guia à direita log. Aqui você pode ver o avanço do treinamento e das avaliações.
Após a conclusão, você verá um log dizendo que o novo modelo foi salvo em uma pasta temporária.
Agora acesse Python.TuningOperation, selecione na guia à direita message e clique no cabeçalho do último item para selecioná-lo. Aqui você pode ver o avanço do treinamento e das avaliações e, ao final, é possível acessar as Métricas do modelo novo e antigo para comparação.

6.1.4. Substituição do modelo

Se você quiser manter o modelo antigo, nenhuma ação é necessária: o antigo permanecerá na pasta não temporária e ainda será carregado para treinamento adicional.

Se você quiser manter o modelo novo, clique em Python.TuningOperation, selecione na guia à direita Actions e teste. Na janela de teste, selecione:

Tipo de solicitação: Grongier.PEX.Message

Em classname, insira:

msg.OverrideRequest

Para json, chaves vazias:

{}

Clique em Invoke Testing Service e veja a mensagem de resposta. O novo modelo foi movido da pasta temporária para a não temporária.

6.2. Uso do modelo

Treinar um modelo é interessante, mas você também pode testá-lo.

6.2.1. Configurações

Se você quiser usar o modelo bert-base-cased e já fez o download usando o script, não é necessário adicionar mais nada às configurações, e você pode avançar para o teste do modelo.

Se você quiser treinar seu próprio modelo, clique em Python.TuningOperation e selecione Settings na guia à direita, depois Python e, na parte %settings, insira o parâmetro para adicionar ao pipeline.

6.2.2. Teste do modelo

Para testar o modelo, você precisa acessar Production neste link:

http://localhost:52795/csp/irisapp/EnsPortal.ProductionConfig.zen?PRODUCTION=iris.Production

E conectar usando:
SuperUser como nome de usuário e SYS como senha.


Para chamar o teste, clique em Python.MLOperation e selecione na guia à direita Actions. Você pode aplicar Test à demonstração.

Na janela de teste, selecione:

Tipo de solicitação: Grongier.PEX.Message

Em classname, insira:

msg.MLRequest

Para json, você precisa inserir todos os argumentos necessários para o modelo funcionar

{
    "inputs":"This was a really bad experience"
}

Pressione Call test services e veja o resultado.

7. Observação importante

O ajuste fino de modelos pode exigir MUITO tempo e recursos, mas sempre consumirá menos recursos do que o treinamento de um modelo do zero.
Você pode ver que já está levando muito tempo e poder computacional para o ajuste fino do modelo, então imagine o custo se tivesse que treiná-lo do zero e começar de novo várias vezes para obter resultados ideais.

8. Solução de problemas

Se você tiver problemas, a leitura é o primeiro conselho que podemos dar a você. A maioria dos erros são facilmente compreendidos apenas ao ler os logs, pois quase todos os erros são capturados por um try/catch e registrados.

Se você precisar instalar um novo módulo ou dependência do Python, abra um terminal dentro do contêiner e digite, por exemplo: "pip install new-module"
Há várias formas de abrir um terminal,

  • Se você usa os plugins da InterSystems, pode clicar na barra abaixo no VSCode, que se parece com docker:iris:52795[IRISAPP], e selecionar Open Shell in Docker.
  • Em qualquer terminal local, digite: docker-compose exec -it iris bash
  • No Docker-Desktop, encontre o contêiner IRIS e clique em Open in terminal

Alguns modelos talvez exijam algumas alterações no pipeline ou nas configurações, por exemplo. É sua responsabilidade adicionar as informações corretas nas configurações e na solicitação.

9. Conclusão

Daqui em diante, você poderá usar modelos de que talvez precise no IRIS e ajustá-los como desejar.
Esse modelo precisa ser modificado para atender às suas necessidades e foi criado como base para qualquer projeto de IA e IRIS que considere a sustentabilidade e interoperabilidade.

Por falta de tempo, não consegui adicionar uma API que usasse um Diretor para se comunicar diretamente com a produção e permitir que os usuários fizessem solicitações aos modelos.
No entanto, se você ainda tiver interesse em uma API IRIS usando esse módulo do Python, confira meu GitHub ou acesse meu exemplo de API no Python para IRIS.

Link para meu perfil do DC: https://community.intersystems.com/user/lucas-enard-0

0
0 71
Artigo Heloisa Paiva · Set. 22, 2022 5m read

Aqui você vai encontrar um programa simples que usa Python em um ambiente de desenvolvimento IRIS e outro programa simples que sua ObjectScript em um ambiente de desenvolvimento Python. Além disso, gostaria de compartilhar alguns dos problemas que tive enquanto aprendia a implementar esses códigos.

Python em ambiente IRIS

Digamos, por exemplo, que você está desenvolvendo no IRIS e tem um problema que acha mais fácil ou mais eficiente de se resolver com Python.

0
0 376
Artigo Danusa Calixto · Set. 14, 2022 3m read

O SDK Nativo para Python da InterSystems é uma interface leve de APIs do InterSystems IRIS que antes estavam disponíveis somente por ObjectScript.

Estou especialmente interessado na capacidade de chamar métodos ObjectScript ou class methods, para ser preciso. Funciona muito bem, mas, por padrão, as chamadas só são compatíveis com argumentos escalares: strings, booleanos, inteiros e floats.

No entanto, se você quiser:

  • Transmitir ou retornar estruturas, como dicionários ou listas
  • Transmitir ou retornar streams

Você precisará escrever glue code ou usar este projeto (instalação com pip install edpy). O pacote edpy fornece uma simples assinatura:

call(iris, class_name, method_name, args)

que permite chamar qualquer método ObjectScript e receber resultados de volta. Faça a importação assim:

from edpy import iris

call aceita 4 argumentos obrigatórios:

  • iris — uma referência a um objeto IRIS estabelecido
  • class_name — classe IRIS para chamar
  • method_name — método IRIS para chamar
  • args — lista de 0 ou mais argumentos

Argumentos

Cada argumento pode ser um destes:

  • string (qualquer comprimento, se for maior do que $$$MaxStringLength or 3641144 símbolos, é automaticamente convertida em um stream)
  • booleano
  • inteiro
  • float
  • dict (convertido em um objeto dinâmico)
  • lista ou tupla (convertidas em array dinâmica)

os argumentos dicionário, lista e tupla podem conter recursivamente outros dicionários, listas e tuplas (enquanto houver memória).

Valor de retorno

Em retorno, esperamos um objeto/array dinâmico ou um stream/string JSON. Nesse caso, edpy primeiro converteria em uma string em Python e, se possível, interpretaria como um dicionário ou uma lista em Python. Caso contrário, o resultado seria retornado ao autor da chamada da mesma maneira.

É basicamente isso, mas veja alguns exemplos de métodos ObjectScript e como chamá-los usando essa função em Python.

Exemplo 1: Pong

ClassMethod Test(arg1, arg2, arg3) As %DynamicArray
{
    return [(arg1), (arg2), (arg3)]
}

Chame com:

>>> iris.call(iris_native, "User.Py", "Test", [1, 1.2, "ABC"])
[1, 1.2, 'ABC']

Nenhuma surpresa aqui. Os argumentos são colocados de volta em uma array e retornados ao autor da chamada.

Exemplo 2: Propriedades

ClassMethod Test2(arg As %DynamicObject) As %String
{
    return arg.Prop
}

Chame desta maneira:

>>> iris.call(iris_native, "User.Py", "Test2", [{"Prop":123}])
123

Agora para uma invocação mais incorporada:

>>> iris.call(iris_native, "User.Py", "Test2", [{"Prop":{"Prop2":123}}])
{'Prop2': 123}

Se uma propriedade for muito longa, também não tem problema — os streams serão usados para enviá-la ao IRIS e/ou de volta:

ret = iris.call(iris_native, "User.Py", "Test2", [{"Prop":"A" * 10000000}])
>>> len(ret)
10000000

Se você precisar de streams garantidos no lado do InterSystems IRIS, você pode usar %Get:

set stream = arg.%Get("Prop",,"stream")

Se o stream for codificado em base64, você pode decodificá-lo com:

set stream = arg.%Get("Prop",,"stream<base64")

Exemplo 3: String ou Stream

ClassMethod Test3(arg As %Stream.Object) As %String
{
    set file = ##class(%Stream.FileCharacter).%New()
    set file.TranslateTable = "UTF8"
    set filename = ##class(%File).ManagerDirectory() _ "test.txt"
    do file.LinkToFile(filename)
    if $isObject(arg) {
        set sc = file.CopyFromAndSave(arg)
    } else {
        do file.Write(arg)
        set sc = file.%Save()
    }
    if $$$ISERR(sc) {
        set jsonret = {"status":0, "payload":($system.Status.GetErrorText(sc))}
    } else {
        set jsonret = {"status":1}
    }
    quit jsonret.%ToJSON()
}

Aqui escrevemos uma string ou um stream para <mgr>test.txt.

>>> iris.call(iris_native, "User.Py", "Test3", ["&#x1f60a;"])
{'status': 1}

Observação: em todas as amostras de código, "&# x1f642;" é inserido como 😊.

E se eu abrir o arquivo e ver um 😊 e não dois ?? — então, a codificação é preservada.

>>> iris.call(iris_native, "User.Py", "Test3", ["&#x1f642;" * 10000000])
{'status': 1}

Omitirei o resultado do arquivo para ser breve, mas ele existe.

Por fim, ao transmitir uma array ou um objeto dinâmico dentro, você consegue evitar completamente a dicotomia string/stream, mesmo se não souber se a propriedade seria mais curta ou longa do que o limite da string. Nesse caso, você sempre pode obter a propriedade suspeita como um stream.

Exemplo 4: Retornar streams

ClassMethod Test4(arg As %DynamicArray) As %String
{
    return arg.%Get(0)
}

Veja como isso funciona:

>>> ret = iris.call(iris_native, "User.Py", "Test4", [["&#x1f60a;" * 10000000]])
>>> len(ret)
10000000
>>> ret[:5]
'&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;'

Mais uma coisa

Também há uma função get_iris(ip="localhost", port=1972, namespace="USER", username="_SYSTEM", password="SYS") para obter um objeto IRIS que funciona. Veja um exemplo completo, se quiser tentar por conta própria:

Primeiro, carregue a classe User.Py e instale a biblioteca Python edpy:

pip install edpy

Em seguida, na chamada do Python:

from edpy import iris
iris_native = iris.get_iris()
iris.call(iris_native, "User.Py", "Test",  [1, 1.2, "ABC"])
iris.call(iris_native, "User.Py", "Test2", [{"Prop":123}])
iris.call(iris_native, "User.Py", "Test2", [{"Prop":{"Prop2":123}}])
ret2 = iris.call(iris_native, "User.Py", "Test2", [{"Prop":"A" * 10000000}])
iris.call(iris_native, "User.Py", "Test3", ["&#x1f60a;"])
iris.call(iris_native, "User.Py", "Test3", ["&#x1f60a;" * 10000000])
ret4 = iris.call(iris_native, "User.Py", "Test4", [["&#x1f60a;" * 10000000]])

Conclusão

O SDK Nativo para Python é uma ferramenta poderosa, que fornece acesso completo e irrestrito ao InterSystems IRIS. Com esse projeto, espero que seja possível poupar tempo com o empacotamento das chamadas do InterSystems IRIS. Alguma combinação de argumentos de métodos não é compatível? Se não for, compartilhe nos comentários como você faz a chamada.

Links

0
0 64