Feedback : Usando Python embutido diariamente por mais de dois anos
Há 2 anos eu venho utilizando Python embutido diariamente. Talvez seja o momento de compartilhar um feedback sobre essa jornada.
Por que escrever esse feedback? Porque eu acredito que sou como a maioria das pessoas aqui, um desenvolvedor ObjectScript, e penso que a comunidade poderia ter algum benefício desse feedback e entender melhor os prós e contras de escolher Python embutido para desenvolver em IRIS. Além de evitar algumas armadilhas.

- Feedback : Usando Python embutido diariamente por 2 anos
- Introdução
- Começando com Python
- Python Embutido
Introdução
Eu sou desenvolvedor desde 2010 e trabalho com ObjectScript desde 2013.
Então são aproximadamente 10 anos de experiência com ObjectScript.
Desde 2021 e o lançamento do Python Embutido no IRIS, eu me propus um desafio:
- Aprender Python
- Fazer tudo o máximo possível com Python
Quando eu comecei essa jornada, eu não tinha ideia do que era Python. Então, comecei com o básico e ainda estou aprendendo todo dia.
Começando com Python
O lado bom do Python é que é fácil de aprender. É ainda mais fácil se você já conhece ObjectScript.
Por quê? Elas têm muito em comum.
| ObjectScript | Python |
|---|---|
| Não tipada | Não tipada |
| Scripting language | Scripting language |
| Orientada a Objeto | Orientada a Objeto |
| Interpretada | Interpretada |
| Fácil integração com C | Fácil integração com C |
Então, se você conhece ObjectScript você já sabe muito sobre Python.
Porém, há algumas diferenças, dentre as quais algumas não são fáceis de entender.
Python não é ObjectScript
Para manter as coisas simples, vou focar nas principais diferenças entre ObjectScript e Python.
Para mim, há 3 principais diferenças:
- Pep8
- Módulos
- Dunders
Pep8
O que é Pep8 ?
É um conjunto de regras para escrever códigos Python.
Algumas delas são:
- Convenção de nomenclatura
- nomes de variáveis
- snake_case
- nomes de classes
- CamelCase
- indentação
- comprimento da linha
- etc.
Por que isso é importante?
Porque é a maneira de escrever código Python. Se você não seguir essas regras, terá dificuldade em ler o código de outras pessoas e elas terão dificuldade de ler o seu.
Como desenvolvedores ObjectScript, também temos algumas regras para seguir, mas não são tão rígidas como a Pep8.
Eu aprendi Pep8 da maneira mais difícil.
Para contar a história, eu sou engenheiro de vendas na InterSystems e estou fazendo várias demos. Certo dia, eu estava fazendo uma demo de Python Embutido para um cliente que era desenvolvedor Python, e a conversa encurtou quando ele viu meu código. Ele me contou que meu código não era nada "Pythonico" (ele estava certo). Eu estava programando em Python como eu programava em ObjectScript. Por causa disso, ele disse que não estava interessado no Python Embutido mais. Eu fiquei chocado e decidi aprender Python do jeito certo.
Assim, se você quer aprender Python, aprenda Pep8 primeiro.
Módulos
Os módulos são uma funcionalidade que não temos em ObjectScript.
Geralmente, em linguagens orientadas a objetos, há classes e pacotes. No Python, temos classes, pacotes e módulos.
O que é um módulo?
É um arquivo com extensão .py. É a maneira de organizar seu código.
Você não entendeu? No começo eu também não. Portanto vamos usar um exemplo.
Geralmente, quando você quer criar uma classe em ObjectScript, você cria um arquivo .cls e coloca sua classe ali dentro. E, se quiser criar outra classe, usa outro arquivo .cls. Por fim, se quiser criar um pacote, você gera uma pasta e coloca os arquivos .cls dentro dela.
Em Python fazemos o mesmo, mas ele tem a habilidade de guardar várias classes num mesmo arquivo. Esse arquivo é chamado módulo. É importante saber que é "Pythônico" ter muitas classes em um mesmo arquivo.
Portanto, planeje de antemão como pretende organizar seu código e como vai nomear seus módulos para não terminar como eu, com um monte de módulos com o mesmo nome das suas classes.
Um mau exemplo:
A bad example :
MinhaClasse.py
class MinhaClasse:
def __init__(self):
pass
def meu_metodo(self):
pass
Para instanciar essa classe, você fará:
import MinhaClasse.MinhaClasse # estranho, né?
minha_classe= MinhaClasse()
Estranho, né?
Dunders
Dunders são métodos especiais em Python. Eles são chamados de dunder porque começam e terminam com dois caracteres underscores (_).
Eles são similares aos métodos do ObjectScript com '%'.
Eles são usados para:
- construtor
- sobrecarga de operador
- representação do objeto
- etc.
Exemplo:
class MinhaClasse:
def __init__(self):
pass
def __repr__(self):
return "MyClass"
def __add__(self, other):
return self + other
Aqui temos 3 métodos dunder:
__init__: construtor__repr__: representação do objeto__add__: sobrecarga de operador
Métodos Dunders estão por toda parte em Python. São uma grande parte da linguagem, mas não se preocupe; você vai aprendê-los rapidamente,
Conclusão
Python não é ObjectScript, e você terá que aprender. Mas não é tão difícil, então você vai aprender rápido. Apenas mantenha em mente que você deverá aprender Pep8 e como organizar seu código com módulos e métodos dunder.
Bons sites para aprender Python
Python Embutido
Agora que você conhece um pouco mais sobre o Python, vamos falar de Python Embutido.
O que é o Python Embutido?
Python Embutido é uma maneira de executar código Python dentro do IRIS. É uma nova funcionalidade do IRIS 2021.2+. Isso significa que seu código Python será executado no mesmo processo que o IRIS.
No mais, toda classe ObjectScript é uma classe Python, e o mesmo cale para métodos e atributos, e vice-versa. 🥳 Isso é bacana!
Como usar Python Embutido?
Há 3 maneiras diferentes de usar Python Embutido:
- Usando a tag de linguagem do ObjectScript
- Method Foo() As %String [ Language = python ]
- Usando a função ##class(%SYS.Python).Import()
- Usando o interpretador de Python
- python3 -c "import iris; print(iris.system.Version.GetVersion())"
Mas se você quiser usar mesmo o Python Embutido, você terá que evitar usar a tag de linguagem.

Por quê?
- Porque não é Pythônico
- Porque também não é ObjectScript
- Porque você não terá um debugador
- Porque você não terá um linter
- Porque você não terá um formatador
- Porque você não terá um framework de testes
- Porque você não terá um gerenciador de pacotes
- Porque você está misturando duas linguagens no mesmo arquivo
- Porque quando o seu processo quebra, você não tem um rastreio da pilha
- Porque você não pode usar um ambiente virtual ou ambientes conda
- ...
Não me entenda mal, isso funciona e pode ser útil se você quiser testar algo rapidamente, mas na minha opinião não é uma boa prática.
Então, o que eu aprendi nesses 2 anos de Python Embutido, e como utilizá-lo da maneira correta?
Como eu uso Python embutido
Para mim, existem duas opções:
- Usar libraries do Python como se fossem classes do ObjectScript
- with ##class(%SYS.Python).Import() function
- Usar uma abordagem de Python em primeiro lugar.
Usar libraries do Python como se fossem classes do ObjectScript
Você ainda quer usar Python no seu código ObjectScript, mas não quer usar a tag de linguagem. O que você pode fazer, então?
"Simplesmente" usar códigos e libraries do Python como se fossem classes ObjectScript.
Vamos tomar como exemplo:
Você quer usar a library requests (é uma library para fazer requisições HTTP) no seu código ObjectScript
Com a tag de linguagem
ClassMethod Get() As %Status [ Language = python ]
{
import requests
url = "https://httpbin.org/get"
# faça uma requisição get
response = requests.get(url)
# pegue os dados do json de resposta
data = response.json()
# iterar pelos dados e printar os pares chave-valoe
for key, value in data.items():
print(key, ":", value)
}
Por que eu não acho uma boa ideia?
Porque você está misturando duas linguagens no mesmo arquivo, e você não tem um debugador, um linter, um formatador, etc. Se esse código der algum problema, você terá dificuldade de debugar. Se você não tem um rastreio da pilha, não saberá da onde veio o erro. E também não tem a funcionalidade de completar automaticamente.
Sem a tag de linguagem
ClassMethod Get() As %Status
{
set status = $$$OK
set url = "https://httpbin.org/get"
// Importar o módulo Python "requests" como uma classe ObjectScript
set request = ##class(%SYS.Python).Import("requests")
// Chamar o método get da classe request
set response = request.get(url)
// Chamar o método json da classe response
set data = response.json()
// Aqui os dados são um dicionário Python
// Para iterar por um dicionário Python, você deve usar o método dunder e items().
// Importar um módulo pré-existente de Python.
set builtins = ##class(%SYS.Python).Import("builtins")
// Aqui estamos usando "len" do módulo de pré-existentes (built in) para saber o comprimento do dicionário.
For i = 0:1:builtins.len(data)-1 {
// Agora nós convertemos os itens do dicionário para uma lista, então pegamos a chave e o valor usando o método dunder __getitem__
Write builtins.list(data.items())."__getitem__"(i)."__getitem__"(0),": ",builtins.list(data.items())."__getitem__"(i)."__getitem__"(1),!
}
quit status
}
Por que eu acho isso uma boa ideia?
Porque você está utilizando Python como se fosse ObejctScript. Você está importando a library requests como uma classe ObjectScript e usando como se fosse uma classe ObjectScript.
Toda a lógica está em ObjectScript, e você utiliza o Python como uma library.
Até para manutenção, é mais fácil de ler e entender; qualquer desenvolvedor ObjectScript consegue entender esse código.
A desvantagem é que você tem que saber como usar os métodos dunders, e como usar Python como se fosse ObjectScript.
Conclusão
Acredite em mim, dessa maneira você vai acabar com um código mais robusto e conseguirá debugar facilmente. A princípio parece difícil, mas você vai entender os benefícios de aprender Python mais rápido do que você pensa.
Usar uma abordagem de Python em primeiro lugar
Essa é a maneira que eu prefiro usar Python Embutido
Eu construí várias ferramentas usando essa abordagem e estou muito feliz com isso.
Alguns exemplos:
- iop
- iris-python-interoperability-template
- iris-imap-adapter
- iris-chemicals-properties
- rest-to-dicom
- iris-fhir-python-strategy
Então, o que é uma abordagem de Python em primeiro lugar?
Existe apenas uma regra: o código Python deve estar em arquivos . py, o código ObjectScript deve estar em arquivos .cls
Como conseguir isso?
A ideia é criar classes de invólucro de ObjectScript para chamar o código Python.
Vamos tomar o exemplo de iris-fhir-python-strategy :
Exemplo : iris-fhir-python-strategy
Primeiramente, nós temos que entender como o servidor IRIS FHIR funciona.
Todo servidor IRIS FHIR implementa uma Strategy (estratégia).
Uma Strategy é um conjunto de duas classes:
| Superclases | Parâmetros de Subclasse |
|---|---|
| HS.FHIRServer.API.InteractionsStrategy | StrategyKey — Especifica um identificador único para a InteractionsStrategy.InteractionsClass — Especifica o nome da sua subclasse de Interactions (interações). |
| HS.FHIRServer.API.RepoManager | StrategyClass —Especifica o nome da sua sublcasse InteractionsStrategy.StrategyKey — Especifica um identificaod único para a InteractionsStrategy. Deve ter o mesmo valor do parâmetro StrategyKey na subclasse InteractionsStrategy. |
As duas classes são do tipo Abstract (abstratas).
HS.FHIRServer.API.InteractionsStrategyé uma classeAbstractque deve ser implementada para customizar o comportamento do Servidor FHIR.HS.FHIRServer.API.RepoManageré uma classeAbstractque deve ser implementada para customizar o armazenamento do servidor FHIR.
Observações
Para o nosso exemplo, vamos focar apenas na classe HS.FHIRServer.API.InteractionsStrategy, mesmo que a classe HS.FHIRServer.API.RepoManager também seja implementada e obrigatória para customizar o Servidor FHIR.
A classe HS.FHIRServer.API.RepoManager é implementada pela classe HS.FHIRServer.Storage.Json.RepoManager, que é a implementação padrão do Servidor FHIR.
##Onde achar o código
Todo o código fonte pode ser achado nesse repositório: iris-fhir-python-strategy
A pasta src contém as seguintes pastas:
python: contém o código Pythoncls: contem o código ObjectScript que é usado para chamar o código Python
Como implementar uma Strategy
Nesta prova de conceito, só estaremos interessados em como implementar uma Strategy em Python, não em como implementar um RepoManager (gerenciador de repositório).
Para implementar uma Strategy você deve criar pelo menos duas classes:
- Uma classe que herda da
HS.FHIRServer.API.InteractionsStrategy - Uma classe que herda da
HS.FHIRServer.API.Interactionsclass
Implementação de InteractionsStrategy
A classe HS.FHIRServer.API.InteractionsStrategy foca em customizar o comportamento do Servidor FHIR ao sobrescrever os seguintes métodos:
GetMetadataResource: chamado para pegar os metadados do servidor FHIR- esse é o único método que vamos sobrescrever nessa prova de conceito
HS.FHIRServer.API.InteractionsStrategy também tem dois parâmetros:
StrategyKey: um identificador único para a InteractionsStrategyInteractionsClass: o nome da subclasse de Interactions
Implementação de Interactions
A classe HS.FHIRServer.API.Interactions foca em customizar o comportamento do Servidor FHIR ao sobrescrever os seguintes métodos:
OnBeforeRequest: chamada antes da requisição ser enviada ao servidorOnAfterRequest: chamada após a requisição ser enviada ao servidorPostProcessRead: chamada após a operação de leitura estar finalizadaPostProcessSearch: chamada após a operação de procura estar finalizadaRead: chamada para ler um recursoAdd: chamada para adicionar um recursoUpdate: chamada para atualizar um recursoDelete: chamada para deletar um recurso- e muito mais...
Nós implementamos a classe HS.FHIRServer.API.Interactions no arquivo src/cls/FHIR/Python/Interactions.cls.
Class FHIR.Python.Interactions Extends (HS.FHIRServer.Storage.Json.Interactions, FHIR.Python.Helper)
{
Parameter OAuth2TokenHandlerClass As%String = "FHIR.Python.OAuth2Token";
Method %OnNew(pStrategy As HS.FHIRServer.Storage.Json.InteractionsStrategy) As%Status
{
// %OnNew é chamado quando o objeto é criado.// O parâmetro pStrategy é o objeto de estratégia criado nesse objeto.// A implementação padrão não faz nada// Primeiro define o caminho do Python de uma variável de ambienteset..PythonPath = $system.Util.GetEnviron("INTERACTION_PATH")
// Depois define o nome da classe de Python pela variável de ambienteset..PythonClassname = $system.Util.GetEnviron("INTERACTION_CLASS")
// Então define o nome do módulo Python pela variável de ambienteset..PythonModule = $system.Util.GetEnviron("INTERACTION_MODULE")
<span class="hljs-keyword">if</span> (<span class="hljs-built_in">..PythonPath</span> = <span class="hljs-string">""</span>) || (<span class="hljs-built_in">..PythonClassname</span> = <span class="hljs-string">""</span>) || (<span class="hljs-built_in">..PythonModule</span> = <span class="hljs-string">""</span>) {
<span class="hljs-comment">//quit ##super(pStrategy)</span>
<span class="hljs-keyword">set</span> <span class="hljs-built_in">..PythonPath</span> = <span class="hljs-string">"/irisdev/app/src/python/"</span>
<span class="hljs-keyword">set</span> <span class="hljs-built_in">..PythonClassname</span> = <span class="hljs-string">"CustomInteraction"</span>
<span class="hljs-keyword">set</span> <span class="hljs-built_in">..PythonModule</span> = <span class="hljs-string">"custom"</span>
}
<span class="hljs-comment">// Então, define a classe Python</span>
<span class="hljs-keyword">do</span> <span class="hljs-built_in">..SetPythonPath</span>(<span class="hljs-built_in">..PythonPath</span>)
<span class="hljs-keyword">set</span> <span class="hljs-built_in">..PythonClass</span> = <span class="hljs-keyword">##class</span>(FHIR.Python.Interactions).GetPythonInstance(<span class="hljs-built_in">..PythonModule</span>, <span class="hljs-built_in">..PythonClassname</span>)
<span class="hljs-keyword">quit</span> <span class="hljs-keyword">##super</span>(pStrategy)
}
Method OnBeforeRequest(
pFHIRService As HS.FHIRServer.API.Service,
pFHIRRequest As HS.FHIRServer.API.Data.Request,
pTimeout As%Integer)
{
// OnBeforeRequest é chamado antes de cada requisição ser processada.if$ISOBJECT(..PythonClass) {
set body = ##class(%SYS.Python).None()
if pFHIRRequest.Json '= "" {
set jsonLib = ##class(%SYS.Python).Import("json")
set body = jsonLib.loads(pFHIRRequest.Json.%ToJSON())
}
do..PythonClass."on_before_request"(pFHIRService, pFHIRRequest, body, pTimeout)
}
}
Method OnAfterRequest(
pFHIRService As HS.FHIRServer.API.Service,
pFHIRRequest As HS.FHIRServer.API.Data.Request,
pFHIRResponse As HS.FHIRServer.API.Data.Response)
{
// OnAfterRequest é chamado depois de cada requisição ser processada.if$ISOBJECT(..PythonClass) {
set body = ##class(%SYS.Python).None()
if pFHIRResponse.Json '= "" {
set jsonLib = ##class(%SYS.Python).Import("json")
set body = jsonLib.loads(pFHIRResponse.Json.%ToJSON())
}
do..PythonClass."on_after_request"(pFHIRService, pFHIRRequest, pFHIRResponse, body)
}
}
Method PostProcessRead(pResourceObject As%DynamicObject) As%Boolean
{
// PostProcessRead é chamada após um recurso ser lido na base de dados.// Retorna 1 para indicar que o recurso deve ser incluído na resposta.// Retorna 0 para indicar que o recurso deve ser excluído da resposta.if$ISOBJECT(..PythonClass) {
if pResourceObject '= "" {
set jsonLib = ##class(%SYS.Python).Import("json")
set body = jsonLib.loads(pResourceObject.%ToJSON())
}
return..PythonClass."post_process_read"(body)
}
quit1
}
Method PostProcessSearch(
pRS As HS.FHIRServer.Util.SearchResult,
pResourceType As%String) As%Status
{
// PostProcessSearch é chamado depois de uma busca.// Retorna $$$OK para indicar que a busca teve sucesso.// Retorna um código de erro para indicar que a busca falhou.if$ISOBJECT(..PythonClass) {
return..PythonClass."post_process_search"(pRS, pResourceType)
}
quit$$$OK
}
Method Read(
pResourceType As%String,
pResourceId As%String,
pVersionId As%String = "") As%DynamicObject
{
return##super(pResourceType, pResourceId, pVersionId)
}
Method Add(
pResourceObj As%DynamicObject,
pResourceIdToAssign As%String = "",
pHttpMethod = "POST") As%String
{
return##super(pResourceObj, pResourceIdToAssign, pHttpMethod)
}
/// Returns VersionId for the "deleted" version
Method Delete(
pResourceType As%String,
pResourceId As%String) As%String
{
return##super(pResourceType, pResourceId)
}
Method Update(pResourceObj As%DynamicObject) As%String
{
return##super(pResourceObj)
}
}
A classeFHIR.Python.Interactions herda das classes HS.FHIRServer.Storage.Json.Interactions e FHIR.Python.Helper.
A classe HS.FHIRServer.Storage.Json.Interactions é o padrão de implementação do Servidor FHIR.
A classe FHIR.Python.Helper foca em ajudar chamar o código Python pelo ObjectScript .
A classe FHIR.Python.Interactions sobrescreve os métodos a seguir:
%OnNew: chamado quando o objeto é criado- nós usamos esse método para definir o caminho, nome da classe de, e nome de módulo do Python a partir das variáveis de ambiente.
- se as variáveis de ambiente não forem definidas, utilizamos valores padrão.
- também definimos a classe python
- chamamos o método
%OnNewda classe pai.
Method %OnNew(pStrategy As HS.FHIRServer.Storage.Json.InteractionsStrategy) As %Status
{
// Primeiro, definimos o caminho Python por uma variável de ambiente
set ..PythonPath = $system.Util.GetEnviron("INTERACTION_PATH")
// Então, definimos o nome da classe Python pela variável de ambiente
set ..PythonClassname = $system.Util.GetEnviron("INTERACTION_CLASS")
// Depois, definimos o nome do módulo Python da variável de ambiente
set ..PythonModule = $system.Util.GetEnviron("INTERACTION_MODULE")
if (..PythonPath = "") || (..PythonClassname = "") || (..PythonModule = "") {
// usa valores padrão
set ..PythonPath = "/irisdev/app/src/python/"
set ..PythonClassname = "CustomInteraction"
set ..PythonModule = "custom"
}
// A seguir, definimos a classe Python
do ..SetPythonPath(..PythonPath)
set ..PythonClass = ..GetPythonInstance(..PythonModule, ..PythonClassname)
quit ##super(pStrategy)
}
OnBeforeRequest: chamada antes da requisição ser enviada ao servidor- chamamos o método
on_before_requestda classe Python - passamos os objetos
HS.FHIRServer.API.ServiceeHS.FHIRServer.API.Data.Request, o corpo da requisição e o tempo limite de execução.
- chamamos o método
Method OnBeforeRequest(
pFHIRService As HS.FHIRServer.API.Service,
pFHIRRequest As HS.FHIRServer.API.Data.Request,
pTimeout As %Integer)
{
// OnBeforeRequest é chamado antes de cada requisição ser processada.
if $ISOBJECT(..PythonClass) {
set body = ##class(%SYS.Python).None()
if pFHIRRequest.Json '= "" {
set jsonLib = ##class(%SYS.Python).Import("json")
set body = jsonLib.loads(pFHIRRequest.Json.%ToJSON())
}
do ..PythonClass."on_before_request"(pFHIRService, pFHIRRequest, body, pTimeout)
}
}
OnAfterRequest: chamado após a requisição ser enviada ao servidor- chamamos o método
on_after_requestda classe Python - passamos os objetos
HS.FHIRServer.API.Service,HS.FHIRServer.API.Data.RequesteHS.FHIRServer.API.Data.Responsee o corpo da resposta
- chamamos o método
Method OnAfterRequest(
pFHIRService As HS.FHIRServer.API.Service,
pFHIRRequest As HS.FHIRServer.API.Data.Request,
pFHIRResponse As HS.FHIRServer.API.Data.Response)
{
// OnAfterRequest é chamado após cada requisição ser processada
if $ISOBJECT(..PythonClass) {
set body = ##class(%SYS.Python).None()
if pFHIRResponse.Json '= "" {
set jsonLib = ##class(%SYS.Python).Import("json")
set body = jsonLib.loads(pFHIRResponse.Json.%ToJSON())
}
do ..PythonClass."on_after_request"(pFHIRService, pFHIRRequest, pFHIRResponse, body)
}
}
- E por aí em diante...
Interactions em Python
A classe FHIR.Python.Interactions chama os métodos on_before_request, on_after_request, ... da classe Python.
Aqui está uma classe Python abstrata:
import abc
import iris
class Interaction(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def on_before_request(self,
fhir_service:'iris.HS.FHIRServer.API.Service',
fhir_request:'iris.HS.FHIRServer.API.Data.Request',
body:dict,
timeout:int):
"""
on_before_request é chamada depois que a requisição é enviada ao servidor.
param fhir_service: o objeto de serviço fhir iris.HS.FHIRServer.API.Service
param fhir_request: o objeto de requisição fhir iris.FHIRServer.API.Data.Request
param timeout: o tempo limite de execução em segundos
return: None
"""
@abc.abstractmethod
def on_after_request(self,
fhir_service:'iris.HS.FHIRServer.API.Service',
fhir_request:'iris.HS.FHIRServer.API.Data.Request',
fhir_response:'iris.HS.FHIRServer.API.Data.Response',
body:dict):
"""
on_after_request é chamada após a requisição ser enviada ao servidor.
param fhir_service: o objeto de serviço fhir iris.HS.FHIRServer.API.Service
param fhir_request: o objeto de requisição fhir iris.FHIRServer.API.Data.Request
param fhir_response: o objeto de resposta fhir iris.FHIRServer.API.Data.Response
return: None
"""
@abc.abstractmethod
def post_process_read(self,
fhir_object:dict) -> bool:
"""
post_process_read é chamado após a operação de leitura finalizar.
param fhir_object: o objeto fhir
return: True - o recurso deve ser retornado ao cliente, False - caso contrário
"""
@abc.abstractmethod
def post_process_search(self,
rs:'iris.HS.FHIRServer.Util.SearchResult',
resource_type:str):
"""
post_process_search é chamada após a operação de pesquisa finalizar.
param rs: o resultado da pesquisa iris.HS.FHIRServer.Util.SearchResult
param resource_type: o tipo de recurso
return: None
"""
Implementação da classe Python abstrata
from FhirInteraction import Interaction
class CustomInteraction(Interaction):
def on_before_request(self, fhir_service, fhir_request, body, timeout):
# Extrai o usuário e seus papéis para essa requisição
# então o consentimento pode avaliado.
self.requesting_user = fhir_request.Username
self.requesting_roles = fhir_request.Roles
def on_after_request(self, fhir_service, fhir_request, fhir_response, body):
# Limpa o usuário e seus papéis entre as requisições.
self.requesting_user = ""
self.requesting_roles = ""
def post_process_read(self, fhir_object):
# Avalia consentimento baseado no recurso e usuários/papéis
# Retornar 0 indica que esse recurso não deveria ser exibido - um erro "404 Not Found" (não encontrado)
# Será retornado ao usuário.
return self.consent(fhir_object['resourceType'],
self.requesting_user,
self.requesting_roles)
def post_process_search(self, rs, resource_type):
# Itera por cada recurso no conjunto de pesquisa e avalia
# Consentimento baseado no recurso e usuário/papéis
# Cada linha marcada como deletada e salva será excluída do Bundle (conjunto ad resposta).
rs._SetIterator(0)
while rs._Next():
if not self.consent(rs.ResourceType,
self.requesting_user,
self.requesting_roles):
# Marca a linha como deletada e salva.
rs.MarkAsDeleted()
rs._SaveRow()
def consent(self, resource_type, user, roles):
# Exemplo de lógica de consentimento - só permite que usuários com o papel '%All' vejam
# Recursos de observações.
if resource_type == 'Observation':
if '%All' in roles:
return True
else:
return False
else:
return True
Muito longo, faça um resumo
A classe FHIR.Python.Interactions é um invólucro para chamar a classe Python.
As classes abstratas IRSI são implementadas para envolver as classes abstratas Python 🥳.
Isso nos ajuda a manter o código Python e o código ObjectScript separados e então se beneficiar do melhor dos dois mundos.