0 Seguidores · 64 Postagens

Recursos Rápidos de Interoperabilidade em Saúde (FHIR, pronuncia-se como "fire") é um projeto de padrão que descreve formatos e elementos de dados (conhecidos como "recursos") e uma interface de programação de aplicações (API) para troca de registros eletrônicos de saúde

Site oficial

Artigo Heloisa Paiva · Out. 5, 2025 12m read

Boas-vindas, estimados membros da Comunidade!


Neste artigo, apresentaremos um exemplo de um projeto que implementa uma solução baseada em FHIR. Este projeto se baseará no projeto nacional (espanhol), conhecido como ÚNICA

O que é ÚNICAS?

Em suas próprias palavras:

Um projeto cujo objetivo é criar um ecossistema de colaborações para melhorar a atenção à saúde de pacientes pediátricos com Doenças Raras Complexas (DRCs). Este projeto está sendo implementado por meio da rede dentro do Sistema Nacional de Saúde (SNS) para melhorar o diagnóstico e o cuidado de pacientes com doenças raras.

0
0 14
Artigo Henry Pereira · Maio 30, 2025 6m read

imagem

Sabe aquela sensação de receber o resultado do seu exame de sangue e parecer que está em grego? É exatamente esse problema que o FHIRInsight veio resolver. Surgiu da ideia de que dados médicos não deveriam ser assustadores ou confusos – deveriam ser algo que todos podemos utilizar. Exames de sangue são extremamente comuns para verificar nossa saúde, mas, sejamos sinceros, interpretá-los é difícil para a maioria das pessoas e, às vezes, até para profissionais da área que não trabalham em um laboratório. O FHIRInsight quer tornar todo esse processo mais simples e acessível.

Logo FHIRInsight

🤖 Por que criamos o FHIRInsight

Tudo começou com uma simples pergunta:

“Por que interpretar um exame de sangue ainda é tão difícil — até para médicos, às vezes?”

Se você já olhou para um resultado de laboratório, provavelmente viu um monte de números, abreviações enigmáticas e uma “faixa de referência” que pode ou não se aplicar à sua idade, sexo ou condição. É uma ferramenta de diagnóstico, com certeza — mas sem contexto, vira um jogo de adivinhação. Até profissionais de saúde experientes às vezes precisam recorrer a diretrizes, artigos científicos ou opiniões de especialistas para entender tudo direito.

É aí que o FHIRInsight entra em cena.

Não foi feito apenas para pacientes — fizemos para quem está na linha de frente do atendimento. Para médicos em plantões intermináveis, para enfermeiros que captam sutis padrões nos sinais vitais, para qualquer profissional de saúde que precise tomar decisões certas com tempo limitado e muita responsabilidade. Nosso objetivo é facilitar um pouco o trabalho deles — transformando dados clínicos FHIR em algo claro, útil e fundamentado em ciência médica real. Algo que fale a língua humana.

O FHIRInsight faz mais do que só explicar valores de exames. Ele também:

  • Fornece aconselhamento contextual sobre se um resultado é leve, moderado ou grave
  • Sugere causas potenciais e diagnósticos diferenciais com base em sinais clínicos
  • Recomenda próximos passos — sejam exames adicionais, encaminhamentos ou atendimento de urgência
  • Utiliza RAG (Retrieval-Augmented Generation) para incorporar artigos científicos relevantes que fundamentam a análise

Imagine um jovem médico revisando um hemograma de um paciente com anemia. Em vez de buscar cada valor no Google ou vasculhar revistas médicas, ele recebe um relatório que não só resume o problema, mas cita estudos recentes ou diretrizes da OMS que embasam o raciocínio. Esse é o poder de combinar IA e busca vetorial sobre pesquisas selecionadas.

E o paciente?

Ele não fica mais diante de um monte de números, sem saber o que significa “bilirrubina 2,3 mg/dL” ou se deve se preocupar. Em vez disso, recebe uma explicação simples e cuidadosa. Algo que se assemelha mais a uma conversa do que a um laudo clínico. Algo que ele realmente entende — e que pode levar ao consultório do médico mais preparado e menos ansioso.

Porque é disso que o FHIRInsight trata de verdade: transformar complexidade médica em clareza e ajudar profissionais e pacientes a tomarem decisões melhores e mais confiantes — juntos.

🔍 Por dentro da tecnologia

Claro que toda essa simplicidade na superfície é viabilizada por uma tecnologia poderosa atuando discretamente nos bastidores.

Veja do que o FHIRInsight é feito:

  • FHIR (Fast Healthcare Interoperability Resources) — padrão global de dados de saúde. É assim que recebemos informações estruturadas como resultados de exames, histórico do paciente, dados demográficos e atendimentos. O FHIR é a linguagem que os sistemas médicos falam — e nós traduzimos essa linguagem em algo que as pessoas possam usar.
  • Busca Vetorial RAG (Retrieval-Augmented Generation): o FHIRInsight aprimora seu raciocínio diagnóstico indexando artigos científicos em PDF e URLs confiáveis em um banco de dados usando a busca vetorial nativa do InterSystems IRIS. Quando um resultado de exame fica ambíguo ou complexo, o sistema recupera conteúdo relevante para embasar suas recomendações — não da memória do modelo, mas de pesquisas reais e atualizadas.
  • Engenharia de Prompts para Raciocínio Médico: refinamos nossos prompts para guiar o LLM na identificação de um amplo espectro de condições relacionadas ao sangue. Desde anemia por deficiência de ferro até coagulopatias, desequilíbrios hormonais ou gatilhos autoimunes — o prompt conduz o LLM pelas variações de sintomas, padrões laboratoriais e causas possíveis.
  • Integração com LiteLLM: um adaptador customizado direciona requisições para múltiplos provedores de LLM (OpenAI, Anthropic, Ollama etc.) através de uma interface unificada, permitindo fallback, streaming e troca de modelo com facilidade.

Tudo isso acontece em segundos — transformando valores brutos de laboratório em insights médicos explicáveis e acionáveis, seja para um médico revisando 30 prontuários ou um paciente tentando entender seus números.

🧩 o Adapter LiteLLM: Uma Interface para Todos os Modelos

Nos bastidores, os relatórios do FHIRInsight movidos a IA são impulsionados pelo LiteLLM — uma library python que nos permite chamar mais de 100 LLMs (OpenAI, Claude, Gemini, Ollama etc.) por meio de uma única interface estilo OpenAI.

Porém, integrar o LiteLLM ao InterSystems IRIS exigiu algo mais permanente e reutilizável do que scripts Python escondidos numa Business Operation. Então, criamos nosso próprio Adapter LiteLLM.

Conheça o LiteLLMAdapter

Esse adaptador lida com tudo o que você esperaria de uma integração robusta de LLM:

  • Recebe parâmetros como prompt, model e temperature
  • Carrega dinamicamente variáveis de ambiente (por exemplo, chaves de API)

Para encaixar isso em nossa produção de interoperabilidade em um simples Business Operation:

  • Gerencia configuração em produção por meio da configuração padrão LLMModel
  • Integra-se ao componente FHIRAnalyzer para geração de relatórios em tempo real
  • Atua como uma “ponte de IA” central para quaisquer componentes futuros que precisem de acesso a LLM

Eis o fluxo de forma simplificada:

set response = ##class(dc.LLM.LiteLLMAdapter).CallLLM("Me fale sobre hemoglobina.", "openai/gpt-4o", 0.7)
write response

🧪 Conclusão

Quando começamos a desenvolver o FHIRInsight, nossa missão era simples: tornar exames de sangue mais fáceis de entender — para todo mundo. Não apenas pacientes, mas médicos, enfermeiros, cuidadores... qualquer pessoa que já tenha encarado um resultado de laboratório e pensado: “Ok, mas o que isso realmente significa?”

Todos nós já passamos por isso.

Ao combinar a estrutura do FHIR, a velocidade do InterSystems IRIS, a inteligência dos LLMs e a profundidade de pesquisas médicas reais via busca vetorial, criamos uma ferramenta que transforma números confusos em histórias significativas. Histórias que ajudam pessoas a tomarem decisões mais inteligentes sobre sua saúde — e, quem sabe, detectar algo cedo que passaria despercebido.

Mas o FHIRInsight não é só sobre dados. É sobre como nos sentimos ao olhar para esses dados. Queremos que seja claro, acolhedor e empoderador. Que a experiência seja... como um verdadeiro “vibecoding” na saúde — aquele ponto perfeito onde código inteligente, bom design e empatia humana se encontram.

Esperamos que você experimente, teste, questione — e nos ajude a melhorar.

Diga o que gostaria de ver no próximo release. Mais condições? Mais explicações? Mais personalização?

Isso é só o começo — e adoraríamos que você ajudasse a moldar o que vem a seguir.

1
0 33
Artigo Larissa Prussak · Jun. 5, 2025 3m read

O IRIS oferece suporte nativo para transformações CCDA e FHIR, mas o acesso e a visualização desses recursos exigem tempo considerável de configuração e conhecimento do produto. O aplicativo IRIS Interop DevTools foi desenvolvido para preencher essa lacuna, permitindo que implementadores comecem a utilizar e visualizar imediatamente as capacidades de transformação embutidas no produto.

Além do ambiente de transformação IRIS XML, XPath e CCDA, o pacote Interop DevTools agora inclui:

0
0 33
Artigo Larissa Prussak · Maio 23, 2025 1m read

Depois que implementamos um novo container baseado em containers.intersystems.com/intersystems/irishealth:2023.1 esta semana, percebemos que nosso FHIR Repository começou a responder com um Erro 500. Isso aconteceu devido a violações de PROTECT no novo namespace e banco de dados HSSYSLOCALTEMP utilizado por essa versão dos componentes FHIR do IRIS for Health.

A solução para isso é adicionar a permissão "%DB_HSSYSLOCALTEMP" nas Aplicações Web que processam as requisições FHIR. Você pode automatizar essa configuração executando o seguinte método de classe nos namespace(s) onde essas Aplicações Web estão definidas:

do ##class(HS.HealthConnect.FHIRServer.Upgrade.MethodsV6).AddLOCALTEMPRoleToCSP()

No nosso caso, isso não foi suficiente. Parte do nosso código customizado precisa acessar o token JWT Bearer enviado pelo cliente, o qual antes era obtido através do elemento AdditionalInfo "USER:OAuthToken", que não está mais presente na build 2023.6.1.809, conforme descrito em: https://docs.intersystems.com/upgrade/results?product=ifh&versionFrom=20...

Contornamos esse problema adicionando a seguinte lógica para buscar o token diretamente do cache de tokens:

$$$ThrowOnError(##class(HS.HC.Util.InfoCache).GetTokenInfo(pInteropRequest.Request.AdditionalInfo.GetAt("USER:TokenId"), .pTokenInfo)) set OAuthToken = pTokenInfo("token_string")

0
0 20
Artigo Larissa Prussak · Maio 23, 2025 1m read

 

Apresento o FHIRCraft, uma ferramenta leve para gerar recursos FHIR sintéticos.

Talvez vocês estejam pensando:
“Mas... já não existe o Synthea, que gera um monte de recursos FHIR?”
Exatamente — e foi justamente por isso que criei este aplicativo.

O FHIRCraft foi desenvolvido para gerar recursos FHIR mais simples, pequenos e focados. Diferente do Synthea, ele não pretende simular prontuários clínicos completos nem fluxos assistenciais. É pensado para quem está começando com FHIR, quer fazer testes de forma progressiva ou explorar como funciona um recurso específico de forma isolada.

0
0 28
Artigo Heloisa Paiva · Maio 19, 2025 2m read

Olá, desenvolvedores!

Este será um artigo bem curto, pois em abril de 2025, com o Lovable e outras ferramentas Prompt-to-UI, tornou-se possível construir o frontend com prompting. Até mesmo para pessoas como eu, que não estão nem um pouco familiarizadas com as técnicas modernas de UI.

Bem, eu conheço pelo menos as palavras javascript, typescript e ReactJS, então, neste artigo bem curto, construiremos a UI ReactJS para o servidor InterSystems FHIR com Lovable.ai.

Vamos lá!

0
0 35
Artigo Heloisa Paiva · Abr. 20, 2025 6m read

Sei que pessoas completamente novas no VS Code, Git, Docker, FHIR e outras ferramentas podem, às vezes, ter dificuldades com a configuração do ambiente. Por isso, decidi escrever um artigo que percorre todo o processo de configuração passo a passo para facilitar o início.

Eu realmente agradeceria se você pudesse deixar um comentário no final – me diga se as instruções foram claras, se algo ficou faltando ou se há mais alguma coisa que você acharia útil.

A configuração inclui:

✅ VS Code – Editor de código
✅ Git – Sistema de controle de versão
✅ Docker – Executa uma instância do IRIS for Health Community
Extensão REST Client do VS Code – Para executar consultas à API FHIR
✅ Python – Para escrever scripts baseados em FHIR
✅ Jupyter Notebooks –Para tarefas de IA e FHIR

Antes de começar: Certifique-se de ter privilégios de administrador no seu sistema.

Além de ler o guia, você também pode seguir os passos nos vídeos:

Para Windows

0
0 39
Artigo Danusa Calixto · Mar. 12, 2025 1m read

Olá Colegas! 

Muitas vezes, durante o desenvolvimento de um frontend de aplicação ou qualquer outra comunicação com API REST, vale a pena usar o Swagger UI -  uma IU de teste para aplicações REST que segue a especificação Open API 2.0. Geralmente, é bem trabalhoso, pois permite testes manuais rápidos em relação à API REST, suas respostas e os dados contidos nela.

Recentemente eu introduzi o suporte ao Swagger para o modelo InterSystems IRIS FHIR para API FHIR R4: 

Como fazê-lo funcionar. 

0
0 36
Artigo Yuri Marx · Nov. 22, 2024 6m read

O FHIR (Fast Healthcare Interoperability Resources) é o padrão mais utilizado pelo mercado para interoperar e armazenar dados em saúde. Trata-se de um padrão que mapeia dezenas de recursos de dados (Pacientes, Observações, Medicações, Diagnósticos, Alergias, Vacinas, Faturamento, Provedores de Saúde, Atendimentos, dentre outros) e seus relacionamentos (Medicações do Paciente, por exemplo). O acesso a todas estas estruturas de dados se dá pelo uso de APIs REST em formato JSON ou XML. A princípio, a maioria dos fornecedores de soluções FHIR, não disponibiliza acesso aos dados no formato SQL.

4
1 73
Artigo Heloisa Paiva · Out. 14, 2024 9m read

Este programa de demonstração é usado para mostrar como um perfil FHIR personalizado pode ser empregado para validar a conformidade dos dados. O guia de implementação FHIR personalizado foi desenvolvido com base na Versão R4 do FHIR, e, neste exemplo, implementa a extensão do recurso Organização para validar a conformidade dos dados.

Instalação

  1. Faça o Download do projeto via Git clone.
  2. Execute docker-compose up -d para criar e iniciar o contêiner, a execução inicial baixará as imagens de contêiner necessárias e a execução do script levará de 5 a 10 minutos (dependendo da máquina). O InterSystems IRIS for Health image será criado, então o servidor FHIR será instalado e a especificação FHIR personalizada será importada para que possa ser usada para validar os dados.
  3. Importe os arquivos de caso de teste de TestCases no Postman para ver como as várias restrições FHIR são validadas 4.Depois que o contêiner for iniciado, você poderá ver o conteúdo doCustom IG.

Estrutura do código

FHIRValidation
├─ ExampleIG                        
│  ├─ ig.ini
│  ├─ input
│  │  ├─ fsh
│  │  │  ├─ alias.fsh
│  │  │  ├─ codesystems.fsh
│  │  │  ├─ organization.fsh
│  │  │  └─ valuesets.fsh
│  │  └─ pagecontent
│  │     └─ index.md
│  └─ sushi-config.yaml
├─ README.md
├─ README_zh.md
├─ TestCases
│  └─ FHIR Profile-based Validation  testcases.postman_collection.json
├─ docker-compose.yml
└─ image-iris
   ├─ Dockerfile
   └─ src
      ├─ FullIG
      ├─ IGPackages
      │  ├─ hl7.fhir.uv.extensions.r4#5.1.0.tgz
      │  ├─ hl7.terminology.r4#6.0.2.tgz
      │  └─ package.tgz
      └─ init
         └─ init.sh

Examplo IG

Todos os arquivos neste subdiretório são códigos-fonte SUSHI da especificação FHIR personalizada.

Casos de teste

Este subdiretório contém scripts de caso de teste baseados na API REST do FHIR, que precisam ser importados para o Postman

image-iris

Este subdiretório contém os arquivos necessários para a imagem InterSystems IRIS for Health: └─ src ├─ FullIG Este diretório armazena o IG FHIR personalizado gerado porSUSHI ├─ IGPackages Este diretório contém os arquivos de pacote para IGs FHIR personalizados └─ ini tEste diretório contém os scripts de inicialização da imagem Docker IRIS

Introdução ao pacote FHIR

A organização HL7 recomenda o uso de guias de implementação (Guia de Implementação](https://build.fhir.org/ig/FHIR/ig-guidance/)) para explicar como utilizar a especificação FHIR. Além de instruções para leitura por desenvolvedores (por exemplo, HTML), os guias de implementação geralmente incluem artefatos legíveis por máquina e aplicáveis diretamente, que podem ser usados para direcionar tarefas como geração de código e validação de dados.

O Guia de Implementação FHIR usa a especificação Pacote NPM para gerenciar dependências. Todas as definições de estrutura (StructureDefinition), conjuntos de valores (ValueSet) e outros recursos cobertos pelo guia são agrupados em um único pacote que pode ser usado pelo servidor FHIR para ler a especificação, gerar código do cliente ou realizar verificações de qualidade de dados.

O guia de implementação gerado pela ferramenta SUSHI contém vários arquivos de pacote. Neste exemplo, image-iris/src/IGPackages/package.tgz é o pacote gerado, que pode ser diretamente importado pelo Servidor FHIR IRIS. É importante observar que, além dos pacotes de recursos centrais (por exemplo, hl7.fhir.r4.core),a especificação FHIR completa precisa referenciar pacotes de recursos adicionais, como terminologia, extensões e assim por diante.

A documentação atual do mecanismo de referência da especificação FHIR ainda não está completa. Por exemplo, com base na versão R4 da especificação FHIR, além de referenciar hl7.fhir.r4.core, ela também precisa referenciar [hl7.fhir.uv.extensions.r4#5.1.0](https://simplifier.net/packages/hl7.fhir.uv.extensions.r4/ 5.1.0) e hl7.terminology.r4#6.0.2. Essas referências estão documentadas naversão R5mas não declaradas na versão R4, então o desenvolvedor precisa adicioná-las durante o processo de desenvolvimento.

Nesse caso, esses pacotes foram baixados na pasta image-iris/src/IGPackages e serão carregados como dependências antes da personalização do guia de implementação FHIR.

Introdução à validação FHIR

Veja a seção Recursos de Validação da especificação FHIR. A especificação FHIR foi projetada com mecanismos de verificação de qualidade de dados para uma ampla variedade de elementos, incluindo estruturas de dados, bases de atributos, campos de valor, ligações de código, restrições etc. A organização HL7, na especificação FHIR, não define qual intensidade de controle de qualidade seguir, mas recomenda que o princípio detolerância seja aplicado aos dados FHIR.

Para repositórios FHIR que armazenam recursos FHIR, garantir a qualidade dos dados é um pré-requisito para tornar a indústria da saúde valiosa e garantir a segurança das práticas de saúde. Portanto, ao construir um esquema de compartilhamento e troca de dados baseado em repositórios FHIR, mesmo que dados que não atendam aos requisitos de qualidade tenham que ser salvos, deve-se calibrar o sistema para marcar as não-conformidades e promover atividades de governança de dados para salvaguardar a segurança da saúde e os interesses dos consumidores de dados.

ODos vários métodos de validação de dados indicados pela especificação FHIR, o Validador FHIR e as Operações FHIR fornecem a cobertura mais abrangente para validação de qualidade de dados.

Este exemplo usará a operação $validate fornecida pelo InterSystems IRIS for Health para validar dados FHIR que ainda não foram salvos via o parâmetro de perfil. Os usuários também podem modificar o caso de teste para construir um parâmetro HTTP POST para validar o recurso FHIR estoque.

Também deve ser observado que a operação $validate, se chamada corretamente, retornará o resultado da validação via Http 200 e, se houver alguma não-conformidade, uma mensagem de erro será encapsulada no recurso OperationOutcome retornado em vez de identificar o erro via o código Http.

Extensões ao FHIR R4

As seguintes extensões foram feitas ao recurso Organização com base no FHIR R4:

1. Modifique a força de ligação da linguagem

Altere a força de ligação do idioma principal da organização para obrigatório

2. Cardinalidade do campo ativo alterada de 0...1 para 1...1

Isso torna o status do campo ativo um campo obrigatório, com um e somente um elemento

3. Cardinalidade do campo Nome alterada de 0..1 para 1..1

O nome da organização se torna um campo obrigatório com um e somente um elemento. Para referência, hospitais na China podem ter mais de um nome além do nome do hospital se tiverem licenças como um Centro de Emergência, um Centro de Dor Torácica e assim por diante. No entanto, é observado que essas licenças geralmente identificam a capacidade dos serviços prestados pela instituição de saúde em vez do nome legal que ela tem no sistema de registro da organização, e o ciclo de vida de tais licenças não coincide com o ciclo de vida da própria instituição de saúde. Portanto, o nome obtido da licença é apropriadamente considerado como a capacidade de serviço da organização de saúde em vez do nome exclusivo da organização. No FHIR, o nome derivado da capacidade de serviço pode ser fornecido através do recurso HealthcareService, que pode ser mais apropriadamente usado para expressar o conceito acima estabelecendo um relacionamento de referência muitos-para-um com o recurso Organização

4.Aumento no tipo de organização de instituições médicas

De acordo com os tipos de organização do padrão nacional chinês GB/T 20091-2021, os CodeSystem organizationtype-code-system e ValueSet organizationtype-vs são adicionados respectivamente, e a extensão mdm-organizationTypeExtension é adicionada ao recurso Organização através de Extensão. A extensão mdm-organizationTypeExtension é adicionada ao recurso Organização para que o recurso possa ser usado para representar o tipo de organização que identifica os tipos de organização chineses. A extensão é implementada fatiando a Extensão com uma cardinalidade de 1..1 para que o recurso Organização de Saúde deva ter um elemento de tipo de organização.

5. Restrições nos números de identificação de organizações de saúde

O padrão base FHIR não inclui o tipo do código de crédito social unificado para organizações chinesas, por esse motivo o CodeSystem cs-identifierType-code-system é adicionado e o Identificador é fatiado de acordo com seu tipo, para que ele possa expressar o código de crédito social. E o formato do código de crédito social segue as seguintes restrições:

  1. identifier.use deve assumir o valor oficial, ou seja, uso oficial/oficial
  2. identifier.type DEVE seguir cs-identifierType-code-system, system DEVE ser o uri do codesystem e code DEVE ser “USCC”.
  3. identifier.value deve seguir a restrição personalizada uscc-length-18, o campo deve ter 18 dígitos de comprimento, dos quais os primeiros 17 dígitos devem ser numéricos e o último 1 dígito deve ser numérico ou alfabético

Lista de Casos de Teste

1. Sem perfil - Tudo OK

O perfil correspondente do recurso não é declarado, portanto, o Servidor FHIR não validará os valores dos atributos no recurso e retornará apenas Tudo OK.

2. Campo desconhecido

Um atributo não definido isNational foi adicionado ao recurso, portanto, o mecanismo de validação retornou um erro de elemento não reconhecido

3. Cardinalidade errada - menos

Neste IG, a cardinalidade do atributo de nome do recurso Organização foi modificada para 1..1, o que significa que deve haver e somente um nome de organização. O nome não é preenchido neste caso de teste e, portanto, a validação de dados falha. Além disso, pode-se observar que Identifier.type foi estendido para incluir o Código de Crédito Social Uniforme como um tipo de identificador, que não está incluído na especificação FHIR R4, mas a força da ligação de código para este campo é apenas EXEMPLO, o que não força restrições. Portanto, o mecanismo de validação retorna a informação de nível de valor do campo de código não-conformidade sem relatar um erro.

4.Força de ligação

Neste Guia de Implementação (IG), a força de ligação do código do atributo de idioma da organização foi alterada para "obrigatório". Isso significa que o valor do campo deve estar de acordo com o conjunto de valores http://hl7.org/fhir/ValueSet/languages. No caso de teste onde o campo recebe o valor "wrong language" (idioma incorreto), como esse valor não está presente no conjunto de valores obrigatório, a validação resultará em um erro crítico

5.Valor errado

Neste IG, o campo de valor para o tipo de organização vem de organizationtype-code-system, portanto, quando o valor de code no elemento de extensão do tipo mdm-organizationTypeExtension, que tem um valor de "999", que não está no campo de valor, resultará em um erro de nível de erro.

6. Invariante falha

Neste IG, o código de crédito social de uma organização deve seguir a restrição personalizada uscc-length-18 (o campo deve ter 18 dígitos de comprimento, onde os primeiros 17 dígitos devem ser numéricos e o último 1 dígito deve ser numérico ou alfabético), e, portanto, violar essa restrição quando o último dígito for o caractere “%” resultará em um erro.

7.Perfil falha

Um único perfil para uma definição de recurso contém várias restrições, portanto, todos os problemas que não satisfazem o perfil serão detectados durante a validação, como os seguintes problemas neste exemplo:

  1. código de idioma errado
  2. tipo de organização errado
  3. campo de nome ausente
0
0 33
Artigo Heloisa Paiva · Set. 28, 2024 1m read

Baseado num ótimo exemplo e workshop construído por @Luis Angel Pérez Ramos (veja artigos relacionados e o Open Exchange app relacionado), que incluiu um container local InterSystems for Health (e setup desejado), essa amostra apresentada aqui, adaptada do workshop para usar o InterSystems Cloud FHIR Server, e suas definições relacionadas.

0
0 35
Artigo Heloisa Paiva · Set. 20, 2024 4m read

Nós concluímos essa série de artigos SMART On FHIR com Auth0 e InterSystems FHIR Repository revisando nossa aplicação desenvolvida em Angular 16.

Vamos relembrar como foi definida a arquitetura da nossa solução:

Nossa aplicação front-end corresponde à segunda coluna e como você pode ver ela tem duas funções:

  1. Redirecionar a requisição de login para Auth0 e receber a resposta
  2. Enviar e receber a resposta de requisições via REST enviadas ao servidor FHIR

Angular

0
0 42
Artigo Heloisa Paiva · Set. 18, 2024 7m read

No último artigo nós apresentamos a arquitetura do nosso projeto SMART On FHIR, então agora é o momento de colocar a mão na massa e começar a configurar os elementos que vamos precisar. 

Vamos começar com Auth0.

Configuração AUTH0

Vamos começar criando uma conta Auth0 com um email válido. Uma vez registrada, vamos criar nossa primeira aplicação e faremos isso do menu a esquerda:

Em nosso exemplo, a aplicação será do tipo Single Page Web Application, como é uma aplicação desenvolvida em Angular 16. Selecionamos esta opção e clicamos em Create.

Na próxima pagina devemos definir os seguintes campos:

0
0 34
Artigo Heloisa Paiva · Set. 14, 2024 4m read

Introdução

Eu recentemente participei no "mão na massa" fantasticamente organizado pelo @Patrick Jamieson no qual uma aplicação Angular foi configurada junto com um servidor IRIS FHIR seguindo os protocolos definidos pelo SMART On FHIR e eu o achei muito interessante, então decidi desenvolver a minha própria aplicação Angular e então usar o que aprendi e publicar na comunidade.

SMART On FHIR

Vamos ver o que o Google nos conta sobre o SMART On FHIR:

0
0 70
Artigo Heloisa Paiva · Maio 20, 2024 21m read

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.

image

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.

ObjectScriptPython
Não tipadaNão tipada
Scripting languageScripting language
Orientada a ObjetoOrientada a Objeto
InterpretadaInterpretada
Fácil integração com CFá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.

pep8.org

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.

image

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:

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:

SuperclasesParâmetros de Subclasse
HS.FHIRServer.API.InteractionsStrategyStrategyKey — Especifica um identificador único para a InteractionsStrategy.
InteractionsClass — Especifica o nome da sua subclasse de Interactions (interações).
HS.FHIRServer.API.RepoManagerStrategyClass —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 classe Abstractque deve ser implementada para customizar o comportamento do Servidor FHIR.
  • HS.FHIRServer.API.RepoManager é uma classe Abstract que 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 Python
  • cls : 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.Interactions class

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 InteractionsStrategy
  • InteractionsClass : 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 servidor
  • OnAfterRequest : chamada após a requisição ser enviada ao servidor
  • PostProcessRead : chamada após a operação de leitura estar finalizada
  • PostProcessSearch : chamada após a operação de procura estar finalizada
  • Read : chamada para ler um recurso
  • Add : chamada para adicionar um recurso
  • Update : chamada para atualizar um recurso
  • Delete : 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.

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

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

Method %OnNew(pStrategy As HS.FHIRServer.Storage.Json.InteractionsStrategy) As%Status { // %OnNew é 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 %OnNew da 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_request da classe Python
    • passamos os objetos HS.FHIRServer.API.Service e HS.FHIRServer.API.Data.Request , o corpo da requisição e o tempo limite de execução.
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_request da classe Python
    • passamos os objetos HS.FHIRServer.API.Service, HS.FHIRServer.API.Data.Request e HS.FHIRServer.API.Data.Response e o corpo da resposta
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.

0
0 93
Artigo Larissa Prussak · Mar. 14, 2024 1m read

Participei do concurso InterSystems Women’s Health FHIR porque adorei o desafio de aprender uma tecnologia nova para mim. Eu queria desenvolver um aplicativo que recebesse dados de um dispositivo móvel como meu Fitbit ou Smart Watch. Não tive acesso a esses dados, exceto quando baixei uma planilha (arquivo CSV) mostrando meus passos diários e dados de sono. Vi no iris-fhir-template que ele importou alguns dados do paciente para um servidor FHIR. Eu descobri como usar uma transformação de dados para transformar um registro de um arquivo delimitado em dados FHIR que eu poderia armazenar no

0
0 76
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 4, 2024 8m read

Introdução

Este artigo busca explorar o funcionamento e desenvolvimento do sistema FHIR-PEX, aproveitando os recursos do InterSystems IRIS.

Otimizando a identificação e o processamento de exames médicos nos centros de diagnóstico clínico, nosso sistema visa aumentar a eficiência e precisão dos fluxos de trabalho de saúde. Ao integrar os padrões FHIR ao banco de dados Java-PEX do InterSystems IRIS, o sistema ajuda os profissionais de saúde com recursos de validação e roteamento, melhorando, em última análise, a tomada de decisões e o cuidado dos pacientes.

como funciona

  • Interoperabilidade IRIS: Recebe mensagens no padrão FHIR, garantindo a integração e a compatibilidade com os dados da saúde.

  • Processamento de informações com "PEX Java": Processa mensagens no formato FHIR e as direciona a tópicos do Kafka com base nas regras configuradas globalmente no banco de dados, possibilitando um processamento e roteamento de dados eficiente, especialmente para exames direcionados à quarentena.

  • Tratamento de retornos do Kafka via back-end Java externo: Interpreta apenas os exames direcionados à quarentena, permitindo que o sistema trate os retornos do Kafka por um back-end Java externo. Facilita a geração de insights prognósticos para profissionais da saúde através de IA Generativa, contando com consultas de resultados de exames anteriores dos respectivos pacientes.

Desenvolvimento

Através do PEX (Production EXtension) da InterSystems, ferramenta de extensibilidade que permite aprimoramento e personalização do comportamento do sistema, elaboramos uma Operação de Negócios. Este componente tem a tarefa de processar mensagens recebidas no formato FHIR dentro do sistema. Veja este exemplo:

import com.intersystems.enslib.pex.*;
import com.intersystems.jdbc.IRISObject;
import com.intersystems.jdbc.IRIS;
import com.intersystems.jdbc.IRISList;
import com.intersystems.gateway.GatewayContext;

import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.*;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class KafkaOperation extends BusinessOperation {
// Conexão ao InterSystems IRIS
private IRIS iris;

// Conexão ao Kafka
private Producer<Long, String> producer;

// Endereço do servidor do Kafka (se forem vários, separados por vírgula)
public String SERVERS;

// Nome do nosso Produtor
public String CLIENTID;

/// Caminho para o arquivo Config
public String CONFIG;

public void OnInit() throws Exception {
[...]
}

public void OnTearDown() throws Exception {
[...]
}

public Object OnMessage(Object request) throws Exception {
    IRISObject req = (IRISObject) request;
    LOGINFO("Received object: " + req.invokeString("%ClassName", 1));

    // Crie o registro
    String value = req.getString("Text");
    String topic = getTopicPush(req);
    final ProducerRecord<Long, String> record = new ProducerRecord<>(topic, value);

    // Envie o novo registro
    RecordMetadata metadata = producer.send(record).get();

    // Retorne as informações do regitro
    IRISObject response = (IRISObject)(iris.classMethodObject("Ens.StringContainer","%New",topic+"|"+metadata.offset()));
    return response;
}

private Producer<Long, String> createProducer() throws IOException {
[...]
}

private String getTopicPush(IRISObject req) {
[...]
}

[...]
}

`

Dentro da aplicação, o método getTopicPush assume a responsabilidade de identificar o tópico para o qual a mensagem será enviada.

A determinação do tópico a que a mensagem será enviada depende da existência de uma regra na global "quarantineRule", conforme lido no IRIS.

String code = FHIRcoding.path("code").asText();
String system = FHIRcoding.path("system").asText();

IRISList quarantineRule = iris.getIRISList("quarantineRule",code,system);

 String reference = quarantineRule.getString(1);
 String value = quarantineRule.getString(2);

 String observationValue = fhir.path("valueQuantity").path("value").asText()

Quando há a global ^quarantineRule, a validação do objeto FHIR pode ser validada.

private boolean quarantineValueQuantity(String reference, String value, String observationValue) {
    LOGINFO("quarantine rule reference/value: " + reference + "/" + value);
    double numericValue = Double.parseDouble(value);
    double numericObservationValue = Double.parseDouble(observationValue);

    if ("<".equals(reference)) {
        return numericObservationValue < numericValue;
    }
    else if (">".equals(reference)) {
        return numericObservationValue > numericValue;
    }
    else if ("<=".equals(reference)) {
        return numericObservationValue <= numericValue;
    }
    else if (">=".equals(reference)) {
        return numericObservationValue >= numericValue;
    }
    
    return false;
}

Exemplo prático:

Ao definir uma global, como:

Set ^quarantineRule("59462-2","http://loinc.org") = $LB(">","500") 

Isso estabelece uma regra para o código "59462-2" e o sistema ""http://loinc.org"" na global ^quarantineRule, especificando uma condição em que o valor é definido como quarentena quando ultrapassa 500. No aplicativo, o método getTopicPush pode então usar essa regra para determinar o tópico apropriado para enviar a mensagem com base no resultado da validação.

Dada a atribuição, o JSON abaixo seria enviado para quarentena, porque corresponde à condição especificada por ter:

 {
          "system": "http://loinc.org",
          "code": "59462-2",
          "display": "Testosterone"
}

"valueQuantity": { "value": 550, "unit": "ng/dL", "system": "http://unitsofmeasure.org", "code": "ng/dL" }

Observação FHIR:

{
    "resourceType": "Observation",
    "id": "3a8c7d54-1a2b-4c8f-b54a-3d2a7efc98c9",
    "status": "final",
    "category": [
      {
        "coding": [
          {
            "system": "http://terminology.hl7.org/CodeSystem/observation-category",
            "code": "laboratory",
            "display": "laboratory"
          }
        ]
      }
    ],
    "code": {
      "coding": [
        {
          "system": "http://loinc.org",
          "code": "59462-2",
          "display": "Testosterone"
        }
      ],
      "text": "Testosterone"
    },
    "subject": {
      "reference": "urn:uuid:274f5452-2a39-44c4-a7cb-f36de467762e"
    },
    "encounter": {
      "reference": "urn:uuid:100b4a8f-5c14-4192-a78f-7276abdc4bc3"
    },
    "effectiveDateTime": "2022-05-15T08:45:00+00:00",
    "issued": "2022-05-15T08:45:00.123+00:00",
    "valueQuantity": {
      "value": 550,
      "unit": "ng/dL",
      "system": "http://unitsofmeasure.org",
      "code": "ng/dL"
    }
}

O aplicativo Quarkus Java

Após o envio para o tópico desejado, um aplicativo Quarkus Java foi criado para receber exames em quarentena.

@ApplicationScoped
 public class QuarentineObservationEventListener {

@Inject
PatientService patientService;

@Inject
EventBus eventBus;

@Transactional
@Incoming("observation_quarantine")
public CompletionStage<Void> onIncomingMessage(Message<QuarentineObservation> quarentineObservationMessage) {
	var quarentineObservation = quarentineObservationMessage.getPayload();
	var patientId = quarentineObservation.getSubject()
			.getReference();
	var patient = patientService.addObservation(patientId, quarentineObservation);
	publishSockJsEvent(patient.getId(), quarentineObservation.getCode()
			.getText());
	return quarentineObservationMessage.ack();
}

private void publishSockJsEvent(Long patientId, String text) {
	eventBus.publish("monitor", MonitorEventDto.builder()
			.id(patientId)
			.message(" is on quarentine list by observation ." + text)
			.build());
}
 }

Esse segmento do sistema tem a tarefa de persistir as informações recebidas de Kafka, armazená-las nas observações do paciente no banco de dados e notificar a ocorrência ao monitor.

O monitor

Por fim, o monitor do sistema é responsável por fornecer uma visualização simples do front-end. Isso permite que os profissionais de saúde revisem os dados do paciente/exame e realizem as ações necessárias.

Implementação de langchainPT

Através do monitor, o sistema permite que os profissionais de saúde solicitem recomendações da IA ​​Generativa.

@Unremovable
@Slf4j
@ApplicationScoped
public class PatientRepository {
	@Tool("Get anamnesis information for a given patient id")
	public Patient getAnamenisis(Long patientId) {
		log.info("getAnamenisis called with id " + patientId);
		Patient patient = Patient.findById(patientId);
		return patient;
	}

	@Tool("Get the last clinical results for a given patient id")
	public List<Observation> getObservations(Long patientId) {
		log.info("getObservations called with id " + patientId);
		Patient patient = Patient.findById(patientId);
		return patient.getObservationList();
	}

}

segue implementação de langchain4j

@RegisterAiService(chatMemoryProviderSupplier = RegisterAiService.BeanChatMemoryProviderSupplier.class, tools = {PatientRepository.class})
public interface PatientAI {

	@SystemMessage("""
			You are a health care assistant AI. You have to recommend exams for patients based on history information.
			""")
	@UserMessage("""
			 Your task is to recommend clinical exams for the patient id {patientId}.

			 To complete this task, perform the following actions:
			 1 - Retrieve anamnesis information for patient id {patientId}.
			 2 - Retrieve the last clinical results for patient id {patientId}, using the property 'name' as the name of exam and 'value' as the value.
			 3 - Analyse results against well known conditions of health care.

			 Answer with a **single** JSON document containing:
			 - the patient id in the 'patientId' key
			 - the patient weight in the 'weight' key
			 - the exam recommendation list in the 'recommendations' key, with properties exam, reason and condition.
			 - the 'explanation' key containing an explanation of your answer, especially about well known diseases.

			Your response must be just the raw JSON document, without ```json, ``` or anything else.
			 """)
	String recommendExams(Long patientId);
}

Dessa forma, o sistema pode auxiliar os profissionais de saúde a tomar decisões e realizar ações.

Vídeo de demonstração

VÍDEO

Autores

OBSERVAÇÃO:

O aplicativo https://openexchange.intersystems.com/package/fhir-pex está participando atualmente do InterSystems Java Contest 2023. Sinta-se à vontade para explorar a solução e não hesite em entrar em contato se tiver alguma dúvida ou precisar de informações adicionais. Recomendamos executar o aplicativo em seu ambiente local para uma experiência prática. Obrigado pela oportunidade 😀!

0
0 85
Artigo Davi Massaru Teixeira Muta · Nov. 26, 2023 9m read

Introdução

Este artigo tem como objetivo explorar como o sistema FHIR-PEX funciona e foi desenvolvido, aproveitando os recursos do InterSystems IRIS.

Agilizando a identificação e o processamento de exames médicos em centros de diagnóstico clínico, nosso sistema visa aumentar a eficiência e a precisão dos fluxos de trabalho de saúde. Ao integrar os padrões FHIR com o banco de dados Java-PEX da InterSystems IRIS, o sistema ajuda os profissionais de saúde com recursos de validação e roteamento, contribuindo, em última análise, para melhorar a tomada de decisões e o atendimento ao paciente.

como funciona

  • Interoperabilidade IRIS: Recebe mensagens no padrão FHIR, garantindo integração e compatibilidade com dados de saúde.

  • Processamento de informações com 'PEX Java': Processa mensagens no formato FHIR e as direciona para tópicos Kafka com base em regras configuradas globalmente no banco de dados, facilitando o processamento e roteamento eficiente de dados, principalmente para exames direcionados à quarentena.

  • Tratamento de devoluções Kafka via back-end Java externo: Interpreta apenas os exames direcionados à quarentena, permitindo que o sistema trate os retornos do Kafka por meio de um backend Java externo. Facilita a geração de insights prognósticos para profissionais de saúde através de IA Generativa, contando com consultas de resultados de exames anteriores dos respectivos pacientes.

 

Desenvolvimento

Através do PEX (Production EXtension) da InterSystems, ferramenta de extensibilidade que permite aprimoramento e customização do comportamento do sistema, elaboramos uma Operação de Negócio. Este componente tem a tarefa de processar mensagens recebidas no formato FHIR dentro do sistema.

Segue exemplo:

    import com.intersystems.enslib.pex.;     import com.intersystems.jdbc.IRISObject;     import com.intersystems.jdbc.IRIS;     import com.intersystems.jdbc.IRISList;     import com.intersystems.gateway.GatewayContext;          import org.apache.kafka.clients.producer.;     import org.apache.kafka.common.serialization.*;          import com.fasterxml.jackson.databind.JsonNode;     import com.fasterxml.jackson.databind.ObjectMapper;          import java.io.FileInputStream;     import java.io.IOException;     import java.util.Properties;

    public class KafkaOperation extends BusinessOperation {     // Connection to InterSystems IRIS     private IRIS iris;          // Connection to Kafka     private Producer<Long, String> producer;          // Kafka server address (comma separated if several)     public String SERVERS;          // Name of our Producer     public String CLIENTID;          /// Path to Config File     public String CONFIG;          public void OnInit() throws Exception {     [...]     }          public void OnTearDown() throws Exception {     [...]     }          public Object OnMessage(Object request) throws Exception {         IRISObject req = (IRISObject) request;         LOGINFO("Received object: " + req.invokeString("%ClassName", 1));              // Create record         String value = req.getString("Text");         String topic = getTopicPush(req);         final ProducerRecord<Long, String> record = new ProducerRecord<>(topic, value);              // Send new record         RecordMetadata metadata = producer.send(record).get();              // Return record info         IRISObject response = (IRISObject)(iris.classMethodObject("Ens.StringContainer","%New",topic+"|"+metadata.offset()));         return response;     }          private Producer<Long, String> createProducer() throws IOException {     [...]     }          private String getTopicPush(IRISObject req) {     [...]     }          [...]     } `

Dentro da aplicação, o método getTopicPush assume a responsabilidade de identificar o tópico para o qual a mensagem será enviada.

A determinação para qual tópico a mensagem será enviada depende da existência de uma regra na “quarantineRule” global, conforme lida dentro do IRIS.

    String code = FHIRcoding.path("code").asText();     String system = FHIRcoding.path("system").asText();

    IRISList quarantineRule = iris.getIRISList("quarantineRule",code,system);

     String reference = quarantineRule.getString(1);      String value = quarantineRule.getString(2);

     String observationValue = fhir.path("valueQuantity").path("value").asText()

Quando o ^quarantineRule global existe, a validação do objeto FHIR pode ser validada.

    private boolean quarantineValueQuantity(String reference, String value, String observationValue) {         LOGINFO("quarantine rule reference/value: " + reference + "/" + value);         double numericValue = Double.parseDouble(value);         double numericObservationValue = Double.parseDouble(observationValue);

        if ("<".equals(reference)) {             return numericObservationValue < numericValue;         }         else if (">".equals(reference)) {             return numericObservationValue > numericValue;         }         else if ("<=".equals(reference)) {             return numericObservationValue <= numericValue;         }         else if (">=".equals(reference)) {             return numericObservationValue >= numericValue;         }                  return false;     }

Exemplo prático:

Ao definir um global, como:

    Set ^quarantineRule("59462-2","http://loinc.org") = $LB(">","500") 

Isso estabelece uma regra para o código "59462-2" e o sistema ""http://loinc.org"" na ^quarantineRule global, especificando uma condição onde o valor quando maior que 500 é definido como quarentena. No aplicativo, o método getTopicPush pode então usar essa regra para determinar o tópico apropriado para enviar a mensagem com base no resultado da validação.

Dada a atribuição, o JSON abaixo seria enviado para quarentena, pois corresponde à condição especificada por ter:

 {
          "system": "http://loinc.org",
          "code": "59462-2",
          "display": "Testosterone"
}

"valueQuantity": { "value": 550, "unit": "ng/dL", "system": "http://unitsofmeasure.org", "code": "ng/dL" }

FHIR Observation:

{
    "resourceType": "Observation",
    "id": "3a8c7d54-1a2b-4c8f-b54a-3d2a7efc98c9",
    "status": "final",
    "category": [
      {
        "coding": [
          {
            "system": "http://terminology.hl7.org/CodeSystem/observation-category",
            "code": "laboratory",
            "display": "laboratory"
          }
        ]
      }
    ],
    "code": {
      "coding": [
        {
          "system": "http://loinc.org",
          "code": "59462-2",
          "display": "Testosterone"
        }
      ],
      "text": "Testosterone"
    },
    "subject": {
      "reference": "urn:uuid:274f5452-2a39-44c4-a7cb-f36de467762e"
    },
    "encounter": {
      "reference": "urn:uuid:100b4a8f-5c14-4192-a78f-7276abdc4bc3"
    },
    "effectiveDateTime": "2022-05-15T08:45:00+00:00",
    "issued": "2022-05-15T08:45:00.123+00:00",
    "valueQuantity": {
      "value": 550,
      "unit": "ng/dL",
      "system": "http://unitsofmeasure.org",
      "code": "ng/dL"
    }
}

### Quarkus Java application

Após o envio para o tópico desejado, foi construída uma aplicação Quarkus Java para recebimento de exames em quarentena.

    @ApplicationScoped      public class QuarentineObservationEventListener {

    @Inject     PatientService patientService;

    @Inject     EventBus eventBus;

    @Transactional     @Incoming("observation_quarantine")     public CompletionStage<Void> onIncomingMessage(Message<QuarentineObservation> quarentineObservationMessage) {         var quarentineObservation = quarentineObservationMessage.getPayload();         var patientId = quarentineObservation.getSubject()                 .getReference();         var patient = patientService.addObservation(patientId, quarentineObservation);         publishSockJsEvent(patient.getId(), quarentineObservation.getCode()                 .getText());         return quarentineObservationMessage.ack();     }

    private void publishSockJsEvent(Long patientId, String text) {         eventBus.publish("monitor", MonitorEventDto.builder()                 .id(patientId)                 .message(" is on quarentine list by observation ." + text)                 .build());     }      }

Este segmento do sistema tem a tarefa de persistir as informações recebidas de Kafka, armazená-las nas observações do paciente no banco de dados e notificar a ocorrência ao monitor.

O monitor

Por fim, o monitor do sistema é responsável por fornecer uma visualização simples do front-end. Isso permite que os profissionais de saúde revisem os dados do paciente/exame e tomem as ações necessárias.

Implementação of langchain

Através do monitor, o sistema permite que os profissionais de saúde solicitem recomendações da IA ​​Generativa.

    @Unremovable     @Slf4j     @ApplicationScoped     public class PatientRepository {         @Tool("Get anamnesis information for a given patient id")         public Patient getAnamenisis(Long patientId) {             log.info("getAnamenisis called with id " + patientId);             Patient patient = Patient.findById(patientId);             return patient;         }              @Tool("Get the last clinical results for a given patient id")         public List<Observation> getObservations(Long patientId) {             log.info("getObservations called with id " + patientId);             Patient patient = Patient.findById(patientId);             return patient.getObservationList();         }          }

segue implementação de langchain4j

    @RegisterAiService(chatMemoryProviderSupplier = RegisterAiService.BeanChatMemoryProviderSupplier.class, tools = {PatientRepository.class})     public interface PatientAI {              @SystemMessage("""                 You are a health care assistant AI. You have to recommend exams for patients based on history information.                 """)         @UserMessage("""                  Your task is to recommend clinical exams for the patient id {patientId}.                       To complete this task, perform the following actions:                  1 - Retrieve anamnesis information for patient id {patientId}.                  2 - Retrieve the last clinical results for patient id {patientId}, using the property 'name' as the name of exam and 'value' as the value.                  3 - Analyse results against well known conditions of health care.                       Answer with a single JSON document containing:                  - the patient id in the 'patientId' key                  - the patient weight in the 'weight' key                  - the exam recommendation list in the 'recommendations' key, with properties exam, reason and condition.                  - the 'explanation' key containing an explanation of your answer, especially about well known diseases.                      Your response must be just the raw JSON document, without json, or anything else.                  """)         String recommendExams(Long patientId);     }

Dessa forma, o sistema pode auxiliar os profissionais de saúde na tomada de decisões e na execução de ações.

Video demonstração 

VIDEO

Autores

NOTA: 

O aplicativo https://openexchange.intersystems.com/package/fhir-pex está participando atualmente do InterSystems Java Contest 2023. Sinta-se à vontade para explorar a solução e não hesite em entrar em contato se tiver alguma dúvida ou precisar de informações adicionais. Recomendamos executar o aplicativo em seu ambiente local para uma experiência prática. Obrigado pela oportunidade 😀!

Artigo original: https://community.intersystems.com/post/sending-kafka-messages-java-pex-processing-quarantine-exam-prognoses

0
0 147