Pesquisar

Resumo
· Abr. 7

Nuevas publicaciones en la Comunidad de InterSystems, 31 marzo - 6 abril

Anúncio
· Abr. 7

Winners of the InterSystems AI Programming Contest

Hi Community,

It's time to announce the winners of the AI Programming Contest: Vector Search, GenAI and AI Agents

Thanks to all our amazing participants who submitted 15 applications 🔥 

Now it's time to announce the winners!

Experts Nomination

🥇 1st place and $5,000 go to the bg-iris-agent app by @Georgii Tatevosian, @Elena Karpova, @Alena Krasinskiene  

🥈 2nd place and $2,500 go to the mcp-server-iris app by @Dmitry Maslennikov 

🥉 3rd place and $1,000 go to the langchain-iris-tool app by @Yuri Marx

🏅 4th place and $500 go to the Facilis app by @Henrique Dias, @Henry Pereira, @José Pereira

🏅 5th place and $300 go to the toot app by @Alex Woodhead 

🌟 $100 go to the iris-AgenticAI app by @Muhammad Waseem

🌟 $100 go to the iris-easybot app by @Eric Fortenberry

🌟 $100 go to the oncorag app by Patrick Salome

🌟 $100 go to the AiAssistant app by @XINING MA 

🌟 $100 go to the iris-data-analysis app by @lando miller 

Community Nomination

🥇 1st place and $1,000 go to the  AiAssistant app by @XINING MA 

🥈 2nd place and $600 go to the bg-iris-agent app by @Georgii Tatevosian, @Elena Karpova, @Alena Krasinskiene  

🥉 3rd place and $300 go to the iris-data-analysis app by @lando miller 

🏅 4th place and $200 go to the Facilis app by @Henrique Dias, @Henry Pereira, @José Pereira

🏅 5th place and $100 go to the langchain-iris-tool app by @Yuri Marx

Our sincerest congratulations to all the participants and winners!

Join the fun next time ;)

11 Comments
Discussão (11)4
Entre ou crie uma conta para continuar
Artigo
· Abr. 7 11min de leitura

Exemple de remplacement du processus de transformation SDA en FHIR pour inclure le paramètre « RequestMethod »

Lors de la création d'un bundle à partir de données héritées, je (et d'autres) souhaitais pouvoir contrôler si les ressources étaient générées avec une méthode de requête FHIR PUT plutôt qu'avec la méthode POST codée en dur. J'ai étendu les deux classes responsables de la transformation de SDA en FHIR dans une production d'interopérabilité afin de prendre en charge un paramètre permettant à l'utilisateur de contrôler la méthode de requête.

La première classe est la classe Processus métier. Elle inclut un nouveau paramètre exposé dans l'onglet « Paramètres » de l'interface d'interopérabilité, appelé FHIRRequestMethod. Elle doit également transmettre la propriété FHIRRequestMethod à la méthode de classe de transformation en tant que paramètre.

Class Demo.FHIR.DTL.Util.HC.SDA.FHIR.ProcessV2 Extends HS.FHIR.DTL.Util.HC.SDA3.FHIR.Process
{
Parameter SETTINGS = "FHIRRequestMethod:Basic";
/// Cette propriété peut remplacer la méthode de requête générée avec chaque ressource FHIR. <br>
/// Cette propriété s'applique uniquement aux nouvelles ressources qui ne possèdent pas d'identifiant issu des données sources.
Property FHIRRequestMethod As %String(MAXLEN = 10) [ InitialExpression = "POST" ];
/// Il s'agit d'une méthode d'instance car elle doit envoyer SendSync à un hôte professionnel et obtenir la réponse de l'hôte.
Method ProcessSDARequest(pSDAStream, pSessionApplication As %String, pSessionId As %String, pPatientResourceId As %String = "") As %Status
{
    New %HSIncludeTimeZoneOffsets
    Set %HSIncludeTimeZoneOffsets = 1
    Set tSC = $$$OK
    Try {
        // Vérifiez la classe de base de l'hôte métier cible. Déterminez s'il s'agit d'un hôte métier FHIRServer Interop ou non.
        If '$Data(%healthshare($$$CurrentClass, "isInteropHost"))#10 {
            $$$ThrowOnError(##class(HS.Director).OpenCurrentProduction(.tProdObj))
            Set tClassName = ""
            For i = 1:1:tProdObj.Items.Count() {
                If tProdObj.Items.GetAt(i).Name = ..TargetConfigName {
                    Set tClassName = tProdObj.Items.GetAt(i).ClassName
                    Quit
                }
            }
            Kill tProdObj
            
            Set tIsInteropHost = 0
            Set tRequiredHostBases("HS.FHIRServer.Interop.Operation") = ""
            Set tRequiredHostBases("HS.FHIRServer.Interop.HTTPOperation") = ""
            Set tHostBase = ""
            For {
                Set tHostBase = $Order(tRequiredHostBases(tHostBase))
                If tHostBase="" Quit
                If $ClassMethod(tClassName, "%IsA", tHostBase) {
                    Set tIsInteropHost = 1
                    Quit
                }
            }
            Set %healthshare($$$CurrentClass, "isInteropHost") = tIsInteropHost
            
        } Else {
            Set tIsInteropHost = %healthshare($$$CurrentClass, "isInteropHost")
        }
        
        // Obtenir l'hôte et le port du serveur Web de l'instance actuelle, à utiliser pour renseigner l'en-tête 
        // HOST du message de requête FHIR. L'en-tête HOST est nécessaire dans le message de requête FHIR lorsque 
        // le message est acheminé pour traitement en production locale, contrairement à sa transmission à un serveur externe.
        Do ..GetHostAndPort(.tHost, .tPort)
        Set tLocalHostAndPort = tHost_$Select(tPort'="":":",1:"")_tPort
        
        If ..FHIRFormat="JSON" {
            Set tMessageContentType = "application/fhir+json"
        } ElseIf ..FHIRFormat="XML" {
            Set tMessageContentType = "application/fhir+xml"
        }
        
        Set tFHIRMetadataSetKey = $ZStrip($Piece(..FHIRMetadataSet, "/", 1), "<>W")
        
        Set tSchema = ##class(HS.FHIRServer.Schema).LoadSchema(tFHIRMetadataSetKey)
        
        If '..FormatFHIROutput {
            Set tIndentChars = ""
            Set tLineTerminator = ""
            Set tFormatter = ""
        } Else {
            Set tIndentChars = $Char(9)
            Set tLineTerminator = $Char(13,10)
            Set tFormatter = ##class(%JSON.Formatter).%New()
            Set tFormatter.IndentChars = tIndentChars
            Set tFormatter.LineTerminator = tLineTerminator
        }
        
        #dim tTransformObj As HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR
        
        Set tTransformObj = $ClassMethod(..TransformClass, "TransformStream", pSDAStream, "HS.SDA3.Container", tFHIRMetadataSetKey, pPatientResourceId, "", ..FHIRRequestMethod)
        
        // tTransformObj.bundle est a %DynamicObject.
        Set tBundleObj = tTransformObj.bundle
        
        $$$HSTRACE("Bundle object", "tBundleObj", tBundleObj.%ToJSON())
        
        // « individual » n'est pas un type de transaction ni une interaction.
        // Ce mode entraîne l'envoi de chaque entrée du Bundle
        // à TargetConfigName individuellement, et non en tant que transaction.
        If ..TransmissionMode="individual" {
            For i = 0:1:tBundleObj.entry.%Size()-1 {
                If tIsInteropHost {
                    Set tSC = ..CreateAndSendInteropMessage(tBundleObj.entry.%Get(i), tSchema, tMessageContentType, tFormatter, tIndentChars, tLineTerminator, pSessionApplication, pSessionId)
                } Else {
                    Set tSC = ..CreateAndSendFHIRMessage(tBundleObj.entry.%Get(i), tSchema, tLocalHostAndPort, tMessageContentType, tFormatter, tIndentChars, tLineTerminator, pSessionApplication, pSessionId)
                }
            }
        } Else {
            If tIsInteropHost {
                Set tSC = ..CreateAndSendInteropMessage(tBundleObj, tSchema, tMessageContentType, tFormatter, tIndentChars, tLineTerminator, pSessionApplication, pSessionId)
            } Else {
                Set tSC = ..CreateAndSendFHIRMessage(tBundleObj, tSchema, tLocalHostAndPort, tMessageContentType, tFormatter, tIndentChars, tLineTerminator, pSessionApplication, pSessionId)
            }
        }
        
    } Catch eException {
        Set tSC = eException.AsStatus()
    }
    
    Quit tSC
}

Storage Default
{
<Data name="ProcessV2DefaultData">
<Subscript>"ProcessV2"</Subscript>
<Value name="1">
<Value>FHIRRequestMethod</Value>
</Value>
</Data>
<DefaultData>ProcessV2DefaultData</DefaultData>
<Type>%Storage.Persistent</Type>
}

}

La deuxième classe est la classe de transformation, pour laquelle nous devons également ajouter une nouvelle propriété pour stocker le paramètre FHIRRequestMethod. La valeur de FHIRRequestMethod provient de l'appel de la méthode de classe à ..TransformStream. Une fois que ce paramètre du processus métier est passé à ..TransformStream, je le stocke dans la propriété de classe afin que toutes les méthodes de cette classe de transformation aient accès à la valeur.

Class Demo.FHIR.DTL.Util.API.Transform.SDA3ToFHIRV2 Extends HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR
{
/// Propriété permettant de remplacer la méthode de requête pour les ressources non identifiées
Property FHIRRequestMethod As %String(MAXLEN = 10);
/// Transforme un flux SDA (conteneur ou classe SDA) vers la version FHIR spécifiée. Renvoie une instance 
/// de cette classe contenant une propriété « bundle ». Cette propriété contiendra un bundle FHIR avec 
/// toutes les ressources générées lors de la transformation et toutes les références résolues. Si 
/// <var>patientId</var> ou <var>encounterId</var> sont spécifiés, ces valeurs seront intégrées à toutes 
/// les références Patient et Encounter applicables.
/// @API.Method
/// @Argument	stream			%Stream representation of an SDA object or Container
/// @Argument	SDAClassname	Classname for the object contained in the stream (eg. HS.SDA3.Container)
/// @Argument	fhirVersion		Version of FHIR used by the resource, eg. "STU3", "R4"
/// @Argument	patientId		(optional) FHIR resource id to be assigned to the Patient resource
/// @Argument	encounterId		(optional) FHIR resource id to be assigned to the Encounter resource, if not transforming a Container
ClassMethod TransformStream(stream As %Stream.Object, SDAClassname As %String, fhirVersion As %String, patientId As %String = "", encounterId As %String = "", FHIRRequestMethod As %String) As HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR
{
    set source = $classmethod(SDAClassname, "%New")
    if SDAClassname = "HS.SDA3.Container" {
        $$$ThrowOnError(source.InitializeXMLParse(stream, "SDA3"))
    }
    else {
        $$$ThrowOnError(source.XMLImportSDAString(stream.Read(3700000)))
    }
    return ..TransformObject(source, fhirVersion, patientId, encounterId, FHIRRequestMethod)
}

/// Transforme un objet SDA (conteneur ou classe SDA) vers la version FHIR spécifiée. Renvoie une instance 
/// de cette classe contenant une propriété « bundle ». Cette propriété contiendra un bundle FHIR avec 
/// toutes les ressources générées lors de la transformation et toutes les références résolues. Si 
/// <var>patientId</var> ou <var>encounterId</var> sont spécifiés, ces valeurs seront intégrées à toutes 
/// les références Patient et Encounter applicables.
/// @API.Method
/// @Argument	source			SDA object or Container
/// @Argument	fhirVersion		Version of FHIR used by the resource, eg. "STU3", "R4"
/// @Argument	patientId		(optional) FHIR resource id to be assigned to the Patient resource
/// @Argument	encounterId		(optional) FHIR resource id to be assigned to the Encounter resource, if not transforming a Container
ClassMethod TransformObject(source, fhirVersion As %String, patientId As %String = "", encounterId As %String = "", FHIRRequestMethod As %String) As HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR
{
    set schema = ##class(HS.FHIRServer.Schema).LoadSchema(fhirVersion)
    set transformer = ..%New(schema)
    
    // Mise à jour à partir de la classe parent pour définir la méthode FHIRRequestMethod pour l'utilisation de n'importe quelle méthode de classe
    Set transformer.FHIRRequestMethod = FHIRRequestMethod

    //SDA obtient l'identifiant du patient et de la rencontre, tandis que le conteneur obtient uniquement l'identifiant du patient. 
    //Parce qu'un conteneur peut avoir plusieurs rencontres et nous ne pouvons pas déterminer à laquelle il fait référence.
    if source.%ClassName(1) = "HS.SDA3.Container" {
        do transformer.TransformContainer(source, patientId)
    }
    else {
        do transformer.TransformSDA(source, patientId, encounterId)
    }
    
    return transformer
}

/// Vérifie que la ressource est FHIR valide, l'ajoute au bundle de sortie et renvoie une référence à cette ressource. 
/// La ressource est également générée sous forme de %DynamicObject.
/// @Inputs
/// source			SDA object which created this resource
/// resource		Object model version of the resource
/// resourceJson	%DynamicObject version of the resource
/// One of <var>resource</var> or <var>resourceJson</var> must be provided. If both are provided,
/// the %DynamicObject representation will be given precedence 
Method AddResource(source As HS.SDA3.SuperClass, resource As %RegisteredObject = "", ByRef resourceJson As %DynamicObject = "") As HS.FHIR.DTL.vR4.Model.Base.Reference [ Internal ]
{
    if '$isobject(resourceJson) {
        set resourceJson = ##class(%DynamicObject).%FromJSON(resource.ToJSON())
    }
    
    try {
        do ..%resourceValidator.ValidateResource(resourceJson)
    } catch ex {
        do ..HandleInvalidResource(resourceJson, ex)
        return ""
    }
    
    set entry = ##class(%DynamicObject).%New()
    set entry.request = ##class(%DynamicObject).%New()
    
    set id = ..GetId(source, resourceJson) 
    if id '= "" {
        set resourceJson.id = id
    }
    
    //Vérifiez un mappage identifiant SDA->id pour maintenir les références 
    //Remarque : Provenance attribue un GUIDE à l'ID externe pour une utilisation interne, il ne s'agit pas d'un ID externe 
    // et il ne devrait pas influencer l'attribution de l'ID 
    set sourceIdentifier = ""
    if resourceJson.resourceType = "Encounter" {
        set sourceIdentifier = source.EncounterNumber
    }
    elseif ((source.%Extends("HS.SDA3.SuperClass")) && (resourceJson.resourceType '= "Provenance")) {
        set sourceIdentifier = source.ExternalId
    }
    
    if id = "" {
        if (resourceJson.resourceType = "Patient") && (..%patientId '= "") {
            set id = ..%patientId
        }
        elseif $get(..%resourceIds(resourceJson.resourceType)) '= "" {
            set id = ..%resourceIds(resourceJson.resourceType)
        }
        elseif (sourceIdentifier '= "") && $data(..%resourceIds(resourceJson.resourceType, sourceIdentifier)) {
            set id = ..%resourceIds(resourceJson.resourceType, sourceIdentifier)
        }
        
        if id '= "" {
            set resource.id = id
            set resourceJson.id = id
        }
    }
    
    if resourceJson.id '= "" {
        set id = resourceJson.id
        set entry.fullUrl = $select(..GetBaseURL()'="":..GetBaseURL() _ "/", 1:"") _ resourceJson.resourceType _ "/" _ resourceJson.id
        set entry.request.method = "PUT"
        set entry.request.url = resourceJson.resourceType _ "/" _ resourceJson.id
    }
    else {
        set id = $zconvert($system.Util.CreateGUID(), "L")
        set entry.fullUrl = "urn:uuid:" _ id
        // changed from parent class to accept parameter as input instead of hard coding "POST"
        set entry.request.method = ..FHIRRequestMethod
        set entry.request.url = resourceJson.resourceType
    }
    
    //Enregistrer les mappages d'identifiants pour un accès ultérieur
    if resourceJson.resourceType = "Patient" {
        set ..%patientId = id
    }
    elseif sourceIdentifier '= "" {
        set ..%resourceIds(resourceJson.resourceType, sourceIdentifier) = id
    }
    
    set duplicate = ..IsDuplicate(resourceJson, id)
    if duplicate '= "" {
        return duplicate
    }
    
    //Index pour la recherche O(1) si nécessaire pour le post-traitement
    set ..%resourceIndex(resourceJson.resourceType, id) = resourceJson
    
    set entry.resource = resourceJson
    do ..%bundle.entry.%Push(entry)
    
    return ..CreateReference(resourceJson.resourceType, id)
}

}

Ces classes sont conçues pour être utilisées dans une production d'interopérabilité. La démonstration qui met en évidence la version de base de ces classes peut être trouvée ici :

Learning Services: Converting Legacy Data to HL7 FHIR R4 in InterSystems IRIS for Health & Github Repo for Legacy To FHIR Transformation Demo
 

Discussão (0)1
Entre ou crie uma conta para continuar
Pergunta
· Abr. 7

How to convert persistent object to dynamic object

Hi devs!

Suppose I have an instance of a persistent class:

Set person = ##class(dc.Sample.Person).%OpenId(1)

Class is an ancestor of %JSON.Adapter.

How can I convert it to dynamic object with the properties that person.%JSONExport() provides?

{"Name":"Elon Mask","Title":"Associate Accountant","Company":"InterSystems","Phone":"799-933-5569","DOB":"1926-12-11"}

Couldn't find a right method. 

set dynObj = ##class(%ZEN.Auxiliary.altJSONProvider).%ObjectToAET(person) works, but adds id and classname, which I don't want to be added:

zw dynObj
dynObj={"_class":"dc.Sample.Person","_id":1,"Name":"Elon Mask","Title":"Associate Accountant","Company":"InterSystems","Phone":"799-933-5569","DOB":31390}  ;

Thoughts?

17 Comments
Discussão (17)5
Entre ou crie uma conta para continuar
Artigo
· Abr. 7 2min de leitura

¡Hey, chat! ¿Qué pasa con mi interoperabilidad?

¿Qué pasaría si pudierais hablar en un chat para comprobar qué sucede en Interoperabilidad, ver si hay errores e incluso resolver algunos tipos de problemas?

Con el servidor MCP, podéis conectar cualquiera de vuestros clientes MCP, por ejemplo, Claude, a IRIS y pedirle que revise la Interoperabilidad.

Lo que necesitáis es una configuración sencilla de Claude.

En la sección de Desarrollador de los ajustes de Claude, podéis encontrar un archivo de configuración.

Con una configuración sencilla, como esta, solo se requiere acceso a IRIS; no es necesario instalar nada en IRIS.

{
  "mcpServers": {
    "iris": {
      "command": "uvx",
      "args": [
        "mcp-server-iris"
      ],
      "env": {
        "IRIS_HOSTNAME": "localhost",
        "IRIS_PORT": "1972",
        "IRIS_NAMESPACE": "USER",
        "IRIS_USERNAME": "_SYSTEM",
        "IRIS_PASSWORD": "SYS"
      }
    }
  }
}

Después de reiniciar Claude, debería mostrar que algunas herramientas MCP están disponibles. Al hacer clic allí, se mostrarán todas las herramientas disponibles.

Claude, al conocer las herramientas disponibles y según el mensaje que proporcionéis, puede seleccionar la herramienta que mejor se adapte para ayudar con la consulta.

Supongamos que funciona bien, vamos a comprobar si hay errores.

Podemos ver la respuesta actual de la herramienta. Claude analiza la salida y extrae los errores que encuentra, sugiriendo por qué pueden ocurrir y ofreciendo formas de solucionarlos.

Actualmente, la implementación de este servidor MCP no ofrece capacidades para editar el código, por lo que no vamos a pedir que se corrija el código. Pero podemos intentar recuperar la producción.

Reiniciarlo suele ayudar

Aquí estamos, todo bien ahora.

Bueno, ya que nuestra producción está funcionando correctamente, echemos un vistazo a los datos que ya hemos recolectado.

Para ayudar con esto, Claude usará una herramienta que puede ejecutar consultas SQL.

Aunque puse el nombre de la clase en lugar del nombre de la tabla, y descubrió que no existía dicha tabla, no se rindió. En una sola sesión, logró averiguar el nombre real de la tabla y obtener algunos datos.

Cuando intentó obtener más información sobre los datos recolectados, incluso trató de adivinar los nombres de las columnas.

Encontró las columnas reales, pero tuvo dificultades con las palabras reservadas y no intentó usar "FOUND".

Y con un último intento...

Decidió mantenerlo simple, pero usando el conocimiento sobre los nombres de las columnas, lo acertó y proporcionó un resumen.

En esta etapa, el MCP parece estar en el principio de su viaje, pero aún así tenemos una herramienta bastante buena.

Si os gusta esta herramienta, por favor, votad en el actual Concurso de OpenExchange Contest 

Discussão (0)1
Entre ou crie uma conta para continuar