Pesquisar

Limpar filtro
Artigo
Danusa Calixto · Out. 27, 2022

Usando OAuth 2.0 / OIDC para login único em uma aplicação REST no IRIS

Digamos que eu esteja desenvolvendo uma aplicação web que usa o IRIS como back-end. Estou trabalhando nela com acesso não autenticado. Está chegando a hora em que eu gostaria de implantar para os usuários, mas preciso adicionar a autenticação primeiro. Em vez de usar a autenticação padrão de senha do IRIS, quero que os usuários façam login com o Login Único (single sign-on - SSO, na sigla em inglês) da minha organização ou outro provedor de identidade popular, como Google ou GitHub. Li que o OpenID Connect é um padrão de autenticação comum e compatível com o IRIS. Qual é a maneira mais simples de colocar isso em funcionamento? ### Exemplo 1: uma app CSP simples A documentação [aqui](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GOAUTH_client) apresenta uma opção bastante fácil para usar uma aplicação CSP como um cliente OpenID Connect. Estas são as etapas: 1. Configure o servidor OAuth 2.0 e a configuração do cliente no IRIS. Veja mais informações na seção "Caché configuration" do[excelente artigo do Daniel Kutac](https://community.intersystems.com/post/intersystems-iris-open-authorization-framework-oauth-20-implementation-part-1). 2. Copie a rotina OAUTH2.ZAUTHENTICATE do [repositório de amostras no GitHub](https://github.com/intersystems/Samples-Security/blob/master/rtn/OAUTH2.ZAUTHENTICATE.mac) para o namespace %SYS e renomeie como ZAUTHENTICATE. 3. Ative a autenticação delegada no sistema inteiro. 4. Crie uma página de login personalizada que seja uma extensão de %OAuth2.Login e substitua o método DefineParameters para especificar o nome do aplicativo OAuth 2.0 e os escopos: Class MyOAuth2.Login Extends %OAuth2.Login { ClassMethod DefineParameters(Output application As %String, Output scope As %String, Output responseMode As %String) { Set application="my application name" Set scope="openid profile email" Set responseMode=..#RESPONSEMODE Quit } } 5. Ative a aplicação web para a autenticação delegada e defina MyOAuth2.Login.cls como a página de login personalizada. 6. Um truque final: para a página de login personalizada funcionar, o usuário CSPSystem no IRIS precisa ter o acesso específico de LEITURA à base de dados em que está MyOAuth2.Login.cls. Neste momento, o login deve "simplesmente funcionar": a visita a uma página CSP nesta aplicação web redirecionará para a página de login no provedor de identidade. Depois de fazer o login, o usuário terá uma sessão CSP autenticada. O $username será igual ao identificador de sujeito do SSO/Google/GitHub/etc., então posso usar a autorização integrada do IRIS para determinar a que dar acesso. ### Exemplo 2: o problema com REST E se a aplicação web estiver usando um manipulador REST? O processo acima não funciona. Se uma aplicação web estiver habilitada para REST, não é possível definir uma página de login personalizada. Descobri que são necessárias mais algumas etapas para contornar isso. 7. Crie uma aplicação web separada sem REST habilitado. O caminho nessa app precisa começar com o caminho do app REST. Por exemplo, se o nome da app REST for "/csp/api", você poderá nomear essa nova app como "/csp/api/login". Ative a autenticação delegada e defina a página MyOAuth2.Login.cls como a página de login personalizada. 8. Defina o Caminho do Cookie da Sessão nessa nova app para o mesmo da app REST: por exemplo, "/csp/api". Assim, ambas as aplicações compartilharão uma sessão CSP. 9. Adicione uma página CSP a essa nova app para servir como uma "página inicial". Um usuário precisa primeiro acessar essa página para estabelecer sua sessão. Veja este exemplo que redireciona para um endpoint na API REST após o login: Class App.Home Extends %CSP.Page { ClassMethod OnPage() As %Status [ ServerOnly = 1 ] { &html<<script type="text/javascript"> window.location="/csp/api/test" </script>> return $$$OK } } 10. Certifique-se de que a classe do manipulador REST tenha o parâmetro UseSession substituído por true. Class API.REST Extends %CSP.REST { Parameter UseSession As BOOLEAN = 1; XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ] { <Routes> <Route Url="/test" Method="GET" Call="Test" Cors="true"/> </Routes> } ClassMethod Test() As %Status { write { "username": ($username) }.%ToJSON() return $$$OK } } Nesse ponto, o login na app REST também deverá "simplesmente funcionar". O usuário visitará a página inicial, será redirecionado para o login SSO e, finalmente, retornará a app REST, onde tem uma sessão CSP autenticada. Até onde sei, essa é a maneira mais fácil de adicionar o OpenID Connect a um app IRIS REST. Outra opção é usar a amostra "REST.ZAUTHENTICATE" do repositório de amostras de segurança. Com isso, espera-se que o front-end anexe um bearer token OAuth 2.0 a cada solicitação. No entanto, não há uma forma definida para o front-end obter esse token de acesso. Você terá que implementar esse fluxo OAuth por conta própria em JavaScript (ou usar uma biblioteca como [angular-oauth2-oidc](https://github.com/manfredsteyer/angular-oauth2-oidc).) Você também precisará se certificar de que o app JavaScript e o back-end IRIS estejam de acordo em todos os itens de configuração, como o endpoint do emissor do servidor de autorização, o ID do cliente OAuth 2.0 etc. Descobri que essa não é uma tarefa simples. Tenho curiosidade para saber se mais alguém está usando o OpenID Connect para autenticar uma app IRIS. Há uma maneira ainda mais simples? Ou vale a pena usar a abordagem mais complicada com bearer tokens? Comente aqui embaixo.
Anúncio
Rochael Ribeiro · Jun. 6, 2022

Lançamentos da Comunidade de Desenvolvedores, Maio de 2022

Bem vindos aos Lançamentos de Maio de 2022 da Comunidade de Desenvolvedores! Disponibilizamos algumas melhorias para aprimorar sua experiência recentemente na Comunidade InterSystems: 🆕 Rastreamento aprimorado dos eventos atuais 🆕 Postagem agendada 🆕 Formatação de código aprimorada 🆕 Criação de tabelas mais rápida 🆕 Experiência de resposta enriquecida 🆕 Alterado a prévia de design de postagens Vamos dar uma olhada mais próxima em todos os detalhes. AO VIVO AGORA para Eventos Para tornar os eventos atuais mais fáceis e convenientes de se localizar adicionamos uma nova seção AO VIVO AGORA no canto superior esquerdo da página. E se você clicar nele, será redirecionado para a página do evento, onde você pode se juntar ao evento (se for online, claro) Para tirar vantagem do uso desta funcionalidade, seus anúncios de eventos devem atender as seguintes condições: Crie seu evento normalmente [adicione a tag Eventos -> campos especiais aparecerão automaticamente] Verifique se seu evento possui horários de início e fim específicos (eventos de dia todo não são tratados nesta seção) Para permitir que pessoas se juntem ao seu evento online diretamente, adicione a opção Link direto para participar É isso, pronto para publicar! Agende sua postagem Você pediu e nós fizemos! Agora você pode agendar sua postagem para que seja publicada em um horário específico. Para agendar sua postagem para mais tarde, você precisa apenas clicar na seta para baixo, próxima ao botão Publicar, e escolher Agendar postagem. Depois, você poderá escolher a data e horário de quando sua postagem será publicada. Escolhido o horário, clique no botão Agendar postagem. Sua postagem será publicada no horário escolhido - é isso! Formatação de Código Para compartilhar seu código com outros, adicionamos um editor embutido para quando ocorrer a inserção de códigos. In it, you can select the appropriate programming language and tab size. Assim, o destaque da sintaxe ocorre imediatamente ao se escrever o texto e a linguagem de programação é exibida no canto superior esquerdo. Como resultado, na sua postagem, você terá um bonito código formatado, na linguagem de programação selecionada. Criação de Tabelas Para facilitar a formatação no momento da criação de tabelas, adicionamos uma função para rapidamente criar uma tabela, através da seleção do número desejado de células. Se você clicar no botão Mais em baixo, será aberta a funcionalidade padrão de criação de tabelas. Respostas e assinaturas Para visualizar todas as informações sobre a discussão rapidamente, adicionamos o número de respostas no topo do editor e também um ícone para assinar uma discussão e assim, postar uma nova resposta. Design da Prévia da Postagem Para tornar a parte de baixo da postagem mais balanceada, rearranjamos e alteramos os ícones. Aproveitem as atualizações! Enviem-nos suas requisições por melhorias e notificações de problemas no GitHub da Comunidade de Desenvolvedores. Ou poste suas sugestões nos comentários desta postagem. Nos vemos em breve com mais mudanças interessantes!
Artigo
Danusa Calixto · Nov. 4, 2022

Livro de receitas de implementação simples do ZPM

O ZPM foi projetado para trabalhar com aplicativos e módulos para a plataforma de dados IRIS da InterSystems. Ele consiste em dois componentes, o ZPN Client que é uma CLI para gerenciar módulos, e o The Registry que é um banco de dados de módulos e meta-informações. Podemos usar o ZPM para pesquisar, instalar, atualizar, remover e publicar módulos. Com o ZPM, você pode instalar classes ObjectScript, aplicativos Frontend, produções de interoperabilidade, soluções IRIS BI, conjuntos de dados IRIS ou quaisquer arquivos como rodas Python incorporadas. Hoje este livro de receitas passará por 3 seções: Instalar ZPM Gerar Módulo Encontre, instale, publique módulos dentro do Registro 1. Instale o ZPM Baixe a versão mais recente do ZPM (este deve ser um único arquivo XML) Link para Download Importe o XML que você baixou para o IRIS e ele só pode ser implantado no terminal IRIS, abra o IRIS e insira write $SYSTEM.OBJ.Load("C:\zpm.xml", "c") Observação "C:\zpm.xml" é o caminho do arquivo XML baixado, esta etapa pode demorar um pouco. Após terminar a instalação, basta digitar zpm, pressionar enter, você verá que está no shell zpm 2. Gerar Módulo Antes de começarmos a gerar o módulo, precisamos preparar uma pasta que tenha um ou mais arquivos prontos para carregar, por isso criei uma pasta na unidade C chamada zpm. Execute o comando generate C:/zpm Depois de especificar todo o necessário, seu primeiro módulo foi gerado com sucesso, você também verá Observação: a versão do módulo está usando versionamento semântico pasta de origem do módulo é a pasta que tem todo o arquivo de classe zpm também oferece uma opção para adicionar aplicativos web e dependência, neste exemplo vou deixar em branco Agora, abra o explorador de arquivos, você verá um arquivo chamado "module.xml" como você pode verificar na captura de tela abaixo Digite command load C:\ZPM\ você verá que seu módulo foi recarregado, validado, compilado e ativado 3. Encontre, instale, publique módulos dentro do Registro Encontre pacotes disponíveis no Registro atual: zpm:USER>search Instalar o pacote do registro atual como exemplo permite instalar um módulo chamado zpmshow no registro público: zpm:USER>install zpmshow (o comando é install "moduleName") Publicar módulo depois de carregado: zpm:USER>publish myFirstZPMDemo Você pode usar zpm:USER>search para verificar a publicação, no meu caso você pode ver que "myfirstzpmdemo 0.1.0" está no Registro atual. Nota: Se ocorrer um erro ao publicar um módulo que diz: "ERRO! Publicando módulo, algo deu errado", certifique-se de que o status do Registro atual esteja habilitado e disponível. Você pode usar zpm:USER>repo -list, para verificar o status do registro atual. Video disponível: Clique aqui
Artigo
Edilson Eberle Carvalho · Jun. 8, 2023

Legado - esse foi um de meus primeiros programas em MUMPS (Cobra Computadores) no ano de 1985 - só uma curiosidade

Incrível esse legado! Esse é um dos motivos principais do sucesso da Intersystems. Fiz um teste simples essa semana e não é que deu certo? Qual linguagem tem essa capacidade? Qual banco de dados pode-se ter Globais e Tabelas SQL? Isso sem falar na velocidade! Será covardia o BachMark entre MS-SQL e Caché. Qual o sistema que vc pode instalar em seu notebook e apresentar resultados como se estivesse em um Servidor? Apresentei para meus alunos, e eles ficaram fascinados com facilidade de programação, criação imediata de variáveis, globais, linha de código, etc. Como um aluno me disse: "O bagulho é loko"....rs O programa abaixo valida CNPJ e CPF, naquela época era CGC rs.... CGC(%CGC) ;PROGRAMA DE VALIDAÇÃO CGC/CPF ; ENTRADA ==> %CGC (CGC/CPF NUMERICO) ; SAIDA ==> VVALUE (CGC/CPF C/PONTUACAO) // PP9 (1-INVALIDO, 0-S/ERRO) N (VVALUE,PP9,%CGC) Q:%CGC="" %CGC S %G="" F %I=1:1:$L(%CGC) S:$E(%CGC,%I)?1N %G=%G_$E(%CGC,%I) S %CGC=%G I $L(%CGC)<14 G CPF S VVALUE=$E(%CGC,1,2)_"."_$E(%CGC,3,5)_"."_$E(%CGC,6,8)_"/"_$E(%CGC,9,12)_"-"_$E(%CGC,13,14),%CT=$L(%CGC),%N=$E(%CGC,1,%CT-2),%DG=$E(%CGC,%CT-1,%CT),%DI="",PP9=0 D CGC1 S %N=%N_%DC,%CT=%CT+1 D CGC1 S:%DG'=%DI PP9=1 K %N,%D,%DG,%CGC,%CT,%S,%P,%DC,%CTN,%I Q VVALUE CGC1 S %S=0,%P=2,%CTN=$L(%N) F %I=%CTN:-1:1 S %D=$E(%N,%I),%S=%P*%D+%S,%P=%P+1 S:%P>9 %P=2 S %DC=%S#11*-1+11 S:%DC>9 %DC=0 S %DI=%DI_%DC Q CPF S VVALUE=$E(%CGC,1,3)_"."_$E(%CGC,4,6)_"."_$E(%CGC,7,9)_"-"_$E(%CGC,10,11),%CT=$L(%CGC),%N=$E(%CGC,1,%CT-2),%DG=$E(%CGC,%CT-1,%CT),%DI="",PP9=0 D CGC2 S %N=%N_%DC,%CT=%CT+1 D CGC2 S:%DG'=%DI PP9=1 K %N,%D,%DG,%CGC,%CT,%S,%P,%DC,%CTN,%I Q VVALUE CGC2 S %S=0,%P=2,%CTN=$L(%N) F %I=%CTN:-1:1 S %D=$E(%N,%I),%S=%P*%D+%S,%P=%P+1 S %DC=%S#11*-1+11 S:%DC>9 %DC=0 S %DI=%DI_%DC Q
Artigo
Heloisa Paiva · Abr. 18

Comandando a Tripulação

![Image generated by OpenAI DALL·E](/sites/default/files/inline/images/images/a-modern-minimalistic-digital-illustrati_hr4pi9PwTwuB11Db4x7hng_RabPUGJrRzKzvwaMPpR_Ug.jpeg) Sou um grande fã de ficção científica, mas embora eu esteja totalmente a bordo da nave Star Wars (desculpas aos meus colegas Trekkies!), sempre apreciei os episódios clássicos de Star Trek da minha infância. A tripulação diversificada da USS Enterprise, cada um dominando suas funções únicas, é uma metáfora perfeita para entender os agentes de IA e seu poder em projetos como o Facilis. Então, vamos embarcar em uma missão intergaláctica, utilizando a IA como a tripulação da nossa nave e  ***audaciosamente ir*** audaciosamente ir ***homem jamais esteve***!  Esse conceito de trabalho em equipe é uma analogia maravilhosa para ilustrar como os agentes de IA funcionam e como os usamos em nosso projeto DC-Facilis. Então, vamos mergulhar e assumir o papel de um capitão de nave estelar, liderando uma tripulação de IA em territórios inexplorados! ## Bem-vindo ao CrewAI! Para gerenciar nossa tripulação de IA, usamos uma estrutura fantástica chamada [CrewAI](https://www.crewai.com/).É enxuta, extremamente rápida e opera como uma plataforma Python multiagente. Uma das razões pelas quais a amamos, além do fato de ter sido criada por outro brasileiro, é sua incrível flexibilidade e design baseado em funções. ```python from crewai import Agent, Task, Crew ``` ![the taken quote](https://media1.giphy.com/media/v1.Y2lkPTc5MGI3NjExcHVocmkyZ2R3ZGN6NmFzcW91NHcyOGN1Mjd1cGl1Z2VtMjlmNHg4diZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/UpWDPgxcHiR1e/giphy.gif) ### Conheça os Planejadores No Facilis, nossos agentes de IA são divididos em dois grupos. Vamos começar com o primeiro, que gosto de chamar de "Os Planejadores". ### O Agente de Extração O papel principal do Facilis é receber uma descrição em linguagem natural de um serviço REST e criar automaticamente toda a interoperabilidade necessária. Portanto, nosso primeiro membro da tripulação é o Agente de Extração. Este agente tem a tarefa de "extrair" as especificações da API a partir da descrição fornecida pelo usuário. Aqui está o que o Agente de Extração procura: - Host (obrigatório) - Endpoint (obrigatório) - Params (opcional) - Port (se disponível) - modelo JSON (para POST/PUT/PATCH/DELETE) - Autenticação (se aplicável) ```python def create_extraction_agent(self) -> Agent: return Agent( role='Extrator de Especificações de API', goal='Extrair especificações de API de descrições em linguagem natural', backstory=dedent(""" Você é especializado em interpretar descrições em linguagem natural e extrair especificações de API estruturadas. """), allow_delegation=True, llm=self.llm ) def extract_api_specs(self, descriptions: List[str]) -> Task: return Task( description=dedent(f""" Extraia as especificações de API das seguintes descrições: {json.dumps(descriptions, indent=2)} Para cada descrição, extraia: - Host (obrigatório) - Endpoint (obrigatório) - Params (opcional) - Port (se disponível) - modelo JSON (para POST/PUT/PATCH/DELETE) - Autenticação (se aplicável) Marque quaisquer campos obrigatórios ausentes como 'missing'. Retorne os resultados em formato JSON como um array de especificações. """), expected_output="""Um array JSON contendo as especificações de API extraídas com todos os campos obrigatórios e opcionais""", agent=self.extraction_agent ) ``` ### O Agente de Validação Próximo na fila, o Agente de Validação! Sua missão é garantir que as especificações de API coletadas pelo Agente de Extração estejam corretas e consistentes. Ele verifica: 1. Formato de host válido 2. Endpoint começando com '/' 3. Métodos HTTP válidos (GET, POST, PUT, DELETE, PATCH) 4. Número de porta válido (se fornecido) 5. Presença de modelo JSON para métodos aplicáveis. ```python def create_validation_agent(self) -> Agent: return Agent( role='API Validator', goal='Validar especificações de API quanto à correção e consistência.', backstory=dedent(""" Você é um especialista em validação de API, garantindo que todas as especificações atendam aos padrões e formatos necessários. """), allow_delegation=False, llm=self.llm ) def validate_api_spec(self, extracted_data: Dict) -> Task: return Task( description=dedent(f""" Valide a seguinte especificação de API: {json.dumps(extracted_data, indent=2)} Verifique: 1. Formato de host válido 2. Endpoint começando com '/' 3. Métodos HTTP válidos (GET, POST, PUT, DELETE, PATCH) 4. Número de porta válido (se fornecido) 5. Presença de modelo JSON para métodos aplicáveis. Retorne os resultados da validação em formato JSON. """), expected_output="""Um objeto JSON contendo os resultados da validação com quaisquer erros ou confirmação de validade""", agent=self.validation_agent ) ``` ### O Agente de Interação Avançando, conhecemos o Agente de Interação, nosso Especialista em Interação com o Usuário. Seu papel é obter quaisquer campos de especificação de API ausentes que foram marcados pelo Agente de Extração e validá-los com base nas descobertas do Agente de Validação. Eles interagem diretamente com os usuários para preencher quaisquer lacunas. ### O Agente de Produção Precisamos de duas informações cruciais para criar a interoperabilidade necessária: namespace e nome da produção. O Agente de Produção interage com os usuários para coletar essas informações, de forma muito semelhante ao Agente de Interação. ### O Agente de Transformação de Documentação Assim que as especificações estiverem prontas, é hora de convertê-las em documentação OpenAPI. O Agente de Transformação de Documentação, um especialista em OpenAPI, cuida disso. ```python def create_transformation_agent(self) -> Agent: return Agent( role='Especialista em Transformação OpenAPI', goal='Converter especificações de API em documentação OpenAPI', backstory=dedent(""" Você é um especialista em especificações e documentação OpenAPI. Seu papel é transformar detalhes de API validados em documentação OpenAPI 3.0 precisa e abrangente. """), allow_delegation=False, llm=self.llm ) def transform_to_openapi(self, validated_endpoints: List[Dict], production_info: Dict) -> Task: return Task( description=dedent(f""" Transforme as seguintes especificações de API validadas em documentação OpenAPI 3.0: Informações de Produção: {json.dumps(production_info, indent=2)} Endpoints Validados: {json.dumps(validated_endpoints, indent=2)} Requisitos: 1. Gerar especificação OpenAPI 3.0 completa 2. Incluir schemas de requisição/resposta apropriados 3. Documentar todos os parâmetros e corpos de requisição 4. Incluir autenticação se especificado 5. Garantir formatação de caminho apropriada Retorne a especificação OpenAPI nos formatos JSON e YAML. """), expected_output="""Um objeto JSON contendo a especificação OpenAPI 3.0 completa com todos os endpoints e schemas.""", agent=self.transformation_agent ) ``` ### The Review Agent Após a transformação, a documentação OpenAPI passa por uma revisão meticulosa para garantir conformidade e qualidade. O Agente de Revisão segue esta lista de verificação: 1.Conformidade OpenAPI 3.0 - Especificação de versão correta - Elementos raiz obrigatórios - Validação da estrutura do schema 2. Completude - Todos os endpoints documentados - Parâmetros totalmente especificados - Schemas de requisição/resposta definidos - Esquemas de segurança configurados corretamente 3. Verificações de Qualidade - Convenções de nomenclatura consistentes - Descrições claras - Uso adequado de tipos de dados - Códigos de resposta significativos 4. Melhores Práticas - Uso adequado de tags - Nomenclatura de parâmetros consistente - Definições de segurança apropriadas Finalmente, se tudo parecer bom, o Agente de Revisão reporta um objeto JSON saudável com a seguinte estrutura: ```json { "is_valid": boolean, "approved_spec": object (a especificação OpenAPI revisada e possivelmente corrigida), "issues": [array de strings descrevendo quaisquer problemas encontrados], "recommendations": [array de sugestões de melhoria] } ``` ```python def create_reviewer_agent(self) -> Agent: return Agent( role='Revisor de Documentação OpenAPI', goal='Garantir a conformidade e qualidade da documentação OpenAPI', backstory=dedent(""" Você é a autoridade final em qualidade e conformidade de documentação OpenAPI. Com vasta experiência em especificações OpenAPI 3.0, você revisa meticulosamente a documentação para precisão, completude e adesão aos padrões. """), allow_delegation=True, llm=self.llm ) def review_openapi_spec(self, openapi_spec: Dict) -> Task: return Task( description=dedent(f""" Revise a seguinte especificação OpenAPI para conformidade e qualidade: {json.dumps(openapi_spec, indent=2)} Lista de Verificação da Revisão:: 1. Conformidade OpenAPI 3.0 - Verificar a especificação de versão correta - Verificar os elementos raiz obrigatórios - Validar a estrutura do schema 2. Completude - Todos os endpoints devidamente documentados - Parâmetros totalmente especificados - Schemas de requisição/resposta definidos - Esquemas de segurança configurados corretamente 3. Quality Checks - Consistent naming conventions - Clear descriptions - Proper use of data types - Meaningful response codes 4. Best Practices - Proper tag usage - Consistent parameter naming - Appropriate security definitions Você deve retornar um objeto JSON com a seguinte estrutura: {{ "is_valid": boolean, "approved_spec": object (a especificação OpenAPI revisada e possivelmente corrigida), "issues": [array de strings descrevendo quaisquer problemas encontrados], "recommendations": [array de sugestões de melhoria] }} """), expected_output=""" Um objeto JSON contendo: is_valid (boolean), approved_spec (object), issues (array), e recommendations (array)""", agent=self.reviewer_agent ) ``` ### O Agente Iris O último agente no grupo do planejador é o Agente Iris, que envia a documentação OpenAPI finalizada para o Iris. ```python def create_iris_i14y_agent(self) -> Agent: return Agent( role='Especialista em Integração Iris I14y', goal='Integrar especificações de API com o serviço Iris I14y', backstory=dedent(""" Você é responsável por garantir uma integração suave entre o sistema de documentação da API e o serviço Iris I14y. Você lida com a comunicação com o Iris, valida as respostas e garante a integração bem-sucedida das especificações da API. """), allow_delegation=False, llm=self.llm ) def send_to_iris(self, openapi_spec: Dict, production_info: Dict, review_result: Dict) -> Task: return Task( description=dedent(f""" Enviar a especificação OpenAPI aprovada para o serviço Iris I14y: Informações de Produção: - Nome: {production_info['production_name']} - Namespace: {production_info['namespace']} - É Novo: {production_info.get('create_new', False)} Status da Revisão: - Aprovado: {review_result['is_valid']} Retornar o resultado da integração em formato JSON. """), expected_output="""Um objeto JSON contendo o resultado da integração com o serviço Iris I14y, incluindo o status de sucesso e os detalhes da resposta.""", agent=self.iris_i14y_agent ) class IrisI14yService: def __init__(self): self.logger = logging.getLogger('facilis.IrisI14yService') self.base_url = os.getenv("FACILIS_URL", "http://dc-facilis-iris-1:52773") self.headers = { "Content-Type": "application/json" } self.timeout = int(os.getenv("IRIS_TIMEOUT", "504")) # in milliseconds self.max_retries = int(os.getenv("IRIS_MAX_RETRIES", "3")) self.logger.info("IrisI14yService initialized") async def send_to_iris_async(self, payload: Dict) -> Dict: """ Enviar carga útil para o endpoint de geração do Iris de forma assíncrona. """ self.logger.info("Enviando carga útil para o endpoint de geração do Iris.") if isinstance(payload, str): try: json.loads(payload) except json.JSONDecodeError: raise ValueError("Invalid JSON string provided") retry_count = 0 last_error = None # Cria timeout para o aiohttp timeout = aiohttp.ClientTimeout(total=self.timeout / 1000) # Converte ms para seconds while retry_count < self.max_retries: try: self.logger.info(f"Attempt {retry_count + 1}/{self.max_retries}: Enviando requisição para {self.base_url}/facilis/api/generate") async with aiohttp.ClientSession(timeout=timeout) as session: async with session.post( f"{self.base_url}/facilis/api/generate", json=payload, headers=self.headers ) as response: if response.status == 200: return await response.json() response.raise_for_status() except asyncio.TimeoutError as e: retry_count += 1 last_error = e error_msg = f"Timeout occurred (attempt {retry_count}/{self.max_retries})" self.logger.warning(error_msg) if retry_count < self.max_retries: wait_time = 2 ** (retry_count - 1) self.logger.info(f"Waiting {wait_time} seconds before retry...") await asyncio.sleep(wait_time) continue except aiohttp.ClientError as e: error_msg = f"Failed to send to Iris: {str(e)}" self.logger.error(error_msg) raise IrisIntegrationError(error_msg) error_msg = f"Failed to send to Iris after {self.max_retries} attempts due to timeout" self.logger.error(error_msg) raise IrisIntegrationError(error_msg, last_error) ``` ## Conheça os Geradores Nosso segundo conjunto de agentes são os Geradores. Eles estão aqui para transformar as especificações OpenAPI em interoperabilidade InterSystems IRIS. Há oito deles neste grupo. O primeiro deles é o Agente Analisador. Ele é como o planejador, traçando a rota. Seu trabalho é mergulhar nas especificações OpenAPI e descobrir quais componentes de Interoperabilidade IRIS são necessários. ```python def create_analyzer_agent(): return Agent( role="Analisador de Especificações OpenAPI", goal="Analisar minuciosamente as especificações OpenAPI e planejar os componentes de Interoperabilidade IRIS", backstory="""Você é um especialista em especificações OpenAPI e em Interoperabilidade InterSystems IRIS. Seu trabalho é analisar documentos OpenAPI e criar um plano detalhado de como eles devem ser implementados como componentes de Interoperabilidade IRIS.""", verbose=False, allow_delegation=False, tools=[analyze_openapi_tool], llm=get_facilis_llm() ) analysis_task = Task( description="""Analisar a especificação OpenAPI e planejar os componentes de Interoperabilidade IRIS necessários. Incluir uma lista de todos os componentes que devem estar na classe de Produção."", agent=analyzer, expected_output="Uma análise detalhada da especificação OpenAPI e um plano para os componentes IRIS, incluindo a lista de componentes de Produção", input={ "openapi_spec": openApiSpec, "production_name": "${production_name}" } ) ``` Em seguida, os **Agentes de Business Services (BS) e Business Operations (BO)** assumem o controle. Eles geram os Business Services e as Business Operations com base nos endpoints OpenAPI. Eles usam uma ferramenta útil chamada MessageClassTool para gerar as classes de mensagens perfeitas, garantindo a comunicação. ```python def create_bs_generator_agent(): return Agent( role="Gerador de Produção e Business Service IRIS", goal="Gerar classes de Produção e Business Service IRIS formatadas corretamente a partir de especificações OpenAPI", backstory="""Você é um desenvolvedor InterSystems IRIS experiente, especializado em Produções de Interoperabilidade. Sua expertise reside na criação de Business Services e Produções capazes de receber e processar requisições de entrada com base emespecificações de API."", verbose=False, allow_delegation=True, tools=[generate_production_class_tool, generate_business_service_tool], llm=get_facilis_llm() ) def create_bo_generator_agent(): return Agent( role="Gerador de Business Operation IRIS", goal="Gerar classes de Business Operation IRIS formatadas corretamente a partir de especificações OpenAPI", backstory="""Você é um desenvolvedor InterSystems IRIS experiente, especializado em Produções de Interoperabilidade. Sua expertise reside na criação de Business Operations capazes de enviar requisições para sistemas externos com base em especificações de API.""", verbose=False, allow_delegation=True, tools=[generate_business_operation_tool, generate_message_class_tool], llm=get_facilis_llm() ) bs_generation_task = Task( description="Gerar classes de Business Service com base nos endpoints OpenAPI", agent=bs_generator, expected_output="Definições de classes de Business Service do IRIS", context=[analysis_task] ) bo_generation_task = Task( description="Gerar classes de Business Operation com base nos endpoints OpenAPI", agent=bo_generator, expected_output="Definições de classes de Business Operation do IRIS", context=[analysis_task] ) class GenerateMessageClassTool(BaseTool): name: str = "generate_message_class" description: str = "Gerar uma classe de Mensagem IRIS" input_schema: Type[BaseModel] = GenerateMessageClassToolInput def _run(self, message_name: str, schema_info: Union[str, Dict[str, Any]]) -> str: writer = IRISClassWriter() try: if isinstance(schema_info, str): try: schema_dict = json.loads(schema_info) except json.JSONDecodeError: return "Error: Invalid JSON format for schema info" else: schema_dict = schema_info class_content = writer.write_message_class(message_name, schema_dict) # Armazenar a classe gerada. writer.generated_classes[f"MSG.{message_name}"] = class_content return class_content except Exception as e: return f"Error generating message class: {str(e)}" ``` Depois que BS e BO fazem o que têm que fazer, é a hora do Agente de Produção brilhar! Este agente junta tudo para criar um ambiente de produção coeso. Depois que tudo estiver configurado, o próximo na linha é o Agente de Validação. Este entra em cena para uma verificação final, garantindo que cada classe Iris esteja ok. Em seguida, temos o Agente de Exportação e o Agente de Coleção. O Agente de Exportação gera os arquivos .cls, enquanto o Agente de Coleção reúne todos os nomes de arquivos. Tudo é passado para o importador, que compila tudo no InterSystems Iris. ```python def create_exporter_agent(): return Agent( role="Exportador de Classes IRIS", goal="Exportar e validar definições de classes IRIS para arquivos .cls adequados", backstory="""Você é um especialista em implantação InterSystems IRIS. Seu trabalho é garantir que as definições de classes IRIS geradas sejam devidamente exportadas como arquivos .cls válidos que possam ser importados diretamente para um ambiente IRIS.""", verbose=False, allow_delegation=False, tools=[export_iris_classes_tool, validate_iris_classes_tool], llm=get_facilis_llm() ) def create_collector_agent(): return Agent( role="Coletor de Classes IRIS", goal="Coletar todos os arquivos de classes IRIS gerados em uma coleção JSON", backstory="""Você é um especialista em sistema de arquivos responsável por reunir e organizar os arquivos de classes IRIS gerados em uma coleção estruturada.""", verbose=False, allow_delegation=False, tools=[CollectGeneratedFilesTool()], llm=get_facilis_llm() ) export_task = Task( description="Exportar todas as classes IRIS geradas como arquivos .cls válidos", agent=exporter, expected_output="Arquivos .cls IRIS válidos salvos no diretório de saída", context=[bs_generation_task, bo_generation_task], input={ "output_dir": "/home/irisowner/dev/output/iris_classes" # Optional } ) collection_task = Task( description="Coletar todos os arquivos de classes IRIS gerados em uma coleção JSON", agent=collector, expected_output="Coleção JSON de todos os arquivos .cls gerados", context=[export_task, validate_task], input={ "directory": "./output/iris_classes" } ) ``` ## Limitações e Desafios Nosso projeto começou como um experimento empolgante, onde meus colegas mosqueteiros e eu almejávamos criar uma ferramenta totalmente automatizada usando agentes. Foi uma jornada selvagem! Nosso foco principal estava em integrações de API REST. É sempre uma alegria receber uma tarefa com uma especificação OpenAPI para integrar; no entanto, sistemas legados podem ser uma história completamente diferente. Pensamos que automatizar essas tarefas poderia ser incrivelmente útil. Mas toda aventura tem suas reviravoltas: Um dos maiores desafios foi instruir a IA a converter OpenAPI para Interoperabilidade Iris. Começamos com o modelo openAI GPT3.5-turbo, que em testes iniciais se mostrou difícil com depuração e prevenção de interrupções. A mudança para o Anthropic Claude 3.7 Sonnet mostrou melhores resultados para o grupo Gerador, mas não tanto para os Planejadores... Isso nos levou a dividir nossas configurações de ambiente, usando diferentes provedores de LLM para flexibilidade. Usamos GPT3.5-turbo para planejamento e Claude sonnet para geração, uma ótima combinação! Essa combinação funcionou bem, mas encontramos problemas com alucinações. A mudança para o GT4o melhorou os resultados, mas ainda enfrentamos alucinações na criação de classes Iris e, às vezes, especificações OpenAPI desnecessárias, como o renomado exemplo Pet Store OpenAPI. Nos divertimos muito aprendendo ao longo do caminho, e estou super animado com o futuro incrível nesta área, com inúmeras possibilidades!
Artigo
Bob Kuszewski · Maio 20, 2021

Migrando do Java Business Host para o PEX

# Migrando do Java Business Host para o PEX Com o lançamento do [PEX](https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=EPEX) a partir do InterSystems IRIS 2020.1 e InterSystems IRIS for Health 2020.1, nossos clientes tem agora uma melhor forma de utilizar Java nas Produções de interoperabilidade que através da utilização do Java Business Host. O PEX (Production EXtension) disponibiliza um conjunto completo de APIs para criar componentes de interoperabilidade e está disponível tanto em Java quanto em .NET. O Java Business Host foi descontinuado e será aposentado em versões futuras. Vantagens do PEX * Permite que desenvolvedores criem qualquer componente da Produção de interoperabilidade em Java ou em .NET * Estruturas de mensagens mais complexas podem ser enviadas através dos componentes * Configurações simplificadas * Fluxo de trabalho simplificado , sem utilização de ObjectScript. O resto deste artigo está focado em como realizar a migração de código existente do Java Business Host para o PEX. ## Visão Geral As classes e interfaces utilizadas pelo PEX são diferentes das do Java Business Host (JBH). Nós iremos disponibilizar uma visão geral das diferenças neste artigo mas a [documentação completa](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=EPEX_apiref) te dará maior profundidade sobre o assunto. * [Convertendo um Business Service](#Converting-a-Business-Service-from-Java-Business-Host-to-PEX) * [Convertendo um Business Operation](#Converting-a-Business-Operation-from-Java-Business-Host-to-PEX) * [Configurações](#Settings) * [Mensagens](#Messages) * [Registrando](#Logging) ## Convertendo um Business Service do Java Business Host para o PEX Para criar um Business Service PEX você precisará implementar a `com.intersystems.enslib.pex.BusinessService` ao invés da `com.intersystems.gateway.bh.BusinessService`. O padrão de design utilizado pelo PEX para Business Service mudou de um onde era esperado que o serviço iniciasse o processo de produção de mensagens para o padrão onde o serviço implementa uma função que é invocada periodicamente para produzir mensagens. No JBH, seu código seria algo como isso ```java @Override public boolean OnInit(Production p) throws Exception { production = p; if (messageThread == null) { Messager messager = new Messager(); messageThread = new Thread(messager); messageThread.start(); } return true; } ``` No PEX, você precisa apenas implementas três funções ```java public void OnInit() throws Exception { // Inicialização return; } public Object OnProcessInput(Object messageInput) throws Exception { // Aqui é onde você invoca a SendMessage() ou SendMessageAsync() return null; } public void OnTearDown() throws Exception { // Desligar return; } ``` Você também precisará alterar como as configurações são utilizadas, mensagens são entregues e onde o registro de log é feito. Falaremos mais sobre estas alterações abaixo. ## Convertendo um Business Operation do Java Business Host para PEX Para criar um Business Operation PEX você precisará implementar a `com.intersystems.enslib.pex.BusinessOperation` ao invés da `com.intersystems.gateway.bh.BusinessOperation`. O padrão de design para os Business Operations é estruturalmente o mesmo entre o JBH e o PEX entretanto os parâmetros para os dois principais pontos de entrada forma alterados. ### Mudanças no OnInit() No PEX o `OnInit()` não recebe parâmetros. ### Mudanças no OnMessage() No PEX o `OnMessage()` recebe um `Object` genérico no lugar da `String` utilizada no JBH. Isto permite que o criador da produção de interoperabilidade envie qualquer tipo de mensagem desejada. No JBH sua aplicação ficaria mais ou menos algo assim: ```java public boolean OnMessage(String message) throws Exception { // Lógica de negócio aqui return true; } ``` No PEX o parâmetro é um Objeto Java genérico, que você deve instanciar apropriadamente, que permite que você transmita mensagens mais complexas que apenas strings. Aqui está um exemplo de como extrair uma requisição que é do tipo file stream. ```java public Object OnMessage(Object request) throws Exception { com.intersystems.jdbc.IRISObject streamContainer = (com.intersystems.jdbc.IRISObject)request; com.intersystems.jdbc.IRISObject str = (com.intersystems.jdbc.IRISObject)streamContainer.get("Stream"); String originalFilename = (String)streamContainer.get("OriginalFilename"); Long contentSize = (Long)str.get("Size"); String content = (String)str.invoke("Read", contentSize); // Lógica de negócio aqui return null; } ``` Você também precisará alterar como as configurações são utilizadas, mensagens são entregues e onde o registro de log é feito. Falaremos mais sobre estas alterações abaixo. ## Settings A declaração das configurações também foi simplificada. No JBH as configurações era declaradas através da string `SETTINGS` e recuperada através de código, algo como isso: ```java String setting = production.GetSetting("Min"); if (!setting.isEmpty()) { min = Integer.parseInt(setting); } ``` No PEX as configurações são apenas campos de membros públicos, desta forma elas são automaticamente carregadas quando a classe é instanciada. ```java public int Min = 0; ``` Qualquer campo de membro público está disponível para ser atribuído em sua produção de interoperabilidade desde que o campo do membro seja um tipo base Java (String, int, etc.). ## Mensagens O envio de Mensagens no PEX é mais poderoso. No JBH mensagens são enviadas como strings. No PEX as mensagens são enviadas como objetos - tanto IRISObject, para objetos definidos em ObjectScript, ou uma subclasse subclass of `com.intersystems.enslib.pex.Message`, para classes definidas em Java. No JBH seu código seria algo assim ```java production.SendRequest(value.toString()); ``` No PEX seria algo como isso ```java MyExampleMessageClass req = new MyExampleMessageClass("mensagem a ser enviada"); SendRequestAsync(Target, req); ``` ## Logging As funções para registro de Logs são todas similares, apenas nomeadas de forma diferente. No PEX você registraria uma mensagem informativa através da `LOGINFO()` ```java LOGINFO("mensagem recebida"); ``` ## Gateway de Objetos O Java Business Host precisava de seu próprio gateway. Com o PEX você pode utilizar um Java gateway apenas para toda sua necessiade de Java. Ou então usar vários, você escolhe. Aqui você encontra uma boa [introdução ao gateway](https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=EJVG_intro). ## Conclusão e Feedback Se você ainda não experimentou utilizar o PEX, o que falta ? O PEX disponibiliza a habilidade de resolver uma quantidade muito mais ampla de problemas de negócio com menos codificação, além de permitir que agora tudo também seja feito em .NET. Se você tiver dúvidas ou problemas migrando suas aplicações de JBH para PEX, entre em contato conosco pelo WRC.
Artigo
Luana Machado · Jun. 12, 2023

Exemplo do FHIR e IntegratedML

Exemplo de uso do banco de dados FHIR InterSystems IRIS for Health para efetuar modelagem ML através do InterSystems IRIS IntegratedML Descrição IntegratedML é uma ótima funcionalidade para treino/teste e implantação de modelos ML. FHIR é um padrão poderoso para a interoperabilidade de informações da saúde. Esse projeto visa mostrar como utilizar as ferramentas IRIS/IRIS for Health. Por exemplo, as transformações DTL para preparar dados do FHIR para aplicação de modelos ML dentro do IntegratedML. Aqui estão algumas potenciais aplicações das ideias apresentadas nesse projeto: Reutilização e extensão de transformações DTL dentro de outras bases de dados FHIR para modelos ML personalizados Utilização das transformações DTL para normalizar mensagens FHIR e publicar modelos ML como serviços Criação de um referencial de modelos e regras de transformação para usar em qualquer conjunto de dados FHIR Instalação Clone o repositório em um diretório local: $ git clone https://github.com/jrpereirajr/fhir-integratedml-example.git Abra o terminal nesse repositório e execute: $ cd fhir-integratedml-example $ docker-compose up -d Se quiser obter um log do que aconteceu durante a instalação, utilize o seguinte comando : $ docker-compose up -d > build-log.txt 2>&1 Inicialização de um terminal IRIS Para inicializar um terminal IRIS, siga esses passos: Em um terminal powershell/cmd, execute: docker exec -it fhir-integratedml-example_iris_1 bash Dentro do shell do linux , crie uma sessão IRIS: irissession iris Demonstração Para demonstrar o conceito do projeto, foram configurados dois modelos: Um modelo de predição de não-comparecimento à uma consulta; Um modelo de predição de insuficiência cardíaca. Primeiro, conjuntos de dados de treinamento foram usados para gerar recursos FHIR sintéticos. Esses conjuntos de dados continham informações sobre pacientes, patologias, observações, consultas e visitas enviadas aos pacientes, representadas por diversos recursos FHIR. Essa etapa emula um banco de dados FHIR real, no qual as previsões de não-comparecimento e insuficiência cardíaca podem ser aplicadas. Quando o banco de dados FHIR estiver pronto para uso, os dados devem ser transformados, combinando recursos FHIR que são pertinentes ao problema, em tabelas únicas. Essa combinação de FHIR é obtida por transformações DTL, como NoShowDTL e HeartFailureDTL : Como as transformações DTL podem ser exportadas/importadas, é possível compartilhar os modelos ML aplicados aos dados FHIR. Essas transformações podem igualmente ser entendidas por outras equipes, se necessário. Depois de aplicar as transformações DTL, os recursos FHIR são mapeados em linhas simples, criando também tabelas que podem ser utilizadas para formar modelos ML para as previsões de não-comparecimento e insuficiência cardíaca. Para treinar e testar os modelos usando o IntegratedML, utilize as seguintes instruções SQL. Eles são executadas durante a instalação, mas você pode executá-los novamente e experimentar o IntegratedML por conta própria. Modelo de não-comparecimento -- criar um conjunto de dados de treinamento CREATE OR REPLACE VIEW PackageSample.NoShowMLRowTraining AS SELECT * FROM PackageSample.NoShowMLRow WHERE ID < 1800 -- criar um conjunto de dados de teste CREATE OR REPLACE VIEW PackageSample.NoShowMLRowTest AS SELECT * FROM PackageSample.NoShowMLRow WHERE ID >= 1800 -- evitar os erros no comando CREATE MODEL; ignore qualquer erro aqui DROP MODEL NoShowModel -- um modelo IntegratedML para a coluna de previsão noShow (não-apresentação) é criada a partir de outros modelos, utilizando o conjunto de dados PackageSample.NoShowMLRowTraining para a etapa de treinamento; o parâmetro "Semente" (seed) aqui destina-se à assegurar a reprodutibilidade dos resultados. CREATE MODEL NoShowModel PREDICTING (Noshow) FROM PackageSample.NoShowMLRowTraining USING {"seed": 6} -- o modelo é treinado, conforme definido no comando de criação de um modelo "CRIAR UM MODELO" TRAIN MODEL NoShowModel -- as informações sobre o modelo treinado são exibidas, como por exemplo o modelo ML selecionado pelo IntegratedML SELECT * FROM INFORMATION_SCHEMA.ML_TRAINED_MODELS -- a função de previsão (PREDICT) é utilizada para ver como usar o modelo nas intruções SQL SELECT top 10 PREDICT(NoShowModel) AS PredictedNoshow, Noshow AS ActualNoshow FROM PackageSample.NoShowMLRowTest -- uma validação em um conjunto de dados de teste é efetuado e as métricas de performance do modelo são calculadas VALIDATE MODEL NoShowModel FROM PackageSample.NoShowMLRowTest -- as métricas de performance são exibidas SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS Modelo de insuficiência cardíaca -- criar um conjunto de dados de treinamento CREATE OR REPLACE VIEW PackageSample.HeartFailureMLRowTraining AS SELECT DEATHEVENT,age,anaemia,creatininephosphokinase,diabetes,ejectionfraction,highbloodpressure,platelets,serumcreatinine,serumsodium,sex,smoking,followuptime FROM PackageSample.HeartFailureMLRow WHERE ID < 200 -- criar um conjunto de dados de teste CREATE OR REPLACE VIEW PackageSample.HeartFailureMLRowTest AS SELECT DEATHEVENT,age,anaemia,creatininephosphokinase,diabetes,ejectionfraction,highbloodpressure,platelets,serumcreatinine,serumsodium,sex,smoking,followuptime FROM PackageSample.HeartFailureMLRow WHERE ID >= 200 -- evitar os erros no comando CREATE MODEL; ignorar todo erro aqui DROP MODEL HeartFailureModel -- as informações sobre o modelo formado são exibidas, como por exemplo o modelo ML selecionado pelo IntegratedML CREATE MODEL HeartFailureModel PREDICTING (DEATHEVENT) FROM PackageSample.HeartFailureMLRowTraining USING {"seed": 6} -- o modelo é treinado, conforme definido no comando de criação de um modelo "CRIAR UM MODELO" TRAIN MODEL HeartFailureModel -- as informações sobre o modelo treinado são exibidas, como por exemplo o modelo ML selecionado pelo IntegratedML SELECT * FROM INFORMATION_SCHEMA.ML_TRAINED_MODELS -- a função de previsão (PREDICT) é utilizada para ver como usar o modelo nas intruções SQL SELECT top 10 PREDICT(HeartFailureModel) AS PredictedHeartFailure, DEATHEVENT AS ActualHeartFailure FROM PackageSample.HeartFailureMLRowTest -- uma validação em um conjunto de dados de teste é efetuado e as métricas de performance do modelo são calculadas VALIDATE MODEL HeartFailureModel FROM PackageSample.HeartFailureMLRowTest -- as métricas de performance são exibidas SELECT * FROM INFORMATION_SCHEMA.ML_VALIDATION_METRICS A última instrução SQL pode indicar os parâmetros de performance da classificação: A mesma transformação pode ser aplicada para transformar os recursos FHIR provenientes dos sistemas externos através de uma API REST, por exemplo (veja o código): Solução de problemas Se você tiver erros durante as requisições API, indicando que o modelo não existe, é provável que alguma coisa anormal se produziu durante a criação do contêiner para os modelos de treinamento. Tente executar o método de treinamento novamente. Abra o terminal IRIS e execute: ZN "FHIRSERVER" Do ##class(PackageSample.Utils).TrainNoShowModel() Do ##class(PackageSample.Utils).TrainHeartFailureModel() Referências Recursos FHIR utilizados como modelos: ttp://hl7.org/fhir/ Conjunto de dados para a formação de modelo de não-comparecimento: IntegratedML templat Conjunto de dados para a formação de modelo de insuficiência cardíaca: kaggle
Artigo
Claudio Devecchi · Fev. 8, 2021

Paciente no centro de todas as Informações - Parte 2

### HealthShare Patient Index Enterprise Master Patient Index - Este é o nome dado ao processo que faz com que os inúmeros cadastros e registros coletados dos vários sistemas das instituições e redes de saúde sejam identificados univocamente e interligados através de um identificador único por indivíduo. Isto viabiliza uma infinidade de benefícios para as instituições ou redes de saúde, pois permite, além da gestão das duplicidades em um mesmo sistema de prontuário eletrônico, que todos os dados segregados por número de cadastro sejam visualizados de forma consolidada por indivíduo. Cada vez mais, as instituições estão buscando uma abordagem holística do cuidado contínuo, da prevenção e da experiência centrada no paciente. Falando sobre o produto [Healthshare Patient Index](https://learning.intersystems.com/course/view.php?id=632) da InterSystems, podemos dividi-lo em 6 grandes grupos de funcionalidades. ### 1 - Integração das informações cadastrais dos pacientes Esta etapa engloba todos os mecanismos de coleta das informações nos sistemas de origem, seja através de API’s com protocolos específicos de saúde como o HL7, seja através de processos específicos ou customizados. Neste ponto, é importante que a plataforma de integração ofereça uma série de requisitos de interoperabilidade que assegure flexibilidade, governança e segurança. É muito comum sistemas de prontuário eletrônico internacionais fornecerem nativamente exportações de dados usando o protocolo HL7, que neste caso também é nativo nos produtos da Intersystems. Os sistemas nacionais geralmente demandam processos menos padronizados, e é por isso que é necessário que a plataforma seja de fácil e rápida implementação. Geralmente as informações são enviadas ou disponibilizadas no momento em que o paciente é admitido nos estabelecimentos de saúde. A arquitetura deste processo é definida conforme as necessidades de cada organização e disponibilidade dos recursos computacionais. ### 2 - Análise Qualitativa e Normalização Normalizar significa trazer para um mesmo plano de comparação informações demográficas que foram cadastradas de formas completamente diferentes. É também nesta etapa que todo o “lixo” é removido. Isto não quer dizer que a informação seja ruim, mas que não serve para o processo do MPI. Se os dados fossem comparados sem esta etapa, provavelmente cadastros de um mesmo indivíduo nunca seriam comparados, dada à discrepância de sistema para sistema. Se observarmos o processo de cadastro de cada estabelecimento e sistema de origem, veremos uma infinidade de formas de entrada dos dados e diversas maneiras de armazenamento das informações. Isso depende de como cada sistema foi concebido e como cada processo foi implementado em cada setor da organização. Um exemplo típico é o processo de admissão em alas emergenciais. Muitas vezes o paciente precisa ser atendido antes mesmo de ser identificado. Isso gera uma série de especificidades que precisam ser tratadas em uma análise qualitativa antes mesmo da implementação do processo de captura dos dados. O outro exemplo muito comum é o cadastramento de recém nascidos. Cada caso é um caso. Em alguns sistemas os nomes são cadastrados com um prefixo “RN DE” seguido pelo nome da mãe. Isso porque os pais não sabem o nome dos bebês antes do parto e eles já precisam constar nos sistemas de prontuário eletrônico. Como todos os sistemas geralmente exigem o CPF, eles podem ser cadastrados com o mesmo CPF da mãe. É claro que este é só um exemplo de uma situação pontual, mas cada caso deve ser estudado e endereçado da forma mais adequada possível. Além das especificidades de processo, há as que são de armazenamento dos dados. Documentos como CPF, RG e carteirinhas de seguro são armazenados com pontos e traços em alguns sistemas. Em outros são armazenados sem. O mesmo ocorre com datas. Nomes geralmente são armazenados em um único campo, uns com caixa baixa, outros com alta. Alguns são abreviados pela limitação de caracteres. Endereços são os vilões na normalização. Os sistemas mais modernos são baseados no CEP, outros não. Os que não são sofrem muitas abreviações devidos aos sufixos, títulos ou até mesmo pela limitação dos caracteres. Enfim, mesmo que em instituições mais modernas tecnologicamente, há sempre os sistemas legados. Estes também são incorporados ao processo de MPI porque trazem informações históricas valiosas para todo o processo assistencial. Culturalmente e diferentemente dos sistemas norte americanos, os sistemas brasileiros possuem um único campo para capturar os nomes. Para melhorar a eficácia do processo de vinculação é importante que os sistemas tenham a capacidade de separar os nomes, considerando também os primeiros nomes compostos. Outra capacidade não menos importante é a capacidade de trabalhar com as abreviações nos endereços. Isto requer um dicionário específico de abreviações para o nosso país. ### 3 - Indexação ou formação dos pares de comparação Nesta etapa é que se decide quais serão os cadastros que serão comparados entre si, formando assim os chamados pares de comparação. A decisão de se comparar registros não se baseia apenas nos documentos do paciente, assim como o CPF. Isso acontece porque há casos que não se tem o CPF do paciente ou casos que os filhos recebem o CPF dos pais. Para isto é necessário que o processo utilize os dados probabilísticos, assim como o nome, a data de nascimento, o sexo, os dados de contato e o endereço. É preciso que este processo tenha algoritmos sofisticados para que os sistemas não gerem um número excessivo de comparações indevidas, assim como não deixem de fora comparações necessárias. Por exemplo: A comparação de todos os “Josés” com todos os outros “Josés” não seria tão eficaz, pois poderia acarretar numa sobrecarga de processamento. Outro ponto não menos importante nesta fase é a capacidade de se trabalhar com algoritmos fonéticos para o mercado brasileiro, que são completamente diferentes dos algoritmos americanos. Isso significa que nomes escritos de maneiras diferentes ou equivocadas também serão considerados no processo. Exemplo: Dois cadastros de um determinado paciente com os nomes Walter Xavier e Valter Chavier podem se referir ao mesmo indivíduo. O Healthshare MPI utiliza um processo extremamente eficiente de análise combinatória que evita este tipo de problema, utilizando tanto informações demográficas determinísticas quanto probabilísticas. ### 4 - Pontuação dos pares Para cada par de comparação gerado, todas as variáveis demográficas são pontuadas separadamente: Primeiros nomes, nomes do meio, sobrenomes, documentos, sexo, cep, telefones, e-mails e endereços. Antes de iniciar a comparação, é determinado um peso com uma pontuação máxima e mínima para cada variável, considerando a singularidade de cada uma. Por exemplo, o sexo possui um peso menor que a data de nascimento, que possui peso menor que o nome, que possui um peso menor que o CPF. E assim por diante. Cada variável possui um algoritmo específico não binário de comparação que vai atribuir uma pontuação entre a mínima e a máxima para cada variável demográfica. Exemplo: Se o sexo for o mesmo, serão atribuídos 2 pontos. Se não for o mesmo será atribuída a pontuação mínima, -4 pontos. Se o CPF for o mesmo, serão atribuídos 14 pontos, Primeiros nomes e sobrenomes comuns também recebem pontuações menores que nomes mais comuns, assim como Silva e Souza. Nomes de casada e solteira também devem ser considerados aqui no Brasil. ### 5 - Avaliação e determinação do identificador unívoco dos pares Antes desta etapa, é necessário configurar as faixas de pontuação ou limiares que serão utilizados para vincular (mesmo indivíduo) ou não vincular (indivíduos diferentes) os pares de cadastro. Neste ponto, pode-se definir também a faixa de pontuação dos pares que irão para uma lista de trabalho, passíveis de uma avaliação ou revisão humana. Limiares a serem configurados: **Vínculo Automático** – Acima de quantos pontos os pares serão automaticamente vinculados. Exemplo: Se o total de pontos dos pares estiver acima de 35 os mesmos serão automaticamente vinculados e não necessitarão de revisão humana. **Vínculo com posterior avaliação na Lista de Trabalho** – Entre quantos pontos os pares irão para a lista de trabalho como vinculados (mesmos indivíduos) para avaliação humana. Exemplo: Os pares entre 30 e 35 pontos serão vinculados, mas poderão sofrer revisão de um profissional ou equipe designados para esta tarefa. **Não vínculo** – Abaixo de quantos pontos os pares não serão vinculados. Exemplo: Se o total de pontos dos pares for abaixo de 30 eles não serão vinculados (indivíduos diferentes). **Não vínculo com revisão posterior na Lista de Trabalho** – Entre quantos pontos os pares irão para a lista de trabalho como não vinculado (indivíduos diferentes) para uma revisão humana. Exemplo: Os pares entre 25 e 30 pontos não serão vinculados, mas poderão sofrer revisão de um profissional ou equipe designados para esta tarefa. Há várias situações de exceções, onde mesmo pares com pontuação elevada podem não se referir ao mesmo indivíduo. Um exemplo típico são os gêmeos, que moram na mesma residência. Para isso é necessário que o produto disponibilize de artifícios para identificar estes casos. Há outras situações que pares com baixa pontuação podem sofrer revisões se determinadas situações ocorrerem. Exemplo: pares com o mesmo CPF e data de nascimento e baixa pontuação. Este caso é no mínimo curioso, pois pode apontar um problema na baixa qualidade dos dados. No HealthShare Patient Index, estes dispositivos são chamados de regras de vinculação (rules), que prevalecem sobre a regra de pontuação. O produto já possui nativamente uma série de regras de exceção e elas são fundamentais para a segurança e confiabilidade de todo o processo. Após esta etapa, todos os cadastros recebem um identificador universal denominado MPIID - Master Patient Index Identification. Os cadastros que possuírem o mesmo MPIID são referentes ao mesmo indivíduo. ### 6 – Serviços e API’s ### Concluindo todo o processo de vinculação (Matching), é essencial que a plataforma ofereça maneiras passivas ou ativas de se comunicarem ou interoperarem com os sistemas de origem ou outros sistemas. Neste momento entram novamente todos os requisitos de interoperabilidade do produto, que já estão presentes na plataforma HealthShare da Intersystems. As API’s de consumo do MPI são disponibilizadas neste momento através de protocolos conhecidos (HTTP Soap ou Rest) para que sistemas consigam obter as informações desejadas para diversos casos de uso. Estes são alguns exemplos comuns de consumo de API’s do HealthShare MPI: • Obter identificadores de outros sistemas partindo do identificador do sistema consumidor. Este tipo de consulta é denominada pelo IHE como PIX. Exemplo: Antes de enviar a prescrição para o laboratório o sistema de origem envia o seu identificador do cadastro e recebe uma resposta da API com o número do identificador do mesmo paciente no laboratório. • Realizar pesquisas probabilísticas por dados demográficos. Exemplo: Consultar se existem cadastros demográficos para o paciente de nome Claudio Devecchi Junior. Este tipo de consulta é denominada pelo IHE como PDQ). • Obter o melhor dado demográfico (Golden Record ou Composite Record) de um determinado paciente para enriquecer os cadastros demográficos ou para aproveitar os seus dados no momento de um determinado cadastro. Existem também mecanismos ativos, onde o MPI se comunica com os sistemas para enviar informações úteis. Estes mecanismos também podem ser acionados de forma passiva através da chamada das Api’s. Alguns exemplos são: • No momento que um cadastro está sendo incluído ou atualizado o MPI pode fazer uma chamada retornando o identificador universal - MPIID. Desta forma o sistema de origem sempre ficará atualizado com este identificador. Quaisquer mudanças nas Listas de Trabalho são gatilhos para este tipo de chamada (callback) • Quando algum cadastro for incluído e o MPI identificar que já existe esta mesma pessoa no mesmo sistema de origem, já é possível enviar uma notificação de duplicidade. Para os casos de resolução das duplicidades, é importante que exista um serviço específico para receber as mensagens de fusão de pacientes (PIX merge). ### Conclusão Todo o processo descrito anteriormente demonstra um pouco de como o produto HealthShare Patient Index trata dos desafios na área com relação aos cadastros e identificação dos pacientes e como é importante tratar das especificidades não somente do país, mas de organização para organização. No próximo artigo, falaremos um pouco de como funciona o Healthshare Unified Care Record (UCR) e de como ele é fundamental para ajudar as instituições na abordagem holística do cuidado contínuo, da prevenção e da experiência centrada no paciente.
Artigo
Heloisa Paiva · Abr. 28

Gerando uma Classe ObjectScript a partir de uma Resposta JSON Usando o ChatGPT.

Tenho um novo projeto para armazenar informações de respostas REST em um banco de dados IRIS. Precisarei sincronizar informações de pelo menos duas dúzias de endpoints REST separados, o que significa criar quase essa quantidade de classes ObjectScript para armazenar os resultados desses endpoints. Poderia usar o ChatGPT para ter uma vantagem inicial na criação dessas classes? A resposta é "Sim", o que é ótimo, já que esta é minha primeira tentativa de usar IA generativa para algo útil. Gerar fotos de girafas tomando sopa estava ficando meio batido... Aqui está o que eu fiz: Obter alguma saída de chamada REST JSON de amostra usando "curl" para consultar o endpoint. Visitar o ChatGPT (a InterSystems fornece uma licença para toda a empresa para os funcionários) Tenha o seguinte diálogo com o ChatGPT. Coloquei o seguinte pedido no prompt "Message ChatGPT": Eu: Gere uma classe ObjectScript que armazenará dados do seguinte JSON: Apertei "Enter" antes de inserir o JSON, então o ChatGPT me direcionou da seguinte forma: ChatGPT: Por favor, forneça a estrutura JSON para a qual você gostaria que eu gerasse a classe ObjectScript. Se você tiver um objeto JSON de exemplo, cole-o aqui e eu criarei a classe InterSystems ObjectScript correspondente para você. Eu inseri o seguinte no prompt: { "id": 335818632, "appId": 113, "appName": "Batch", "contentId": 182455380233, "subject": "Test Subject", "name": "Test email campaign name", "counters": { "processed": 3910, "deferred": 86, "unsubscribed": 17, "statuschange": 17, "bounce": 153, "mta_dropped": 8, "dropped": 299, "delivered": 3749, "sent": 3903, "click": 16, "open": 323 }, "lastProcessingFinishedAt": 1733232499000, "lastProcessingStartedAt": 1733232303000, "lastProcessingStateChangeAt": 1733232499000, "numIncluded": 4201, "processingState": "DONE", "scheduledAt": 1733232300000, "type": "BATCH_EMAIL" } Resposta do ChatGPT: Aqui está a classe InterSystems ObjectScript que mapeia a estrutura JSON fornecida: Class MyNamespace.MyJSONData Extends (%Persistent, %JSON.Adaptor) { Property id As %Integer; Property appId As %Integer; Property appName As %String; Property contentId As %Integer; Property subject As %String; Property name As %String; Property counters As MyNamespace.Counters; Property lastProcessingFinishedAt As %BigInt; Property lastProcessingStartedAt As %BigInt; Property lastProcessingStateChangeAt As %BigInt; Property numIncluded As %Integer; Property processingState As %String; Property scheduledAt As %BigInt; Property type As %String; ClassMethod %OnJSONImport(jsonObject As %Object, instance As MyNamespace.MyJSONData) As %Status { Quit ..%JSONImport(jsonObject, .instance) } } Class MyNamespace.Counters Extends (%SerialObject, %JSON.Adaptor) { Property processed As %Integer; Property deferred As %Integer; Property unsubscribed As %Integer; Property statuschange As %Integer; Property bounce As %Integer; Property mta_dropped As %Integer; Property dropped As %Integer; Property delivered As %Integer; Property sent As %Integer; Property click As %Integer; Property open As %Integer; } Junto com uma explicação: Explicação: Mynamespace.MyJSONData: A principal classe representando o objeto JSON. As propriedades mapeiam diretamente para os campos no JSON. Ela estende tanto %Persistent quanto %JSON.Adaptor para persistência e compatibilidade com JSON. Mynamespace.Counters: Uma classe aninhada para representar o objeto counters no JSON. Esta classe é definida como um %SerialObject, pois faz parte do objeto principal e não precisa de persistência independente. Manipulação de JSON: O %JSON.Adaptor lida automaticamente com a serialização e desserialização de/para o formato JSON. O método %OnJSONImport permite a personalização da importação JSON, embora o método padrão %JSONImport seja suficiente para esse mapeamento direto. Você pode salvar esse código em um arquivo de classe e usá-lo para analisar ou armazenar dados JSON. Avise se quiser recursos adicionais ou mais personalizações! Este código é um ótimo começo, mas eu gostaria que ele se encaixasse no meu pacote chamado“MyPkg”e desse o nome "EmailCampaign" para a classe. Além disso, usando um pouco de NI (Network Intelligence), sei que os valores que geraram as propriedades "%BigInt" são, na verdade, tempos Linux e as propriedades deveriam ser "%Library.PosixTime". Faço o seguinte pedido (não tenho certeza por que estou dizendo "por favor" para uma máquina; acho que meus pais fizeram um bom trabalho): Eu: Podemos recriar a classe novamente, renomeando o pacote para "MyPkg" e a Classe como "EmailCampaign"? Além disso, por favor, altere os tipos das propriedades de %BigInt para %Library.PosixTime. Resposta do ChatGPT: Aqui está a classe ObjectScript atualizada com o nome do pacote e o nome da classe especificados, e as propriedades de timestamp atualizadas para usar %Library.PosixTime: Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor) { Property id As %Integer; Property appId As %Integer; Property appName As %String; Property contentId As %Integer; Property subject As %String; Property name As %String; Property counters As MyPkg.Counters; Property lastProcessingFinishedAt As %Library.PosixTime; Property lastProcessingStartedAt As %Library.PosixTime; Property lastProcessingStateChangeAt As %Library.PosixTime; Property numIncluded As %Integer; Property processingState As %String; Property scheduledAt As %Library.PosixTime; Property type As %String; ClassMethod %OnJSONImport(jsonObject As %Object, instance As MyPkg.EmailCampaign) As %Status { Quit ..%JSONImport(jsonObject, .instance) } } Class MyPkg.Counters Extends (%SerialObject, %JSON.Adaptor) { Property processed As %Integer; Property deferred As %Integer; Property unsubscribed As %Integer; Property statuschange As %Integer; Property bounce As %Integer; Property mta_dropped As %Integer; Property dropped As %Integer; Property delivered As %Integer; Property sent As %Integer; Property click As %Integer; Property open As %Integer; } Junto com esta explicação: Alterações Feitas: Nome do Pacote e da Classe: O pacote agora é CRMBI. A classe principal foi renomeada para EmailCampaign. Propriedades de Timestamp: lastProcessingFinishedAt, lastProcessingStartedAt, lastProcessingStateChangeAt e scheduledAt agora utilizam %Library.PosixTime. %Library.PosixTime é apropriado para propriedades que armazenam timestamps no formato UNIX. Outras Propriedades: Todos os outros campos permanecem inalterados e seguem a estrutura original do JSON. Esta classe está pronta para lidar com a serialização/desserialização JSON e fornece o mapeamento correto para o objeto aninhado counters. Me avise se precisar de mais ajustes ou tiver novos requisitos! Depois de copiar o código "como está" para o VSCode, problemas de sintaxe com as chaves foram destacados. Classes ObjectScript não habilitam definições de classe internas. Pedir ao ChatGPT para corrigir o problema das "chaves" movendo a classe interna MyPkg.Counters para uma classe separada é uma "não-operação"; um humano precisará mover essa classe para outro arquivo. O ChatGPT renomeou essa classe após o seguinte pedido: Eu: A classe como está escrita não compila. Podemos mover a classe interna chamada MyPkg.Counters para fora da classe principal para sua própria classe e renomeá-la para MyPkg.CampaignCounters? Resposta do ChatGPT: Aqui está o código ObjectScript atualizado com a classe MyPkg.CampaignCounters movida para fora como sua própria classe: Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor) { Property id As %Integer; Property appId As %Integer; Property appName As %String; Property contentId As %Integer; Property subject As %String; Property name As %String; Property counters As MyPkg.CampaignCounters; Property lastProcessingFinishedAt As %Library.PosixTime; Property lastProcessingStartedAt As %Library.PosixTime; Property lastProcessingStateChangeAt As %Library.PosixTime; Property numIncluded As %Integer; Property processingState As %String; Property scheduledAt As %Library.PosixTime; Property type As %String; ClassMethod %OnJSONImport(jsonObject As %Object, instance As MyPkg.EmailCampaign) As %Status { Quit ..%JSONImport(jsonObject, .instance) } } Class MyPkg.CampaignCounters Extends (%SerialObject, %JSON.Adaptor) { Property processed As %Integer; Property deferred As %Integer; Property unsubscribed As %Integer; Property statuschange As %Integer; Property bounce As %Integer; Property mta_dropped As %Integer; Property dropped As %Integer; Property delivered As %Integer; Property sent As %Integer; Property click As %Integer; Property open As %Integer; } Depois de colar o código acima no VSCode e compilá-lo, o compilador relatou que o tipo chamado "%Object" não existe em ObjectScript. De acordo com a documentação do IRIS, a propriedade para o primeiro parâmetro do método %JSONImport() de %JSON.Adaptor é flexível: “O argumento de entrada é JSON como uma string ou stream, ou uma subclasse de %DynamicAbstractObject.” Depois de remover a propriedade "As %Object" para o primeiro parâmetro de %OnJSONImport() na primeira classe e separar as duas classes em arquivos separados, a classe MyPkg.EmailCampaign compilou com sucesso. No entanto, a MyPkg.CampaignCounters não compilou; o ChatGPT não sabe (ainda?) que o sublinhado é um caractere especial em ObjectScript (o operador de concatenação de strings) Vamos tentar ensiná-lo. Eu: O caractere sublinhado "_" é um caractere especial em ObjectScript. Por favor, altere as propriedades com um sublinhado em seus nomes para nomes em camel case e remova o sublinhado. Resposta do ChatGPT: Aqui está o código atualizado com as propriedades que tinham sublinhados em seus nomes convertidas para camel case: Class MyPkg.EmailCampaign Extends (%Persistent, %JSON.Adaptor) { Property id As %Integer; Property appId As %Integer; Property appName As %String; Property contentId As %Integer; Property subject As %String; Property name As %String; Property counters As MyPkg.CampaignCounters; Property lastProcessingFinishedAt As %Library.PosixTime; Property lastProcessingStartedAt As %Library.PosixTime; Property lastProcessingStateChangeAt As %Library.PosixTime; Property numIncluded As %Integer; Property processingState As %String; Property scheduledAt As %Library.PosixTime; Property type As %String; ClassMethod %OnJSONImport(jsonObject As %Object, instance As MyPkg.EmailCampaign) As %Status { Quit ..%JSONImport(jsonObject, .instance) } } Class MyPkg.CampaignCounters Extends (%SerialObject, %JSON.Adaptor) { Property processed As %Integer; Property deferred As %Integer; Property unsubscribed As %Integer; Property statusChange As %Integer; Property bounce As %Integer; Property mtaDropped As %Integer; Property dropped As %Integer; Property delivered As %Integer; Property sent As %Integer; Property click As %Integer; Property open As %Integer; } Junto com esta explicação: Mudanças Realizadas: 1. Conversão para Camel Case: Propriedades em CampaignCounters com underscores agora são escritas em camel case: statuschange → statusChange mta_dropped → mtaDropped 2. Nomes de Propriedades em JSON: ObjectScript irá mapear automaticamente nomes de propriedades em camel case para seus correspondentes com underscore no JSON usando o %JSON.Adaptor. Este código atualizado compila corretamente e preserva a funcionalidade para serialização/desserialização JSON! Avise-me se precisar de mais ajustes! O ChatGPT foi além e alterou outra propriedade, uma nomeada com duas palavras separadas, para camel case também (statuschange); estou mantendo essa alteração, já que outras propriedades com nomes semelhantes na classe principal estão todas em camel case. Com o código gerado mais recente em dois arquivos separados (e removendo a definição "As %Object" na definição do método %OnJSONImport()), todo o código compila com sucesso. Essas classes geradas são um excelente ponto de partida para o que preciso e repetirei o processo para as outras fontes de dados REST para este projeto, me poupando muita digitação desnecessária.
Artigo
Danusa Calixto · Jul. 27, 2022

Como configurar um "Mirror" programaticamente

**Histórico** | Versão| Data | Atualizações| | :----- | :-- | :----- | | V1 | 2022-02-08 | Início | | V1.1 | 2022-04-06 | Geração de certificados com arquivo sh em vez de pki-scriptUsando variáveis de ambiente em arquivos de configuração| Olá Comunidade, Vocês já configuraram um ambiente espelhado? Tem rede privada, IP virtual, e configuração SSL? Após fazer isso algumas vezes, eu me dei conta que isso é longo, e há várias ações manuais obrigatórias para gerar os certificados e configurar cada instancia IRIS. É uma dor no pescoço para quem tem que fazer isso muitas vezes. Por exemplo, um time de Quality Assurance pode precisar criar um novo ambiente para cada nova versão da aplicação para testar. O time de suporte pode exigir a criação de um ambiente para reproduzir um problema complexo. Nós definitivamente precisamos de ferramentas para criá-los rapidamente. Neste artigo iremos criar um exemplo para configurar um Mirror com: - Arbitro - Primário - Membro de backup - Membro assíncrono de relatorio (leitura e escrita) - Configuração SSL para transferencia de Journal entre os nós - Rede privada para o Mirror - Endereço IP virtual - Um banco de dados espelhado ![network-schema](https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/img/net-schema.png) À primeira vista, parece um pouco complexo e parece que precisa de muito código, mas não se preocupe. Existem bibliotecas hospedadas no OpenExchange para realizar facilmente a maioria das operações. O objetivo deste artigo é fornecer um exemplo de como adaptar o processo às suas necessidades, mas não é um guia de práticas recomendadas em termos de segurança. Então, vamos criar nossa amostra. ### Ferramentas e bibliotecas - [config-api](https://openexchange.intersystems.com/package/Config-API): Esta biblioteca será utilizada para configurar o IRIS. Ela suporta configuração de espelhamento desde a versão 1.1.0. Não iremos dar uma descrição detalhada de como usar esta biblioteca. Já existe uma série de artigos. [here](https://community.intersystems.com/post/environment-setup-config-api). Resumindo, config-api será usada para criar um modelo de arquivos de configuração IRIS (formato JSON) e carregá-los facilmente. - [ZPM](https://openexchange.intersystems.com/package/ObjectScript-Package-Manager). - Docker. - OpenSSL. ### Github Você pode encontrar todos os arquivos necessários em [iris-mirroring-samples repository](https://github.com/lscalese/iris-mirroring-samples/). ### Prepare o seu sistema Clone o repositorio existente: ```bash git clone https://github.com/lscalese/iris-mirroring-samples cd iris-mirroring-samples ``` Se você preferir criar uma amostra do zero, ao invés de clonar o repositório, basta criar um novo diretório com subdiretórios: `backup` e `config-files`. Baixe [irissession.sh](https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/session.sh) : ``` mkdir -p iris-mirroring-samples/backup iris-mirroring-samples/config-files cd iris-mirroring-samples wget -O session.sh https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/session.sh ``` Para evitar o problema de "permissão negada" posteriormente, precisamos criar o grupo 'irisowner', o usuário 'irisowner', e alterar o grupo do diretório de backup para 'irisowner' ```bash sudo useradd --uid 51773 --user-group irisowner sudo groupmod --gid 51773 irisowner sudo chgrp irisowner ./backup ``` Este diretório será utilizado como volume para compartilhar um backup do banco de dados após configurar o primeiro membro espelho com os outros nós. ### Obtenha uma licença IRIS O espelhamento não está disponível com a edição da comunidade IRIS. Se você ainda não tiver uma licença de contêiner IRIS válida, conecte-se ao [Worldwide Response Center (WRC)](https://wrc.intersystems.com) com suas credenciais. Clique em "Ações" --> "Distribuição online", depois no botão "Avaliações" e selecione "Licença de avaliação"; Preencha o formulário. Copie seu arquivo de licença `iris.key` para este diretório. ### Login no Intersystems Containers Registry Por motivos de conveniência, usamos o Intersystems Containers Registry (ICR) para extrair imagens do docker. Se você não souber seu login\\password do docker, basta conectar-se a [SSO.UI.User.ApplicationTokens.cls](https://login.intersystems.com/login/SSO.UI.User.ApplicationTokens.cls) com suas credenciais WRC, e você pode recuperar seu Token ICR. ```bash docker login -u="YourWRCLogin" -p="YourICRToken" containers.intersystems.com ``` ### Crie o banco de dados `myappdata` e o mapeamento de global Nós não criamos realmente o banco de dados `myappdata` agora, mas preparamos uma configuração para criá-lo no momento da compilação do docker. Para isso, basta criar um arquivo simples usando o formato JSON; A biblioteca config-api será usada para carregá-la nas instâncias IRIS. Crie o arquivo [config-files/simple-config.json](https://github.com/lscalese/iris-mirroring-samples/blob/master/config-files/simple-config.json) ```json { "Defaults":{ "DBDATADIR" : "${MGRDIR}myappdata/", "DBDATANAME" : "MYAPPDATA" }, "SYS.Databases":{ "${DBDATADIR}" : {} }, "Databases":{ "${DBDATANAME}" : { "Directory" : "${DBDATADIR}" } }, "MapGlobals":{ "USER": [{ "Name" : "demo.*", "Database" : "${DBDATANAME}" }] }, "Security.Services" : { "%Service_Mirror" : { /* Habilita o serviço de espelhamento nesta instancia */ "Enabled" : true } } } ``` Este arquivo de configuração permite que você crie um novo banco de dados com configurações padrão e faça o mapeamento global `demo.*` no namespace USER. Para obter mais informações sobre os recursos do arquivo de configuração [config-api](https://openexchange.intersystems.com/package/config-api), consulte o [artigo](https://community.intersystems.com/post/environment -setup-config-api) ou a [página do github](https://community.intersystems.com/post/environment-setup-config-api) ### O arquivo Docker O arquivo docker é baseado no [modelo docker] existente (https://github.com/intersystems-community/objectscript-docker-template), mas precisamos fazer algumas alterações para criar um diretório de trabalho, instalar ferramentas para usar IP, instalar ZPM, etc… Nossa imagem IRIS é a mesma para cada membro do espelho. O espelhamento será configurado no contêiner começando com a configuração correta dependendo de sua função (primeiro membro, backup ou relatório de leitura/gravação). Veja os comentários sobre o Dockerfile abaixo: ```Dockerfile ARG IMAGE=containers.intersystems.com/intersystems/iris:2021.1.0.215.0 # Não precisa baixar a imagem do WRC. Ele será retirado do ICR no momento da construção. FROM $IMAGE USER root COPY session.sh / COPY iris.key /usr/irissys/mgr/iris.key # /opt/demo será nosso diretório de trabalho usado para armazenar nossos arquivos de configuração e outros arquivos de instalação. # Instale o iputils-arping para ter um comando arping. É necessário configurar o IP virtual. # Baixe a versão mais recente do ZPM (o ZPM está incluído apenas na edição da comunidade). RUN mkdir /opt/demo && \ chown ${ISC_PACKAGE_MGRUSER}:${ISC_PACKAGE_IRISGROUP} /opt/demo && \ chmod 666 /usr/irissys/mgr/iris.key && \ apt-get update && apt-get install iputils-arping gettext-base && \ wget -O /opt/demo/zpm.xml https://pm.community.intersystems.com/packages/zpm/latest/installer USER ${ISC_PACKAGE_MGRUSER} WORKDIR /opt/demo # Defina a função de espelho padrão como mestre. # Ele será substituído no arquivo docker-compose em tempo de execução (mestre para a primeira instância, backup e relatório) ARG IRIS_MIRROR_ROLE=master # Copie o conteúdo do diretório config-files em /opt/demo. # Atualmente criamos apenas uma configuração simples para configurar nosso banco de dados e mapeamento de global. # Mais adiante neste artigo adicionaremos outros arquivos de configuração para configurar o espelho. ADD config-files . SHELL [ "/session.sh" ] # Instale o ZPM # Use ZPM para instalar config-api # Carregue o arquivo simple-config.json com config-api para: # - criar o banco de dados "myappdata" , # - adicionar o mapeamento de global no namespace "USER" para a global "demo.*" no banco de dados "myappdata" . # Basicamente, o ponto de entrada para instalar seu aplicativo ObjectScript é aqui. # Para este exemplo, carregaremos o simple-config.json para criar um banco de dados simples e um mapeamento de global. RUN \ Do $SYSTEM.OBJ.Load("/opt/demo/zpm.xml", "ck") \ zpm "install config-api" \ Set sc = ##class(Api.Config.Services.Loader).Load("/opt/demo/simple-config.json") # Copie o script de instalação do espelho COPY init_mirror.sh / ``` ### Construa a imagem IRIS O Dockerfile está pronto; podemos construir a imagem: ``` docker build --no-cache --tag mirror-demo:latest . ``` Essa imagem será usada para executar nós primários, de backup e de relatório. ### O arquivo .env Os arquivos de configuração JSON e a composição do docker usam variáveis de ambiente. Seus valores são armazenados em um arquivo chamado `.env`, para este exemplo nosso arquivo env é: ``` APP_NET_SUBNET=172.16.238.0/24 MIRROR_NET_SUBNET=172.16.220.0/24 IRIS_HOST=172.16.238.100 IRIS_PORT=1972 IRIS_VIRTUAL_IP=172.16.238.100 ARBITER_IP=172.16.238.10 MASTER_APP_NET_IP=172.16.238.20 MASTER_MIRROR_NET_IP=172.16.220.20 BACKUP_APP_NET_IP=172.16.238.30 BACKUP_MIRROR_NET_IP=172.16.220.30 REPORT_APP_NET_IP=172.16.238.40 REPORT_MIRROR_NET_IP=172.16.220.40 ``` ### Prepare o primeiro arquivo de configuração do membro espelho A biblioteca config-api permite configurar um espelho, então temos que criar um arquivo de configuração dedicado ao primeiro membro do espelho `config-files/mirror-master.json` Por conveniência, os comentários estão localizados diretamente no JSON. Você pode baixar o [mirror-master.json sem comentários aqui](https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/config-files/mirror-master.json). ```json { "Security.Services" : { "%Service_Mirror" : { "Enabled" : true } }, "SYS.MirrorMaster" : { "Demo" : { "Config" : { "Name" : "Demo", /* O nome do nosso espelho */ "SystemName" : "master", /* O nome da instancia no espelhamento */ "UseSSL" : true, "ArbiterNode" : "${ARBITER_IP}|2188", /* Endereço IP e Porta do nó do Arbitro */ "VirtualAddress" : "${IRIS_VIRTUAL_IP}/24", /* Endereço IP virtual */ "VirtualAddressInterface" : "eth0", /* Interface de rede usada para o endereço de IP virtual */ "MirrorAddress": "${MASTER_MIRROR_NET_IP}", /* Endereço IP deste nó na rede privada do espelho */ "AgentAddress": "${MASTER_APP_NET_IP}" /* Endereço IP deste nó (o Agente é istalado na mesma máquina) */ }, "Databases" : [{ /* Lista de banco de dados para adicionar ao espelhamento */ "Directory" : "/usr/irissys/mgr/myappdata/", "MirrorDBName" : "MYAPPDATA" }], "SSLInfo" : { /* SSL Configuration */ "CAFile" : "/certificates/CA_Server.cer", "CertificateFile" : "/certificates/master_server.cer", "PrivateKeyFile" : "/certificates/master_server.key", "PrivateKeyPassword" : "", "PrivateKeyType" : "2" } } } } ``` ### Prepare o arquivo de configuração do membro de failover Crie um arquivo de configuração do membro de backup de failover `config-files/mirror-backup.json`. Parece o primeiro membro: ```json { "Security.Services" : { "%Service_Mirror" : { "Enabled" : true } }, "SYS.MirrorFailOver" : { "Demo" : { /* Espelho para juntar */ "Config": { "Name" : "Demo", "SystemName" : "backup", /* Este nome de instância no espelho */ "InstanceName" : "IRIS", /* Nome da instancia IRIS do primeiro membro do espelho */ "AgentAddress" : "${MASTER_APP_NET_IP}", /* Endereço IP do agente do primeiro membro espelho */ "AgentPort" : "2188", "AsyncMember" : false, "AsyncMemberType" : "" }, "Databases" : [{ /* DB no espelho */ "Directory" : "/usr/irissys/mgr/myappdata/" }], "LocalInfo" : { "VirtualAddressInterface" : "eth0", /* Interface de rede usada para o endereço IP virtual. */ "MirrorAddress": "${BACKUP_MIRROR_NET_IP}" /* Endereço IP deste nó na rede espelho privada*/ }, "SSLInfo" : { "CAFile" : "/certificates/CA_Server.cer", "CertificateFile" : "/certificates/backup_server.cer", "PrivateKeyFile" : "/certificates/backup_server.key", "PrivateKeyPassword" : "", "PrivateKeyType" : "2" } } } } ``` ### Prepare o arquivo de configuração do membro assíncrono de leitura e gravação É bastante semelhante ao arquivo de configuração de failover. As diferenças são os valores de `AsyncMember`, `AsyncMemberType` e `MirrorAddress`. Crie o arquivo `./config-files/mirror-report.json`: ```json { "Security.Services" : { "%Service_Mirror" : { "Enabled" : true } }, "SYS.MirrorFailOver" : { "Demo" : { "Config": { "Name" : "Demo", "SystemName" : "report", "InstanceName" : "IRIS", "AgentAddress" : "${MASTER_APP_NET_IP}", "AgentPort" : "2188", "AsyncMember" : true, "AsyncMemberType" : "rw" }, "Databases" : [{ "Directory" : "/usr/irissys/mgr/myappdata/" }], "LocalInfo" : { "VirtualAddressInterface" : "eth0", "MirrorAddress": "${REPORT_MIRROR_NET_IP}" }, "SSLInfo" : { "CAFile" : "/certificates/CA_Server.cer", "CertificateFile" : "/certificates/report_server.cer", "PrivateKeyFile" : "/certificates/report_server.key", "PrivateKeyPassword" : "", "PrivateKeyType" : "2" } } } } ``` ### Gere os certificados e configure os nós IRIS Todos os arquivos de configuração estão prontos! Agora temos que adicionar script para gerar certificados para comunicação segura entre cada nós. Um script pronto para uso está disponível no repositório [gen-certificates.sh](https://raw.githubusercontent.com/lscalese/iris-mirroring-samples/master/gen-certificates.sh) ``` # sudo é necessário devido ao uso de chown, chgrp chmod. sudo ./gen-certificates.sh ``` Para configurar cada nó o `init_mirror.sh` será executado no início dos containers. Ele será configurado posteriormente em `docker-compose.yml` na seção de comando `command: ["-a","/init_mirror.sh"]` : ```bash #!/bin/bash # Banco de dados usado para testar o espelho. DATABASE=/usr/irissys/mgr/myappdata # O diretório contém myappdata com backup feito pelo mestre para restaurar em outros nós e fazer o espelho. BACKUP_FOLDER=/opt/backup # Espelhar arquivo de configuração no formato json config-api para o nó mestre. MASTER_CONFIG=/opt/demo/mirror-master.json # Espelhar arquivo de configuração no formato json config-api para o nó de backup. BACKUP_CONFIG=/opt/demo/mirror-backup.json # Espelhar arquivo de configuração no formato json config-api para o nó assíncrono do relatório. REPORT_CONFIG=/opt/demo/mirror-report.json # O nome do espelho MIRROR_NAME=DEMO # Lista de membros do espelho . MIRROR_MEMBERS=BACKUP,REPORT # Realizado no mestre. # Carregue a configuração do espelho usando config-api com o arquivo /opt/demo/simple-config.json. # Inicie um Job para aceitar automaticamente outros membros chamados "backup" e "report" para ingressar no espelho (evite validação de manual no gerenciamento do portal). mestre() { rm -rf $BACKUP_FOLDER/IRIS.DAT envsubst < ${MASTER_CONFIG} > ${MASTER_CONFIG}.resolved iris session $ISC_PACKAGE_INSTANCENAME -U %SYS
Artigo
Alessandro Marin · Nov. 26, 2020

DeepSee - Banco de Dados, Namespaces, e Mapeamento - Parte 1 de 5

Estou planejando implementar a Inteligência de Negócio (BI) com base nos dados de minhas instâncias. Qual é a melhor maneira de configurar meus bancos de dados e ambiente para usar o DeepSee?   ![](/sites/default/files/inline/images/graphic-3a-final.png) Este tutorial aborda essa questão mostrando três exemplos de arquitetura para DeepSee. Começaremos com um modelo de arquitetura básico e destacaremos suas limitações. O modelo subsequente é recomendado para aplicações de Inteligência de Negócio (BI) de complexidade intermediária e deve ser suficiente para a maioria dos casos de uso. Terminaremos este tutorial descrevendo como aumentar a flexibilidade da arquitetura para gerenciar implementações avançadas. Cada exemplo neste tutorial apresenta novos bancos de dados e mapeamentos globais, junto com uma discussão sobre por que e quando eles devem ser configurados. Ao construir a arquitetura, os benefícios fornecidos pelos exemplos mais flexíveis serão destacados. Antes de começar Servidores primários e analíticos Para tornar os dados altamente disponíveis, a InterSystems geralmente recomenda usar as soluções de espelhamento ou sombreamento e então basear a implementação DeepSee no servidor espelho/sombra. A máquina que hospeda a cópia original dos dados é chamada de servidor Primário, enquanto as máquinas que hospedam cópias dos dados e as aplicações de Inteligência de Negócio (BI) costumam ser chamados de servidores Analíticos (ou, às vezes, de Relatórios) Ter servidores primários e analíticos é muito importante, o principal motivo é evitar problemas de desempenho em qualquer um dos servidores. Verifique a documentação sobre a Arquitetura Recomendada. Dados e código da aplicação Armazenar dados de origem e código no mesmo banco de dados geralmente funciona bem apenas para aplicações de pequena escala. Para aplicações mais extensas, é recomendado armazenar os dados de origem e código em dois bancos de dados dedicados, o que permite compartilhar o código com todos os namespaces onde o DeepSee é executado, mantendo os dados separados. O banco de dados de dados de origem deve ser espelhado no servidor de produção. Este banco de dados pode ser somente leitura ou leitura-gravação. É recomendável manter o registro do diário habilitado para este banco de dados. As classes de origem e as aplicações personalizados devem ser armazenados em um banco de dados dedicado nos servidores de produção e analítico. Observe que esses dois bancos de dados para o código-fonte não precisam estar sincronizados ou mesmo rodar a mesma versão do Caché. Normalmente, o registro no diário não é necessário, desde que o backup do código seja feito regularmente em outro lugar. Neste tutorial teremos a seguinte configuração. O namespace do APP no servidor analítico tem o APP-DATA e o APP-CODE como bancos de dados padrão. O banco de dados APP-DATA tem acesso aos dados (a classe da tabela de origem e seus fatos) no banco de dados de dados de origem no Primário. O banco de dados APP-CODE armazena o código Caché (arquivos .cls e .INT) e outros códigos personalizados. Essa separação de dados e código é uma arquitetura típica e permite ao usuário, por exemplo, implantar com eficiência o código DeepSee e a aplicação personalizada.   Executar DeepSee em diferentes namespaces As implementações de Inteligência de Negócio (BI) usando DeepSee geralmente são executadas a partir de namespaces diferentes. Nesta postagem, mostraremos como configurar um único namespace de APP, mas o mesmo procedimento se aplica a todos os namespaces onde a aplicação de inteligência de negócio é executada. Documentação Recomenda-se familiarizar-se com a página de documentação Executando a Configuração Inicial. Esta página inclui a configuração de aplicações web, como colocar DeepSee globais em bancos de dados separados e uma lista de mapeamentos alternativos para DeepSee globais.   * * * Na segunda parte desta série mostraremos com a implementação de um modelo básico de arquitetura Excelente artigo! Ansioso pela série completa
Pergunta
Henrique Dias · Dez. 16, 2020

Fact Table com 1 registro

Fala pessoal, tudo bem? Criei uma tabela com 100k registros. No SELECT, retorna tudo belezinha. No entanto, quando tento criar um cubo utilizando essa tabela como base, o cubo é compilado com sucesso. No entanto, quando faço o BUILD, ele gera apenas 1 fato. Alguém já se deparou com alguma situação similar? Alguns detalhes: Class diashenrique.olist.data.order Extends %Persistent { Property customerID As diashenrique.olist.data.customer; Property orderStatus As %String; Property purchaseTimeStamp As %TimeStamp; Property approvedTimeStamp As %TimeStamp; Property deliveredCarrierDate As %TimeStamp; Property deliveredCustomerDate As %TimeStamp; Property estimatedDelivery As %TimeStamp; Index OrderStatusIndex On orderStatus; Index customerIndex On customerID; Index purchaseIndex On purchaseTimeStamp; Storage Default { <Data name="orderDefaultData"> <Value name="1"> <Value>%%CLASSNAME</Value> </Value> <Value name="2"> <Value>customerID</Value> </Value> <Value name="3"> <Value>orderStatus</Value> </Value> <Value name="4"> <Value>purchaseTimeStamp</Value> </Value> <Value name="5"> <Value>approvedTimeStamp</Value> </Value> <Value name="6"> <Value>deliveredCarrierDate</Value> </Value> <Value name="7"> <Value>deliveredCustomerDate</Value> </Value> <Value name="8"> <Value>estimatedDelivery</Value> </Value> </Data> <DataLocation>^orderD</DataLocation> <DefaultData>orderDefaultData</DefaultData> <IdLocation>^orderD</IdLocation> <IndexLocation>^orderI</IndexLocation> <StreamLocation>^orderS</StreamLocation> <Type>%Storage.Persistent</Type> } } IRISAPP>d ##class(%DeepSee.Utils).%PrintBuildErrors("OrderCube") 0 build error(s) for 'OrderCube' IRISAPP>Do ##class(%DeepSee.Utils).%BuildCube("OrderCube") Building cube [OrderCube] Existing cube deleted. Fact table built: 1 fact(s) (1 worker(s) used) Fact indices built: 1 fact(s) (1 worker(s) used) Complete Elapsed time: 0.209013s Source expression time: 0.000003s Abraços, Henrique Caso alguém se depare com esse mesmo cenário no futuro, a causa do problema estava no tipo de indice. @Eduard.Lebedyuk respondeu na Community em inglês e deixarei o link para quem quiser maiores detalhes. https://community.intersystems.com/post/fact-table-1-x-sql-select-100k#comment-140541 Segue a resposta em português do @Eduard.Lebedyuk : Os cubos dependem muito de índices de bitmap para serem executados rapidamente. Principalmente, cada fato na tabela de fatos deve ser acessível por meio do índice de bitmap. No passado, os índices de bitmap funcionavam apenas com inteiros positivos, mas agora parece haver uma abordagem %BID - essencialmente uma chave substituta. Eu acho que o InterSystems BI deveria gerar um erro ou gerar um %BID ou oferecer para gerar um %BID se a propriedade id da classe do fato não for um inteiro positivo.
Artigo
Heloisa Paiva · Mar. 10, 2023

Criando uma conexão ODBC - passo a passo

Introdução Esse artigo tem a intenção de ser um simples tutorial sobre como criar conexões ODBC e trabalhar com elas, já que eu achei o assunto um pouco confuso quando estava começando, mas tive pessoas incríveis que pegaram minha mão e me guiaram para conseguir, e eu acredito que todos merecem esse tipo de ajuda também. Vou dividir cada pequena parte em seções, então sinta-se à vontade para pular para a que sentir necessidade, apesar de eu recomendar ler o texto na íntegra. Vou usar os dados de exemplo criados num artigo anterior, Tutorial - forma mais rápida de criar uma base de dados de exemplo: Samples.PersistentData, com as propriedades Name e Age. Criando a conexão Abra a Fonte de Dados ODBC - procure por ODBC na barra de pesquisa do seu computador e vai encontrar facilmente. Selecione a aba DNS de sistema Clique em Adicionar Selecione o driver apropriado - para esse exemplo vou utilizar InterSystems IRIS ODBC35 Escolha um nome para a conexão Digite o servidor, porta e namespace que quer conectar (ex.: IP 12.0.0.1, porta 1972 e namespace SAMPLE) Digite o Usuário e a Senha que vai usar para se conectar Clique em "Testar conexão" para ver se tudo está funcionando corretamente - se não for bem sucedida, cheque novamente usuário, senha, servidor, porta e namespace, e também cheque se o IRIS está ligado (para esse exemplo), ou se precisa de uma VPN para esse conexão. OBS.: Eu não sei dizer se esses passos são similares para Linux ou iOS, perdão! Usando sua conexão numa Business Operation numa produção Esse é apenas um exemplo de como pode colocar essa conexão em prática, mas é um muito utilizado. Com um Business Operation com adaptador "EnsLib.SQL.OutboundAdapter" em uma produção, abra a aba de configurações e expanda a parte de Parâmetros Básicos. Você verá um input de DSN como esse: Expanda o input e ache a conexão que acabamos de criar. Se não estiver ali, garanta que a conexão foi criada na Fonte de Dados ODBC correta (32-bit ou 64-bit). Se não aparecer, simplesmente siga os passos novamente na outra opção e cheque o input de DSN outra vez. Credenciais O IRIS pode precisar de um usuário e senha para poder acessar essa conexão, então você deve fornecê-los. Logo abaixo do input de DSN, você vai encontrar um input de Credenciais com uma lupa ao lado. Clique na lupa e você vai se encontrar no menu de credenciais. Na aba à sua direita, clique em "Novo", digite um ID que facilitará que você identifique a credencial, o usuário e a senha necessários e salve. Muito bem! Agora que você tem as credenciais, pode voltar para a produção e selecioná-las pelo ID que escolheu. PS.: um exemplo para você testar Para esse simples tutorial, eu criei a seguinte classe num namespace diferente do que tem a tabela "Sample.PersistentData": Class Sample.ODBC.Operation Extends Ens.BusinessOperation { Parameter ADAPTER = "EnsLib.SQL.OutboundAdapter"; Property Adapter As EnsLib.SQL.OutboundAdapter; Parameter INVOCATION = "Queue"; Method LegalAge(Request As Sample.request, Response As Sample.response) As %Status { // instanciate the response Do Request.NewResponse(.Response) // Execute the query and select the first result Do ..Adapter.ExecuteQuery(.result, "SELECT Name, Age from Sample.PersistentData where Age > 20") Do result.%Next() // just for visualizing, sets the first result in the response Set Response.result = result.%Get("Name")_" "_result.%Get("Age") Quit 1 } XData MessageMap { <MapItems> <MapItem MessageType="Sample.request"> <Method>LegalAge</Method> </MapItem> </MapItems> } } Conclusão Obrigada por ler e eu espero que tenha sido útil! Sinta-se à vontade para me contatar sobre qualquer dúvida.
Artigo
Anton Umnikov · jan 11, 2021

Lendo dados do COVID com AWS S3 como tabela SQL no IRIS

IRIS External Table é um projeto de código aberto da comunidade InterSystems, que permite usar arquivos armazenados no sistema de arquivos local e armazenamento de objetos em nuvem, como o AWS S3, como tabelas SQL. ![IRIS External Table](https://github.com/intersystems-community/IRIS-ExternalTable/raw/master/images/ExternalTableDiagram.png) Ele pode ser encontrado no GitHub , Open Exchange e está incluído no InterSystems Package Manager, ZPM. Para instalar o External Table a partir do GitHub, use: git clone https://github.com/antonum/IRIS-ExternalTable.git iris session iris USER>set sc = ##class(%SYSTEM.OBJ).LoadDir("/IRIS-ExternalTable/src", "ck",,1) Para instalar usando o ZPM Package Manager: USER>zpm "install external-table" ## Trabalhando com arquivos locais Vamos criar um arquivo simples parecido com este: a1,b1 a2,b2 Abra seu editor favorito e crie o arquivo ou apenas use uma linha de comando no linux/mac: echo $'a1,b1\na2,b2' > /tmp/test.txt No IRIS SQL, crie uma tabela para representar este arquivo: create table test (col1 char(10),col2 char(10)) Converta a tabela para usar o armazenamento externo: CALL EXT.ConvertToExternal( 'test', '{ "adapter":"EXT.LocalFile", "location":"/tmp/test.txt", "delimiter": "," }') E finalmente, consulte a tabela: select * from test Se tudo funcionar conforme o esperado, você verá uma saída como esta: col1 col2 a1 b1 a2 b2 Agora volte ao editor, altere o conteúdo do arquivo e execute novamente a consulta SQL. Uau!!! Você está lendo os novos valores de seu arquivo local no SQL. col1 col2 a1 b1 a2 b99 ## Lendo dados a partir do S3 Em você pode obter acesso a dados atualizados constantemente sobre o COVID, armazenados pela AWS no data lake público. Vamos tentar acessar uma das fontes de dados neste data lake: `s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states` Se você tiver a ferramenta de linha de comando AWS instalada, pode repetir as etapas abaixo. Caso contrário, vá direto para a parte SQL. Você não precisa de usar um AWS específico instalado em sua máquina para acompanhar a parte SQL. $ aws s3 ls s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/ 2020-12-04 17:19:10 510572 us-states.csv $ aws s3 cp s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv . download: s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv to ./us-states.csv $ head us-states.csv date,state,fips,cases,deaths 2020-01-21,Washington,53,1,0 2020-01-22,Washington,53,1,0 2020-01-23,Washington,53,1,0 2020-01-24,Illinois,17,1,0 2020-01-24,Washington,53,1,0 2020-01-25,California,06,1,0 2020-01-25,Illinois,17,1,0 2020-01-25,Washington,53,1,0 2020-01-26,Arizona,04,1,0 Portanto, temos um arquivo com uma estrutura bastante simples. Com cinco campos delimitados. Para expor esta pasta S3 como um External Table, primeiro, precisamos criar uma tabela "regular" com a estrutura desejada: -- create external table create table covid_by_state ( "date" DATE, "state" VARCHAR(20), fips INT, cases INT, deaths INT ) Observe que alguns nomes de campo como “Date” são palavras reservadas no IRIS SQL e precisam ser colocados entre aspas duplas. Em seguida, precisamos converter esta tabela “regular” para a tabela “externa”, com base no bucket AWS S3 e tipo CSV. -- convert table to external storage call EXT.ConvertToExternal( 'covid_by_state', '{ "adapter":"EXT.AWSS3", "location":"s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/", "type": "csv", "delimiter": ",", "skipHeaders": 1 }' ) Se você observar com atenção, os argumentos dos procedimentos EXT.ExternalTable são o nome da tabela e a string JSON, contendo vários parâmetros, como localização para procurar por arquivos, adaptador, delimitador, etc. Além da AWS S3, o External Table oferece suporte ao armazenamento BLOB do Azure, Cloud Buckets e o sistema de arquivos local. O GitHub Repo contém referências para a sintaxe e as opções suportadas em todos os formatos. E finalmente, consulte a tabela: -- query the table select top 10 * from covid_by_state order by "date" desc [SQL]USER>>select top 10 * from covid_by_state order by "date" desc 2. select top 10 * from covid_by_state order by "date" desc date state fips cases deaths 2020-12-06 Alabama 01 269877 3889 2020-12-06 Alaska 02 36847 136 2020-12-06 Arizona 04 364276 6950 2020-12-06 Arkansas 05 170924 2660 2020-12-06 California 06 1371940 19937 2020-12-06 Colorado 08 262460 3437 2020-12-06 Connecticut 09 127715 5146 2020-12-06 Delaware 10 39912 793 2020-12-06 District of Columbia 11 23136 697 2020-12-06 Florida 12 1058066 19176 Compreensivelmente, leva mais tempo para consultar dados da tabela remota, do que na tabela "IRIS nativa" ou com base global, porém, ela é completamente armazenada e atualizada em nuvem e está sendo puxada para o IRIS "nos bastidores". Vamos explorar mais alguns recursos do External Table. ## %PATH e tabelas, com base em vários arquivos Em nossa pasta de exemplo, o bucket contém apenas um arquivo. Mais frequentemente, ele teria vários arquivos com a mesma estrutura, onde nome do arquivo identifica o carimbo de data/hora ou deviceid de algum outro atributo que desejaremos usar em nossas consultas. O campo %PATH é adicionado automaticamente a cada External Table e contém o caminho completo para o arquivo de onde a linha foi recuperada. select top 5 %PATH,* from covid_by_state %PATH date state fips cases deaths s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-21 Washington 53 1 0 s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-22 Washington 53 1 0 s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-23 Washington 53 1 0 s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-24 Illinois 17 1 0 s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv 2020-01-24 Washington 53 1 0 Você pode usar este campo %PATH em suas consultas SQL como em qualquer outro campo. ## Dados ETL para "Tabelas Regulares" Se sua tarefa é carregar dados do S3 em uma tabela IRIS, você pode usar o External Table como uma ferramenta ETL. Apenas faça: INSERT INTO internal_table SELECT * FROM external_table No nosso caso, se quisermos copiar os dados sobre COVID do S3 para a tabela local: --create local table create table covid_by_state_local ( "date" DATE, "state" VARCHAR(100), fips INT, cases INT, deaths INT ) --ETL from External to Local table INSERT INTO covid_by_state_local SELECT TO_DATE("date",'YYYY-MM-DD'),state,fips,cases,deaths FROM covid_by_state ## JOIN entre IRIS - tabela nativa e externa. Consultas federadas External Table é uma tabela SQL. Ele pode ser unido a outras tabelas, usado em subseleções e UNIONs. Você pode até combinar a tabela IRIS “Regular” e duas ou mais tabelas externas de fontes diferentes na mesma consulta SQL. Tente criar uma tabela regular, como os nomes de estado correspondendo com códigos de estado, como por exemplo, Washington – WA. E junte-a com nossa tabela baseada em S3. create table state_codes (name varchar(100), code char(2)) insert into state_codes values ('Washington','WA') insert into state_codes values ('Illinois','IL') select top 10 "date", state, code, cases from covid_by_state join state_codes on state=name Altere 'join' para 'left join' para incluir linhas para as quais o código de estado não foi definido. Como você pode ver, o resultado é uma combinação de dados do S3 e sua tabela IRIS nativa. ## Acesso seguro aos dados A AWS Covid Data Lake é público. Qualquer pessoa pode ler dados dele sem qualquer autenticação ou autorização. Na vida real, você desejará acessar seus dados de forma segura, evitando que estranhos espiem seus arquivos. Os detalhes completos da AWS Identity and Access Management (IAM) estão fora do escopo deste artigo. Mas o mínimo que você precisa saber é que você precisa de pelo menos a chave de acesso e a chave secreta da conta da AWS para acessar dados privados em sua conta. AWS usa autenticação de chave/segredo de conta para assinar solicitações. https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys Se você estiver executando o IRIS External Table na instância EC2, a maneira recomendada de lidar com a autenticação é usar as funções da instância EC2 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html. O IRIS External Table será capaz de usar as permissões dessa função. Nenhuma configuração extra é necessária. Em uma instância local/não EC2, você precisa especificar o AWS_ACCESS_KEY_ID e AWS_SECRET_ACCESS_KEY, especificando variáveis de ambiente ou instalando e configurando o cliente AWS CLI. export AWS_ACCESS_KEY_ID=AKIAEXAMPLEKEY export AWS_SECRET_ACCESS_KEY=111222333abcdefghigklmnopqrst Certifique-se de que a variável de ambiente esteja visível em seu processo IRIS. Você pode verificá-lo executando: USER>write $system.Util.GetEnviron("AWS_ACCESS_KEY_ID") Ela deve retornar o valor da chave. ou instale o AWS CLI, seguindo as instruções aqui https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html e execute: aws configure O External Table poderá, então, ler as credenciais dos arquivos de configuração do aws cli. Seu shell interativo e o processo IRIS podem estar sendo executados em contas diferentes. Certifique-se de executar `aws configure` na mesma conta do seu processo IRIS.
Artigo
Henrique Dias · Mar. 6, 2021

O que é IRIS RAD Studio?

@José.Pereira e eu criamos um bom projeto e gostaríamos de falar um pouco deste projeto para vocês. O que é IRIS RAD Studio? IRIS RAD Studio é a nossa ideia de low-code para mostrar o que é possível, oferecendo mais facilidade e flexibilidade aos desenvolvedores. Por que? Porque não!? Aplicações low-code tem ganho grande destaque no mercado nos últimos anos e a imagem abaixo mostra o "Magic Quadrant" atual fornecido pela Gartner para plataformas empresariais de aplicação low-code. O que mostra o quão interessante é esse mercado. RESTForms2 RESTForms2 é uma das peças-chave para nosso projeto. Como descrito por @Eduard.Lebedyuk neste artigo, RESTForms é um backend REST API para aplicações web modernas https://community.intersystems.com/post/restforms-rest-api-your-classes Funcionalidades Com a utilização de RESTForms2, classes persistentes herdadas de dc.irisrad.FormAdaptor, ganham automaticamente um formulário CRUD (Create, Read, Update, Delete). Os formulários disponíveis são exibidos na página inicial. Cada formulário, além das funcionalidades básicas de CRUD, também possui: Pesquisa geral Pesquisa avançada de um campo em específico Criação de filtros combinados Agrupamento Exportação do Datagrid para Excel Além da geração automática dos formulários baseado no JSON fornecido pelo RESTForms2, uma coisa que queremos oferecer aos usuários é o nosso Import Wizard. Import Wizard A funcionalidade apresentada anteriormente com o projeto iris-analytics-package, permite a qualquer usuário: Importar um arquivo CSV Criar uma classe persistente de acordo com o arquivo importado Criar um cubo para ser utilizado pelo InterSystems Analytics Gerar um dashboard de exemplo com base nos dados E se além disso, você também pudesse editar esse arquivo? Se você tivesse a possibilidade de criar novas informações, editar as informações providas pelo CSV ou até mesmo deletar linhas desnecessárias? Com IRIS RAD Studio você pode! 😃 Criando novas classes Se você possui classes existentes que herdam dc.irisrad.FormAdaptor, já pode tirar proveito dos recursos oferecidos pelo RAD Studio. Para novas classes, criamos um endpoint onde você fornece a classe desejada no formato JSON e voilà Exemplo: { "name": "My.ClassName", "displayFormName": "My tasks", "displayProperty": "text", "fields": [{ "name":"text", "displayName":"Task name", "type":"%Library.String", "required": false },{ "name":"taskDate", "displayName":"Task date", "type":"%Library.TimeStamp" },{ "name":"important", "displayName":"Important", "type":"%Library.Boolean" },{ "name":"completed", "displayName":"Completed", "type":"%Library.Boolean" }]} Facinho, né?! Hmmm... mas você deve estar se perguntando "E se eu não tenho essa familiaridade toda com JSON, ou se não entendo nada da parte técnica?" Calma... IRIS RAD Studio também oferece uma interface gráfica para a criação de novas classes! Criando novas classes - Wizard A ideia do low-code é justamente evitar que código seja escrito pelo seu usuário. E isso inclui descrever uma classe e suas propriedade no formato JSON! A interface criada permite que você crie o nome da sua classe, forneça a descrição que o seu formulário terá. Clicando no botão Save, ela abrirá um datagrid onde você pode definir as propriedades da sua classe, ou se preferir chamar assim, as colunas da sua tabela. Assim que terminar de definir, basta clicar em Compile e está pronto para utilizar seu novo formulário! Roadmap Acreditamos que esse projeto tem muito potencial para ser explorado. Caso bem recebido pela comunidade, gostaria de explorar essas possibilidades e criar um roadmap com todos. Assim, cada vez mais teremos um produto que atenda a necessidade geral. Demo Disponibilizamos a aplicação no link abaixo:https://irisrad.contest.community.intersystems.com/csp/irisapp/login.html Se você quer saber o usuário e senha, primeiro, precisa votar no nosso projeto! Sacanagem hehehe nós informamos o usuário/senha na nossa página no OpenExchange https://openexchange.intersystems.com/package/iris-rad-studio Hora da votação Se você gostou da nossa ideia, e acredita que merecemos seu voto, corre lá e nos ajude 😃 Seu voto é muito importante para nós iris-rad-studio https://openexchange.intersystems.com/contest/12