Nouvelle publication

查找

Question
· Mai 18, 2024

Confusion regarding order status

Hi. I am struggling to understand the meaning of different elements of an order in my hospital's EHR.

Page: EPR > All Orders

Question: What is the difference between Start Date and Date Executed and Order End Date? If an order was started on Monday and executeed on Tuesday.. does this mean the patient received the order on Monday or Tuesday? and what is End date?

Also, how do I interpret order status? What is the difference between discontinued, verified, and executed?

Also, some  orders have a green / red / yellow bar to their left, that continues as a line under that row. What does that mean?

Thanks in advance.

2 Comments
Discussion (2)1
Connectez-vous ou inscrivez-vous pour continuer
Article
· Mai 18, 2024 3m de lecture

Enhancing Preventive Health Engagement: The Backend Powering ChatIRIS with InterSystems IRIS

ChatIRIS Health Coach, a GPT-4 based agent that leverages the Health Belief Model as a psychological framework to craft empathetic replies. This article elaborates on the backend architecture and its components, focusing on how InterSystems IRIS supports the system's functionality.

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer
Article
· Mai 18, 2024 3m de lecture

IRIS AI Studio: Playground to explore RAG capabilities on top of IRIS DB vector embeddings

In the previous article, we saw in detail about Connectors, that let user upload their file and get it converted into embeddings and store it to IRIS DB. In this article, we'll explore different retrieval options that IRIS AI Studio offers - Semantic Search, Chat, Recommender and Similarity. 

New Updates  ⛴️ 

  • Added installation through Docker. Run `./build.sh` after cloning to get the application & IRIS instance running in your local
  • Connect via InterSystems Extension in vsCode - Thanks to @Evgeny Shvarov 
  • Added FAQ's in the home page that covers the basic info for new users

Semantic Search

Semantic Search is a data retrieval method that aims to understand the user's intent and the context of the query rather than just matching keywords. It considers the relationship between words and concepts, going beyond simple keyword matching.

Building upon the index creation and data loading into the IRIS DB covered in the previous article's Connectors code, we now start with the index and run the query engine:

Ref. to `query_llama.py`

query_engine = index.as_query_engine()
response = query_engine.query(query)

 

Chat Engine

The Chat Engine augments the loaded content and acts as a stateful version of the previous query_engine. It keeps track of the conversation history and can answer questions with past context in mind.

Ref. to `chat_llama.py`

chat_engine = index.as_chat_engine(chat_mode='condense_question')
response = chat_engine.chat(user_message)

 

Recommender 

The Recommender is similar to the recommendation systems used on e-commerce sites, where similar products are displayed below the product we look for. The Recommender and Similarity RAG falls in similar lines, but in the Recommender, you can choose the LLM ranking model to be used.

Ref. to `reco_llama.py`

if reco_type == 'cohere_rerank':
    cohere_rerank = CohereRerank(api_key=cohere_api_key, top_n=results_count)
    query_engine = index.as_query_engine(
        similarity_top_k=10,
        node_postprocessors=[cohere_rerank],
    )
    response = query_engine.query(query)

elif reco_type == 'openai_rerank':
    rankgpt_rerank = RankGPTRerank(
        llm=OpenAI(
            model="gpt-3.5-turbo-16k",
            temperature=0.0,
            api_key=openai_api_key,
        ),
        top_n=results_count,
        verbose=True,
    )
    query_engine = index.as_query_engine(
        similarity_top_k=10,
        node_postprocessors=[rankgpt_rerank],
    )
    response = query_engine.query(query)

Similarity

The Similarity feature returns a similarity score, which helps evaluate the quality of a question-answering system via semantic similarity. You can filter the results based on the minimum similarity score and the number of similar items to retrieve from the DB.

Ref. to `similarity_llama.py`

retriever = index.as_retriever(similarity_top_k=top_k)
nodes = retriever.retrieve(query)

 

By leveraging these different retrieval options at IRIS AI Studio, including Semantic Search, Chat Engine, Recommender, and Similarity, users can explore the potential of their data. These features enable advanced information retrieval, contextual question answering, personalized recommendations, and semantic similarity analysis, empowering users to derive valuable insights and make data-driven decisions (at least to build similar ones in their domain).

🚀 Vote for this application in Vector Search, GenAI and ML contest, if you find it promising!

Feel free to share any feedback or inputs you may have. Thank you.

Discussion (0)0
Connectez-vous ou inscrivez-vous pour continuer
Article
· Mai 17, 2024 4m de lecture

IRIS SIEM System Integration with Crowdstrike Logscale

IRIS makes SIEM systems integration simple with Structured Logging and Pipes!

Adding a SIEM integration to InterSystems IRIS for "Audit Database Events" was dead simple with the Community Edition of CrowdStrike's Falcon LogScale, and here's how I got it done.  

CrowdStrike Community LogScale Setup

Getting Started was ridiculously straight forward and I had the account approved in a couple of days with the following disclaimer:

Falcon LogScale Community is a free service providing you with up to 16 GB/day of data ingest, up to 5 users, and 7 day data retention, if you exceed the limitations, you’ll be asked to upgrade to a paid offering. You can use Falcon LogScale under the limitations as long as you want, provided, that we can modify or terminate the Community program at any time without notice or liability of any kind.

Pretty generous and a good fit for this implementation, with the caveat all good things can come to an end I guess, cut your self an ingestion token in the UI and save it to your favorite hiding place for secrets.

Python Interceptor - irislogd2crwd.py

Wont go over this amazing piece of software engineering in detail, but it is as simple as a python implementation that accepts STDIN, breaks up what it sees into events, and ships them off to the SIEM platform to be ingested.

#!/usr/bin/env python
import json
import time
import os
import sys
import requests
import socket
from datetime import datetime
from humiolib.HumioClient import HumioIngestClient


input_list = sys.stdin.read().splitlines() # From ^LOGDMN Pipe!
for irisevent in input_list:
    # Required for CRWD Data Source
    today = datetime.now()
    fqdn = socket.getfqdn()

    payload = [
        {
            "tags": {
                "host": fqdn,
                "source": "irislogd"
            },
                "events": [
                {
                    "timestamp": today.isoformat(sep='T',timespec='auto') + "Z",
                    "attributes": {"irislogd":json.loads(irisevent)} 
                }
            ]
        }
    ]

    client = HumioIngestClient(
        base_url= "https://cloud.community.humio.com",
        ingest_token= os.environ["CRWD_LOGSCALE_APIKEY"]
    )
    ingest_response = client.ingest_json_data(payload)

    

You will want to chmod +x this script and put it where irisowner can enjoy it.



InterSystems IRIS Structured Logging Setup
Structured Logging in IRIS is documented to the 9's, so this will be a Cliff Note to the end state of configuring ^LOGDMN.  The thing that caught my attention in the docs is probably the most unclear part of the implementation, but the most powerful and fun for sure.

After:
ENABLING the Log Daemon, CONFIGURING the Log Daemon and STARTING Logging your configuration should look like this:
 

%SYS>Do ^LOGDMN
1) Enable logging
2) Disable logging
3) Display configuration
4) Edit configuration
5) Set default configuration
6) Display logging status
7) Start logging
8) Stop logging
9) Restart logging

LOGDMN option? 3
LOGDMN configuration

Minimum level: -1 (DEBUG)
 Pipe command: /tmp/irislogd2crwd.py
       Format: JSON
     Interval: 5
/tmp/irislogd2crwd.py  # Location of our chmod +x Python Interceptor
JSON                   # Important

Now that we are logging somewhere else, lets just pump up the verbosity in the Audit Log and enable all the events since somebody else is paying for it.

Stealing from @Sylvain Guilbaud 's post:



CrowdStrike LogScale Event Processing

It wont take long to get the hang of, but the Search Console is the beginning of all good things with setting up customized observability based on your events.  The search pane with filter criteria displays in the left corner, the available attributes on the left sidebar and the matching events in the results pane in the main view.

LogScale uses The LogScale Query Language (LQL to back the widgets, alerts and actions.

I suck at visualizations, so I am sure you could do better than below with a box of crayons, but here is my 4 widgets of glory to put a clown suit on the SIEM events for this post:

If we look under the hood for the "Event Types" widget, the following LQL is only needed behind a time series graph lql:

timechart(irislogd.event)


So we did the thing!

We've integrated IRIS with the Enterprise SIEM implementation and the Security Team is "😀 "  

The bonus here are the things that are also accomplished with the exact same development pattern as above:

  • Notifications
  • Actions
  • Scheduled Searches
  • Scheduled Daily Reports
2 Comments
Discussion (2)3
Connectez-vous ou inscrivez-vous pour continuer
Article
· Mai 17, 2024 52m de lecture

『 ?ó?? ??????????? Monitorizar ?????????? 】

🙂 Buenos días comunidad.

Me gustaría compartir con ustedes, en este artículo, una manera muy práctica, de obtener información relevante de todos los Namespaces de un Entorno. ¿Para qué?. Pues por ejemplo para uno de los casos de uso que más nos encontramos los desarrolladores con respecto a Healtshare: el desafío de necesitar crear 1 servicio, y la odisea de ir entorno a entorno, namespace a namespace, buscando si el puerto XYZAB está libre o no... 🙃

Para ello, vamos a construirnos un Servicio REST, que reciba peticiones desde el POSTMAN, y entrege toda la información de los namespaces del Entorno consultado.

Supongamos que en una primera aproximación, simplemente queremos obtener todos los datos de 1 Namespace concreto. 

En el Studio nos creamos el Servicio REST así:

Class Servicios.REST.Monitorizacion.ConsultarElementosProduccion Extends Ens.BusinessService
{

Parameter ADAPTER = "EnsLib.HTTP.InboundAdapter";
Parameter EnsServicePrefix = "/monitorizacion";
Method OnProcessInput(pInput As %Stream.Object, Output pOutput As %Stream.Object) As %Status
{
    Set tCmd=$ZConvert(pInput.Attributes("HttpRequest"),"U")
    Set tURL=$ZConvert(pInput.Attributes("URL"),"I","URL")
    
    Set tService="/"_$Piece(tURL,"/",2)    
    
    Quit:..#EnsServicePrefix'=tService $$$ERROR($$$EnsErrGeneral,"Service "_tService_"/ not supported.")
    
    Set tType=$Piece(tURL,"/",3)
    
    set namespace = $Piece(tURL,"/",4)
    
    if (tType = "ConsultarElementosProduccion") {
        do ..ConsultarElementosProduccion(pInput,.pOutput,namespace)
    } 
    
    Quit $$$OK
}

///     Devuelve un mensajes con los Namespaces, sus Producciones y Componentes
Method ConsultarElementosProduccion(pInput As %Stream.Object, Output pOutput As %Stream.Object, namespace As %String(MAXLEN="")) As %Status
{    
    Set pOutput  = ##class(%GlobalBinaryStream).%New()
    set claseAux = ##class(%ZEN.Auxiliary.jsonProvider).%New()
    
    if (##class(Util.FuncionesComunes).getClear(namespace) '= ""){
        set response = ##class(Servicios.REST.Monitorizacion.ConsultarElementosProduccion.Code).ConsultarElementosProduccion(namespace)
    }else{
        set response = ##class(Mensajes.Response.Monitorizacion.ConsultarElementosProduccionResponse).%New()
        set response.error = "Al final de la URL se debe indicar el NAMESPACE del cual necesitamos obtener datos, por ejemplo: 'DRAGOAP', 'ESBSSCC', 'ESBHCDSNS'..."
    }
    
    set tSC = claseAux.%WriteJSONStreamFromObject(.pOutput,.response,,,,"aeloqtuw")
    
    Do:$$$ISOK(tSC) pOutput.SetAttribute("Content-Type","application/json")
    do pOutput.SetAttribute("Access-Control-Allow-Origin","*")
    do pOutput.SetAttribute("Access-Control-Allow-Credentials","true")
    do pOutput.SetAttribute("Access-Control-Allow-Methods","GET")
    do pOutput.SetAttribute("Access-Control-Allow-Headers","request,Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers")
    Quit tSC
}

}

Añadimos el Servicio en la Producción mediante el Portal de Gestión:

 

Lo que hemos hecho hasta ahora es un Servicio que recibe mediante: 
    http://[Entorno]:[Puerto]/monitorizacion/ConsultarElementosProduccion/[NombreDelNamespace]
    
    
Ahora viene el núcleo de la cuestión, el meollo de la lógica. Mediante el siguiente código, recibimos el namespace y estructuramos su información:

Class Servicios.REST.Monitorizacion.ConsultarElementosProduccion.Code
{

ClassMethod ConsultarElementosProduccion(namespace As %String(MAXLEN="")) As Mensajes.Response.Monitorizacion.ConsultarElementosProduccionResponse
{
    ; Se registra un mensaje de información al entrar en el método ConsultarElementosProduccion
    Do ##class(Ens.Util.Log).LogInfo("","","Entra en Servicios.REST.Monitorizacion.ConsultarElementosProduccion.Code ConsultarElementosProduccion()")
    set response = ##class(Mensajes.Response.Monitorizacion.ConsultarElementosProduccionResponse).%New()
    
    ;set string = ""
    
    set rs = ##class(%ResultSet).%New()
    try {
        ; Configuración y ejecución de la consulta para obtener la lista de namespaces
        set rs.ClassName = "%SYS.Namespace"
        set rs.QueryName = "List"
        set tSC = rs.%Execute() 
        if 'tSC {
            ; Si hay un error al ejecutar la consulta, se registran mensajes de error y se sale del método
            Do ##class(Ens.Util.Log).LogInfo("","","catch inicial") 
            Do ##class(Ens.Util.Log).LogError("","",$system.OBJ.DisplayError(tSC)) 
            Do ##class(Ens.Util.Log).LogError("","",$System.Status.GetErrorText(tSC)) 
            kill rs
            quit
        }
        set currentNamespace = $SYSTEM.SYS.NameSpace()
        ; Se registra el namespace actual antes de cambiarlo
        Do ##class(Ens.Util.Log).LogInfo("","","currentNamespace: "_currentNamespace) 
        New $NAMESPACE        
        While rs.%Next(.tSC){
            ; Se recorren los namespaces obtenidos de la consulta
            Do ##class(Ens.Util.Log).LogInfo("","","Entra en WHILE para recorrer Namespaces") 
            set nsp = ""
            set nsp = rs.Data("Nsp")
            Do ##class(Ens.Util.Log).LogInfo("","","1 nsp: "_nsp) 
            
            ; Consultar 1 único namespace según la URL
            if ( nsp = namespace ){
                ; Se registra la entrada al bloque IF
                Do ##class(Ens.Util.Log).LogInfo("","","Entra en IF '= %SYS...")
                Do ##class(Ens.Util.Log).LogInfo("","","2 nsp: "_nsp)
                ; Se cambia el namespace
                Set $NAMESPACE=nsp
                Do ##class(Ens.Util.Log).LogInfo("","","3 nsp: "_nsp)
                Do ##class(Ens.Util.Log).LogInfo("","","Antes de Set $NAMESPACE=nsp")

                Do ##class(Ens.Util.Log).LogInfo("","","Después de Set $NAMESPACE=nsp")
                ; Se crea un objeto para representar el namespace en la base de datos
                set Namespace = ##class(EsquemasDatos.Monitorizacion.Namespace).%New()
                set Namespace.Nombre = $SYSTEM.SYS.NameSpace()
                Do ##class(Ens.Util.Log).LogInfo("","","1111 Namespace.Nombre: "_Namespace.Nombre_"("_nsp_")")
                
                ; Se preparan consultas SQL para obtener información sobre las producciones
                set resultset = ##class(%ResultSet).%New()
                set statement=##class(%SQL.Statement).%New()
                 set enumerate=statement.%PrepareClassQuery("Ens.Config.Production","EnumerateConfigItems")
                 Do ##class(Ens.Util.Log).LogAlert("","","enumerate: "_enumerate)
                 
                 set status=statement.%PrepareClassQuery("Ens.Config.Production","ProductionStatus")
                 Do ##class(Ens.Util.Log).LogAlert("","","status: "_status)
                 Do ##class(Ens.Util.Log).LogInfo("","","status: "_status)
                 
                 set resultset=statement.%Execute()
                 while resultset.%Next() {
                     ; Se recorren las producciones obtenidas de la consulta
                     Do ##class(Ens.Util.Log).LogInfo("","","Entra en el WHILE desde ESBSSCC, que recorre las Producciones")
                     set produccionName = resultset.%Get("Production")
                     set estado = resultset.%Get("StatusEnum")
                     set iniciadoDesde = resultset.%Get("LastStartTime")
                     set paradoDesde = resultset.%Get("LastStopTime")
                     
                     ; Se crea un objeto para representar la producción en la base de datos
                     set Produccion = ##class(EsquemasDatos.Monitorizacion.Produccion).%New()
                     set Produccion.Nombre = produccionName
                     set Produccion.Estado = "Suspendida"
                     set Produccion.Fecha = iniciadoDesde
                         
                     if (estado = 1) {
                         ; Si la producción está en ejecución, se actualiza la información
                         set Produccion.Estado = "En ejecución"
                         set Produccion.Fecha = paradoDesde
                         
                         ; Se obtiene información detallada sobre los componentes de la producción
                         set tProduction = ##class(Ens.Config.Production).%OpenId(produccionName,,.tSC)
                        if (tSC) {
                            Do ##class(Ens.Util.Log).LogInfo("","","tProduction.Items.Count(): "_tProduction.Items.Count())
                            For i=1:1:tProduction.Items.Count() {
                                #dim item as Ens.Config.Item
                                set item = tProduction.Items.GetAt(i)
                                set Componente = ##class(EsquemasDatos.Monitorizacion.Componente).%New()
                                set Componente.Nombre = item.Name
                                
                                set tipo = item.BusinessType()
                                set x = 0
                                if (tipo = "1") {
                                    set Componente.Tipo = "Servicio"
                                    set x = item.GetSetting("Port",.port)
                                    
                                    ; 06/02/2024 Para obtener el puerto de los Servicios DICOM
                                    if (x '= 1)
                                    {
                                        set x = item.GetSetting("IPPort",.port)    
                                    }
                                    
                                } elseif (tipo = "2") {
                                       set Componente.Tipo = "Proceso"
                                       
                                } elseif (tipo = "3") {
                                    set Componente.Tipo = "Operacion"
                                    // Si es SOAP la URL se configura en "WebServiceURL"
                                    set tSC = item.GetSetting("WebServiceURL",.endpoint)
                                    if (tSC = 1){
                                        set Componente.Endpoint = endpoint    
                                    }else{
                                        // Si es REST la URL se configura en "HTTPServer" y/o "URL"
                                        set tSC = item.GetSetting("HTTPServer",.endpoint)
                                        if (tSC = 1){
                                            set Componente.Endpoint = endpoint    
                                        }
                                        set tSC = item.GetSetting("URL",.endpoint)
                                        if (tSC = 1){    
                                            set Componente.Endpoint = Componente.Endpoint_endpoint
                                        }else{
                                            // Si es TCP/HL7 la URL se configura en "IPAddress" y "Port"
                                            set tSC = item.GetSetting("IPAddress",.endpoint)
                                            if (tSC = 1){
                                                set Componente.Endpoint = endpoint
                                                set tSC = item.GetSetting("Port",.endpoint)    
                                                if (tSC = 1){
                                                    set Componente.Endpoint = Componente.Endpoint_":"_endpoint    
                                                }
                                            }else{
                                                // Si ES BBDD se conecta al "DSN"    
                                                set tSC = item.GetSetting("DSN",.endpoint)
                                                if (tSC = 1){
                                                    set Componente.Endpoint = endpoint
                                                }
                                            }
                                                
                                        }        
                                    }
                                }
                                set Componente.Categoria = item.Category
                                ; 24/01/2024 Añadimos para incorporar el PoolSize en la respuesta de los Componentes
                                try{
                                    set Componente.PoolSize = item.PoolSize
                                }catch{
                                    Do ##class(Ens.Util.Log).LogWarning("","","Error en set Componente.PoolSize = item.PoolSize")    
                                }
                                
                                ; Añadimos 30/01/2024 para expresar si está encendido el elemento
                                set Componente.Habilitado = item.Enabled
                                if (x=1) && (tipo="1") {
                                    set Componente.Puerto = port
                                }
                                do Namespace.Componentes.Insert(Componente)    
                            }
                        } else {
                            Do ##class(Ens.Util.Log).LogWarning("","","tSC: "_tSC)
                        }

                     } elseif (estado = 2) {
                         ; Si la producción está detenida, se actualiza la información
                         set Produccion.Estado = "Detenida"
                         set Produccion.Fecha = paradoDesde
                         
                     }
                     do Namespace.Producciones.Insert(Produccion)
                }
                 Do ##class(Ens.Util.Log).LogInfo("","","Antes de do response.Namespaces.Insert(Namespace)")
                do response.Namespaces.Insert(Namespace)
            }
        }
        do rs.Close()
        kill rs
        kill comp
        kill nsp
        kill tSC
        kill statement
        kill status
        kill resultset
        Set $NAMESPACE=currentNamespace
        kill currentNamespace
    } catch {
        ; En caso de excepción, se registran mensajes de error y se limpian las variables
        Do ##class(Ens.Util.Log).LogInfo("","","catch final")
        Do ##class(Ens.Util.Log).LogError("","",$system.OBJ.DisplayError(tSC))
        Do ##class(Ens.Util.Log).LogError("","",$System.Status.GetErrorText(tSC))
        do rs.Close()
        kill rs
        kill comp
        kill nsp
        kill tSC
        kill statement
        kill status
        kill resultset
        Set $NAMESPACE=currentNamespace
        kill currentNamespace
    }
    ; Se retorna la respuesta generada durante la ejecución del método
    quit response
}

}

 

De esta forma, si probamos, obtendremos una respuesta tal que así:

{
    "Namespaces": [
        {
            "Nombre": "ESBCentrales",
            "Producciones": [
                {
                    "Nombre": "Nombre1",
                    "Estado": "Detenida",
                    "Fecha": ""
                },
                {
                    "Nombre": "Producion.ESBCentrales",
                    "Estado": "En ejecución",
                    "Fecha": ""
                }
            ],
            "Componentes": [
                {
                    "Nombre": "EnvioADT",
                    "Tipo": "Operacion",
                    "Categoria": "Gestion de Pacientes",
                    "Puerto": "",
                    "PoolSize": 1,
                    "Habilitado": false,
                    "Endpoint": "[Entorno1]:[Puerto1]"
                },
       
                {
                    "Nombre": "EnrutadorREPOSICIONES",
                    "Tipo": "Proceso",
                    "Categoria": "Logística",
                    "Puerto": "",
                    "PoolSize": 1,
                    "Habilitado": true,
                    "Endpoint": ""
                },
                {
                    "Nombre": "Servicios.UnidadCuidadosIntensivosv01r00",
                    "Tipo": "Servicio",
                    "Categoria": "UCI",
                    "Puerto": 1989,
                    "PoolSize": 1,
                    "Habilitado": false,
                    "Endpoint": ""
                },
                {
                    "Nombre": "Operaciones.BBDD.ConsultarRecursosSanitarios",
                    "Tipo": "Operacion",
                    "Categoria": "Prenatal",
                    "Puerto": "",
                    "PoolSize": 1,
                    "Habilitado": false,
                    "Endpoint": "DataSourcePRENATALES"
                }           
            ]
        }
    ],
    "error": ""
}

 

 

Si vamos un paso más allá, y necesitamos obtener todos los elementos de todos los Namespaces y todas las Producciones de 1 Entorno, podríamos duplicar el Servicio:

Class Servicios.REST.Monitorizacion.ConsultarElementosProduccionTodosNamespaces Extends Ens.BusinessService
{

Parameter ADAPTER = "EnsLib.HTTP.InboundAdapter";
Parameter EnsServicePrefix = "/monitorizacion";
Method OnProcessInput(pInput As %Stream.Object, Output pOutput As %Stream.Object) As %Status
{
    Set tCmd=$ZConvert(pInput.Attributes("HttpRequest"),"U")
    Set tURL=$ZConvert(pInput.Attributes("URL"),"I","URL")
    
    Set tService="/"_$Piece(tURL,"/",2)    
    
    Quit:..#EnsServicePrefix'=tService $$$ERROR($$$EnsErrGeneral,"Service "_tService_"/ not supported.")
    
    Set tType=$Piece(tURL,"/",3)
    
    set tSC = '$$$OK
    if (tType = "ConsultarElementosProduccion") {
        set tSC = ..ConsultarElementosProduccion(pInput,.pOutput)
    }elseif (tType = "VerEstado") {
        set tSC =  ..VerEstado(.pOutput)
    }  
    
    Quit tSC
}

///     Devuelve un mensajes con los Namespaces, sus Producciones y Componentes
Method ConsultarElementosProduccion(pInput As %Stream.Object, Output pOutput As %Stream.Object) As %Status
{
    Set pOutput  = ##class(%GlobalBinaryStream).%New()
    set claseAux = ##class(%ZEN.Auxiliary.jsonProvider).%New()
    
    set response = ##class(Servicios.REST.Monitorizacion.ConsultarElementosProduccionTodosNamespaces.Code).ConsultarElementosProduccion()

    set tSC = claseAux.%WriteJSONStreamFromObject(.pOutput,.response,,,,"aeloqtuw")
    
    Do:$$$ISOK(tSC) pOutput.SetAttribute("Content-Type","application/json")
    do pOutput.SetAttribute("Access-Control-Allow-Origin","*")
    do pOutput.SetAttribute("Access-Control-Allow-Credentials","true")
    do pOutput.SetAttribute("Access-Control-Allow-Methods","GET")
    do pOutput.SetAttribute("Access-Control-Allow-Headers","request,Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers")
    Quit tSC
}

Method VerEstado(Output pOutput As %Stream.Object) As %Status
{
    Set pOutput = ##class(%GlobalBinaryStream).%New()
    set respuesta = {"estado": "OK"}
    set respuesta = respuesta.%ToJSON()
    do pOutput.Write(respuesta)
    
    do pOutput.SetAttribute("Content-Type","application/json")
    do pOutput.SetAttribute("Access-Control-Allow-Origin","*")
    do pOutput.SetAttribute("Access-Control-Allow-Credentials","true")
    do pOutput.SetAttribute("Access-Control-Allow-Methods","GET")
    do pOutput.SetAttribute("Access-Control-Allow-Headers","request,Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers")    
    quit $$$OK
}

}

Y adaptaríamos la clase auxiliar para que sepa qué hacer con todos los Namespaces:

Class Servicios.REST.Monitorizacion.ConsultarElementosProduccionTodosNamespaces.Code
{

ClassMethod ConsultarElementosProduccion() As Mensajes.Response.Monitorizacion.ConsultarElementosProduccionResponse
{
    ; Se registra un mensaje de información al entrar en el método ConsultarElementosProduccion
    Do ##class(Ens.Util.Log).LogInfo("","","Entra en Servicios.REST.Monitorizacion.ConsultarElementosProduccion.Code ConsultarElementosProduccion()")
    set response = ##class(Mensajes.Response.Monitorizacion.ConsultarElementosProduccionResponse).%New()
    
    ;set string = ""
    
    set rs = ##class(%ResultSet).%New()
    try {
        ; Configuración y ejecución de la consulta para obtener la lista de namespaces
        set rs.ClassName = "%SYS.Namespace"
        set rs.QueryName = "List"
        set tSC = rs.%Execute() 
        if 'tSC {
            ; Si hay un error al ejecutar la consulta, se registran mensajes de error y se sale del método
            Do ##class(Ens.Util.Log).LogInfo("","","catch inicial") 
            Do ##class(Ens.Util.Log).LogError("","",$system.OBJ.DisplayError(tSC)) 
            Do ##class(Ens.Util.Log).LogError("","",$System.Status.GetErrorText(tSC)) 
            kill rs
            quit
        }
        set currentNamespace = $SYSTEM.SYS.NameSpace()
        ; Se registra el namespace actual antes de cambiarlo
        Do ##class(Ens.Util.Log).LogInfo("","","currentNamespace: "_currentNamespace) 
        New $NAMESPACE        
        While rs.%Next(.tSC){
            ; Se recorren los namespaces obtenidos de la consulta
            Do ##class(Ens.Util.Log).LogInfo("","","Entra en WHILE para recorrer Namespaces") 
            set nsp = ""
            set nsp = rs.Data("Nsp")
            Do ##class(Ens.Util.Log).LogInfo("","","1 nsp: "_nsp) 
            
            ; Se verifica si el namespace está en la lista de namespaces permitidos
            If (( nsp = "ESBSSCC-DCM")||( nsp = "ESBLAPALMA")||( nsp = "ESBLANZAROTE")||( nsp = "ESBLAGOMERA")
            ||( nsp = "ESBHUGCDN")||( nsp = "ESBHUC")||( nsp = "ESBHCDSNS")||( nsp = "ESBHCDNEGRIN")
            ||( nsp = "ESBHCDLAPALMA")||( nsp = "ESBHCDLANZAROTE")||( nsp = "ESBHCDLAGOMERA")
            ||( nsp = "ESBHCDHUNSC")||( nsp = "ESBHCDFUERTEVENTURA")||( nsp = "ESBHCDELHIERRO")
            ||( nsp = "ESBHCDCHUIMI")||( nsp = "ESBFUERTEV")||( nsp = "ESBELHIERRO")||( nsp = "ESBCHUIMI")
            ||( nsp = "ENSEMBLE")||( nsp = "ESBSSCC")||( nsp = "DRAGOAP")||( nsp = "DRAGOAE")){
                
                ; Se registra la entrada al bloque IF
                Do ##class(Ens.Util.Log).LogInfo("","","Entra en IF '= %SYS...")
                Do ##class(Ens.Util.Log).LogInfo("","","2 nsp: "_nsp)
                ; Se cambia el namespace
                Set $NAMESPACE=nsp
                Do ##class(Ens.Util.Log).LogInfo("","","3 nsp: "_nsp)
                Do ##class(Ens.Util.Log).LogInfo("","","Antes de Set $NAMESPACE=nsp")
                Do ##class(Ens.Util.Log).LogInfo("","","Después de Set $NAMESPACE=nsp")
                
                ; Se crea un objeto para representar el namespace en la base de datos
                set Namespace = ##class(EsquemasDatos.Monitorizacion.Namespace).%New()
                set Namespace.Nombre = $SYSTEM.SYS.NameSpace()
                Do ##class(Ens.Util.Log).LogInfo("","","1111 Namespace.Nombre: "_Namespace.Nombre_"("_nsp_")")
                
                ; Se preparan consultas SQL para obtener información sobre las producciones
                set resultset = ##class(%ResultSet).%New()
                set statement=##class(%SQL.Statement).%New()
                 set enumerate=statement.%PrepareClassQuery("Ens.Config.Production","EnumerateConfigItems")
                 Do ##class(Ens.Util.Log).LogAlert("","","enumerate: "_enumerate)
                 
                 set status=statement.%PrepareClassQuery("Ens.Config.Production","ProductionStatus")
                 Do ##class(Ens.Util.Log).LogAlert("","","status: "_status)
                 Do ##class(Ens.Util.Log).LogInfo("","","status: "_status)
                 
                 set resultset=statement.%Execute()
                 while resultset.%Next() {
                     ; Se recorren las producciones obtenidas de la consulta
                     Do ##class(Ens.Util.Log).LogInfo("","","Entra en el WHILE desde ESBSSCC, que recorre las Producciones")
                     set produccionName = resultset.%Get("Production")
                     set estado = resultset.%Get("StatusEnum")
                     set iniciadoDesde = resultset.%Get("LastStartTime")
                     set paradoDesde = resultset.%Get("LastStopTime")
                     
                     ; Se crea un objeto para representar la producción en la base de datos
                     set Produccion = ##class(EsquemasDatos.Monitorizacion.Produccion).%New()
                     set Produccion.Nombre = produccionName
                     set Produccion.Estado = "Suspendida"
                     set Produccion.Fecha = iniciadoDesde
                         
                     if (estado = 1) {
                         ; Si la producción está en ejecución, se actualiza la información
                         set Produccion.Estado = "En ejecución"
                         set Produccion.Fecha = paradoDesde
                         
                         ; Se obtiene información detallada sobre los componentes de la producción
                         set tProduction = ##class(Ens.Config.Production).%OpenId(produccionName,,.tSC)
                        if (tSC) {
                            Do ##class(Ens.Util.Log).LogInfo("","","tProduction.Items.Count(): "_tProduction.Items.Count())
                            For i=1:1:tProduction.Items.Count() {
                                #dim item as Ens.Config.Item
                                set item = tProduction.Items.GetAt(i)
                                set Componente = ##class(EsquemasDatos.Monitorizacion.Componente).%New()
                                set Componente.Nombre = item.Name
                                
                                set tipo = item.BusinessType()
                                set x = 0
                                if (tipo = "1") {
                                    set Componente.Tipo = "Servicio"
                                    set x = item.GetSetting("Port",.port)
                                    
                                    ; 06/02/2024 Para obtener el puerto de los Servicios DICOM
                                    if (x '= 1)
                                    {
                                        set x = item.GetSetting("IPPort",.port)    
                                    }
                                    
                                } elseif (tipo = "2") {
                                       set Componente.Tipo = "Proceso"
                                       
                                } elseif (tipo = "3") {
                                    set Componente.Tipo = "Operacion"
                                    // Si es SOAP la URL se configura en "WebServiceURL"
                                    set tSC = item.GetSetting("WebServiceURL",.endpoint)
                                    if (tSC = 1){
                                        set Componente.Endpoint = endpoint    
                                    }else{
                                        // Si es REST la URL se configura en "HTTPServer" y/o "URL"
                                        set tSC = item.GetSetting("HTTPServer",.endpoint)
                                        if (tSC = 1){
                                            set Componente.Endpoint = endpoint    
                                        }
                                        set tSC = item.GetSetting("URL",.endpoint)
                                        if (tSC = 1){    
                                            set Componente.Endpoint = Componente.Endpoint_endpoint
                                        }else{
                                            // Si es TCP/HL7 la URL se configura en "IPAddress" y "Port"
                                            set tSC = item.GetSetting("IPAddress",.endpoint)
                                            if (tSC = 1){
                                                set Componente.Endpoint = endpoint
                                                set tSC = item.GetSetting("Port",.endpoint)    
                                                if (tSC = 1){
                                                    set Componente.Endpoint = Componente.Endpoint_":"_endpoint    
                                                }
                                            }else{
                                                // Si ES BBDD se conecta al "DSN"    
                                                set tSC = item.GetSetting("DSN",.endpoint)
                                                if (tSC = 1){
                                                    set Componente.Endpoint = endpoint
                                                }
                                            }
                                                
                                        }        
                                    }
                                }
                                set Componente.Categoria = item.Category
                                ; 24/01/2024 Añadimos para incorporar el PoolSize en la respuesta de los Componentes
                                try{
                                    set Componente.PoolSize = item.PoolSize
                                }catch{
                                    Do ##class(Ens.Util.Log).LogWarning("Error en set Componente.PoolSize = item.PoolSize")    
                                }
                                
                                ; Añadimos 30/01/2024 para expresar si está encendido el elemento
                                set Componente.Habilitado = item.Enabled
                                
                                if (x=1) && (tipo="1") {
                                    set Componente.Puerto = port
                                }
                                do Namespace.Componentes.Insert(Componente)    
                            }
                        } else {
                            Do ##class(Ens.Util.Log).LogWarning("","","tSC: "_tSC)
                        }

                     } elseif (estado = 2) {
                         ; Si la producción está detenida, se actualiza la información
                         set Produccion.Estado = "Detenida"
                         set Produccion.Fecha = paradoDesde
                         
                     }
                     do Namespace.Producciones.Insert(Produccion)
                }
                 Do ##class(Ens.Util.Log).LogInfo("","","Antes de do response.Namespaces.Insert(Namespace)")
                do response.Namespaces.Insert(Namespace)
            }
        }
        do rs.Close()
        kill rs
        kill comp
        kill nsp
        kill tSC
        kill statement
        kill status
        kill resultset
        Set $NAMESPACE=currentNamespace
        kill currentNamespace
    } catch {
        ; En caso de excepción, se registran mensajes de error y se limpian las variables
        Do ##class(Ens.Util.Log).LogInfo("","","catch final")
        Do ##class(Ens.Util.Log).LogError("","",$system.OBJ.DisplayError(tSC))
        Do ##class(Ens.Util.Log).LogError("","",$System.Status.GetErrorText(tSC))
        do rs.Close()
        kill rs
        kill comp
        kill nsp
        kill tSC
        kill statement
        kill status
        kill resultset
        Set $NAMESPACE=currentNamespace
        kill currentNamespace
    }
    ; Se retorna la respuesta generada durante la ejecución del método
    quit response
}

}

A continuación subimos el Servicio al Portal Web:

Y ahora viene la parte curiosísima... Puesto que estamos cambiando de Namespace, esto implica que necesitamos desplegar el código del Servicio de TODOS los Namespaces y su clase auxiliar, en cada uno de los NAMESPACES que queramos consultar, es decir, los que incluyamos en el if:

            
            ; Se verifica si el namespace está en la lista de namespaces permitidos
            If (( nsp = "ESBSSCC-DCM")||( nsp = "ESBLAPALMA")||( nsp = "ESBLANZAROTE")||( nsp = "ESBLAGOMERA")
            ||( nsp = "ESBHUGCDN")||( nsp = "ESBHUC")||( nsp = "ESBHCDSNS")||( nsp = "ESBHCDNEGRIN")
            ||( nsp = "ESBHCDLAPALMA")||( nsp = "ESBHCDLANZAROTE")||( nsp = "ESBHCDLAGOMERA")
            ||( nsp = "ESBHCDHUNSC")||( nsp = "ESBHCDFUERTEVENTURA")||( nsp = "ESBHCDELHIERRO")
            ||( nsp = "ESBHCDCHUIMI")||( nsp = "ESBFUERTEV")||( nsp = "ESBELHIERRO")||( nsp = "ESBCHUIMI")
            ||( nsp = "ENSEMBLE")||( nsp = "ESBSSCC")||( nsp = "DRAGOAP")||( nsp = "DRAGOAE")){

 

Una vez hecho lo anterior, ya estamos listos para probar mediante:
    http://[Entorno]:[Puerto]/monitorizacion/ConsultarElementosProduccion/
    

Observando como resultado:

{
    "Namespaces": [
        {
            "Nombre": "DRAGOExternosActivos",
            "Producciones": [
                {
                    "Nombre": "Candelaria",
                    "Estado": "Detenida",
                    "Fecha": ""
                },
                {
                    "Nombre": "Producción.HistoriaClínica",
                    "Estado": "En ejecución",
                    "Fecha": ""
                }
            ],
            "Componentes": [
                {
                    "Nombre": "ProcesarMedicamentos",
                    "Tipo": "Proceso",
                    "Categoria": "ESB",
                    "Puerto": "",
                    "PoolSize": 1,
                    "Habilitado": true,
                    "Endpoint": ""
                },
                {
                    "Nombre": "ESB_ConsultasInterhospitalarias",
                    "Tipo": "Operacion",
                    "Categoria": "ESB",
                    "Puerto": "",
                    "PoolSize": 1,
                    "Habilitado": false,
                    "Endpoint": ""
                },
                {
                    "Nombre": "JavaGateway",
                    "Tipo": "Servicio",
                    "Categoria": "",
                    "Puerto": "",
                    "PoolSize": 1,
                    "Habilitado": true,
                    "Endpoint": ""
                },
                {
                    "Nombre": "ServicioUnionPacientes",
                    "Tipo": "Servicio",
                    "Categoria": "",
                    "Puerto": "",
                    "PoolSize": 1,
                    "Habilitado": true,
                    "Endpoint": ""
                }
            ]
        },
        {
            "Nombre": "DRAGOPrimerosAuxilios",
            "Producciones": [
                {
                    "Nombre": "ITB.Production.TestNACK",
                    "Estado": "Detenida",
                    "Fecha": ""
                },
                {
                    "Nombre": "Produccion.DRAGOPrincipiosActivos",
                    "Estado": "En ejecución",
                    "Fecha": ""
                }
            ],
            "Componentes": [
                {
                    "Nombre": "HTTPOperacionUnificada",
                    "Tipo": "Operacion",
                    "Categoria": "LABORATORIO",
                    "Puerto": "",
                    "PoolSize": 1,
                    "Habilitado": true,
                    "Endpoint": ""
                },
                {
                    "Nombre": "HTTPProcesoLogística",
                    "Tipo": "Proceso",
                    "Categoria": "",
                    "Puerto": "",
                    "PoolSize": 0,
                    "Habilitado": true,
                    "Endpoint": ""
                },
                {
                    "Nombre": "Consultas Privadas to Ambulatorio",
                    "Tipo": "Servicio",
                    "Categoria": "",
                    "Puerto": 19010,
                    "PoolSize": 1,
                    "Habilitado": false,
                    "Endpoint": ""
                },
         
                {
                    "Nombre": "PeticionesLaboratorioCanarias",
                    "Tipo": "Operacion",
                    "Categoria": "Antigenos,LABORATORIO",
                    "Puerto": "",
                    "PoolSize": 1,
                    "Habilitado": true,
                    "Endpoint": "http://pre:ABCDE/csp/XDYZA/Servicios.Laboratorio.Recetas.CLS"
                },
            ]
            "Nombre": "ENSEMBLE",
            "Producciones": [
                {
                    "Nombre": "Produccion.ENSEMBLE",
                    "Estado": "Detenida",
                    "Fecha": "2024-05-15 13:44:07.868"
                },
                {
                    "Nombre": "TestAlert.ProdManaged",
                    "Estado": "Detenida",
                    "Fecha": ""
                },
                {
                    "Nombre": "TestAlert.ProdRouting",
                    "Estado": "Detenida",
                    "Fecha": ""
                }
            ],
            "Componentes": []
        }
    ],
    "error": ""
}

De esta forma ya hemos construido una manera de obtener todo lo relevante de una Producción, y podría desarrollarse una aplicación y/o web mediante la cual visualizar estos datos e incluso buscar si cierto puerto está ocupado o libre.

 

Ojalá que les halla resultado interesante y sobre todo un aporte :=)

 

Además me gustaría agradecer de manera sincera y honesta que han participado de alguna u otra forma o bien en el desarrollo directo, o bien en la idea y en la resolución de dudas prácticas, y me han ayudado al respecto:

?duardo Pérez Mederos ✅

?ntonio Gabriel Sánchez ☑️

?arlos Gómez Castro ✔️

?drián Vargas Rodríguez 🟦

 

Ojalá que les halla ayudado el artículo.

 

Por último me gustaría recordarles que es muy probable y posible que sea el código compartido muy mejorable y se podría hacer de maneras más completas, correctas y estructuradas. Por tanto a modo de reflexión para que sean comprensivos con este artículo me parece positivo compartir esta imagen:

Viñeta que aparece en la revista: "Linux Format" con fecha 30 Abril de 2023, página 14

- WE SHIPPED THE CODE WITH BUGS? HOW MANY DID YOU FIND? (¿ENVIAMOS EL CÓDIGO CON ERRORES? ¿CUÁNTOS ENCONTRASTE?)

- ONE ... (UNO...)

 

 

 

- IT SHIPPED WITH A BUG! THAT'S AWFUL! (¡SE ENVIÓ CON UN ERROR! ¡ESO ES TERRIBLE!)

- ... HUNDRED AND THIRTEEN (...CIENTO TRECE)

 

 

 

- YOU DIDN'T LET ME FINISH MY SENTENCE (NO ME DEJASTE TERMINAR MI FRASE)

 

 

🙂

Discussion (0)1
Connectez-vous ou inscrivez-vous pour continuer