0 Seguidores · 19 Publicaciones

Mapeo de clases, rutinas o globales para los Namespace.

Artículo Ricardo Paiva · jun 4, 2025 3m read

IRIS admite transformaciones CCDA y FHIR de forma nativa, pero acceder y visualizar estas funcionalidades requiere tiempo de configuración y conocimiento del producto. La aplicación IRIS Interop DevTools fue diseñada para cerrar esa brecha, permitiendo a los implementadores comenzar de inmediato y explorar las capacidades de transformación integradas del producto.

Además del entorno de transformación IRIS XML, XPath y CCDA, el paquete Interop DevTools ahora proporciona:

0
0 41
Artículo Alberto Fuentes · oct 21, 2024 3m read

¡Muy buenas a todos! Os paso un ejemplo de resolución de problemas a la hora de implementar transformaciones de datos que es muy interesante: estaba trabajando en un transformación de datos (DTL) de mensajes HL7 pero no paraba de obtener errores del tipo ERROR #5002... MAXSTRING. El problema era que la mayor parte de acciones en la interfaz gráfica de DTL utilizan el tipo de datos %String al trabajar con segmentos de un mensaje HL7.

Un %String tiene un límite de 3,641,144 caracteres, y mi OBX5.1 tenía 5,242,952 caracteres de longitud, como en el ejemplo proporcionado. Por supuesto, el administrador del sistema PACS dijo que se necesitaban archivos de ultra alta calidad, incluyendo resolución 4K, por lo que no pudimos lograr que el proveedor comprimiera o reformateara estos archivos a jpg comprimido o algo similar.

Inicialmente, este proveedor envía un ORU^R01 en la versión 2.3, y el EHR espera un MDM^T02 también en la versión 2.3. Además, necesitábamos realizar las siguientes transformaciones:

  1. La imagen incrustada se enviaba en OBX-5.1, y necesitábamos moverla a OBX-5.5.
  2. El formato de la imagen se enviaba en OBX-6, y lo necesitábamos en OBX-5.3 y OBX-5.4.
  3. Necesitábamos crear un segmento TXA.
  4. Soportar un conjunto de 25 segmentos OBX que podían estar completamente vacíos (>25 x 5Mb = ¡más de 125Mb de tamaño de mensaje!).

Ejemplo de estructura de mensaje recibido (cada "..." quedaría reemplazado con datos de más de 5 MB):

MSH|^~\&|VENDOR||||20241017125335||ORU^R01|1|P|2.3|
PID|||203921||LAST^FIRST^^^||19720706|M||||||||||100001|
PV1||X||||||||GI6|||||||||100001|
ORC|RE||21||SC||1|||||||||||
OBR|1||21|21^VENDOR IMAGES|||20241017123056|||||||||1001^GASTROENTEROLOGY^PHYSICIAN|||||Y||||F||1|
OBX|1|PR|100001|ch1_image_001.bmp|...^^^^^^^|BMP|||||F|
OBX|2|PR|100001|ch1_image_003.bmp|...|BMP|||||F|
OBX|3|PR|100001|ch1_video_01thumbnail.bmp|...|BMP|||||F|
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||
OBX||||||||||||

Las herramientas y métodos de prueba habituales no estaban a la altura para manejar mensajes tan grandes. Cuando usaba este ejemplo reemplazando los datos con ..., la interfaz gráfica de DTL de arrastrar y soltar funcionaba bien, pero al insertar los datos reales, todo se venía abajo.

Finalmente, descubrí que tenía que usar un bloque de código con ObjectScript y los tipos de datos Stream para poder trabajar correctamente con estos mensajes grandes.

Comparto mi clase final de DTL para cualquiera que venga después de mí y pueda encontrar esto útil:

Class OrdRes.VendorMDM Extends Ens.DataTransformDTL [ DependsOn = EnsLib.HL7.Message ]
{

Parameter IGNOREMISSINGSOURCE = 1;

Parameter REPORTERRORS = 1;

Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;

XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
{
<transform sourceClass='EnsLib.HL7.Message' targetClass='EnsLib.HL7.Message' sourceDocType='2.3:ORU_R01' targetDocType='2.5:MDM_T02' create='new' language='objectscript' >
<assign value='source.{MSH}' property='target.{MSH}' action='set' />
<assign value='"MDM"' property='target.{MSH:MessageType.MessageCode}' action='set' />
<assign value='"T02"' property='target.{MSH:MessageType.TriggerEvent}' action='set' />
<assign value='"2.5"' property='target.{MSH:VersionID.VersionID}' action='set' />
<assign value='source.{MSH:DateTimeofMessage}' property='target.{EVN:2}' action='set' />
<assign value='source.{PIDgrpgrp().PIDgrp.PID}' property='target.{PID}' action='set' />
<assign value='source.{PIDgrpgrp().PIDgrp.PV1grp.PV1}' property='target.{PV1}' action='set' />
<assign value='source.{PIDgrpgrp().ORCgrp().ORC}' property='target.{ORCgrp().ORC}' action='set' />
<assign value='source.{PIDgrpgrp().ORCgrp().OBR}' property='target.{ORCgrp().OBR}' action='set' />
<assign value='source.{PIDgrpgrp().ORCgrp().NTE()}' property='target.{ORCgrp().NTE()}' action='set' />
<assign value='"Endoscopy Image"' property='target.{TXA:DocumentType}' action='set' />
<assign value='"AU"' property='target.{TXA:DocumentCompletionStatus}' action='set' />
<assign value='"AV"' property='target.{TXA:DocumentAvailabilityStatus}' action='set' />
<assign value='source.{PID:18}' property='target.{TXA:12.3}' action='set' />
<code>
<![CDATA[
 set OBXCount=source.GetValueAt("PIDgrpgrp(1).ORCgrp(1).OBXgrp(*)")
 For k1 = 1:1:OBXCount
 {
   // if OBX-1 is empty then it is assumed the rest of the segment will be empty too, so disregard it.
   If source.GetValueAt("PIDgrpgrp(1).ORCgrp(1).OBXgrp("_k1_").OBX:SetIDOBX") '= ""
   {
     // create new stream to read source OBX
     set srcOBXStream=##class(%Stream.GlobalCharacter).%New()
     // get stream data from source OBX
     set tSC=source.GetFieldStreamRaw(srcOBXStream,"PIDgrpgrp(1).ORCgrp(1).OBXgrp("_k1_").OBX")
     // get the positions of needed delimitters:
     set p1=srcOBXStream.FindAt(1,"|")    // 0>p1="OBX"
     set p2=srcOBXStream.FindAt(p1+1,"|") // p1>p2=OBX-1
     set p3=srcOBXStream.FindAt(p2+1,"|") // p2>p3=OBX-2
     set p4=srcOBXStream.FindAt(p3+1,"|") // p3>p4=OBX-3
     set p5=srcOBXStream.FindAt(p4+1,"|") // p4>p5=OBX-4
     set p6=srcOBXStream.FindAt(p5+1,"^") // p5>p6=OBX-5.1
     set p7=srcOBXStream.FindAt(p6+1,"|") // p6>p7=OBX-5.2 -> OBX 5.*
     // if no OBX-5.2 then there will not be the `^` and p6 and p7 will be `-1`
     // when that is the case, find p7 starting at `p5+1` and make p6 = p7
     if (p6 < 0) {
       set p7=srcOBXStream.FindAt(p5+1,"|") // p5>p7=OBX-5
       set p6=p7
     }
     set p8=srcOBXStream.FindAt(p7+1,"|") // p7>p8=OBX-6
     set tStream=##class(%Stream.GlobalCharacter).%New()

     // renumber OBX-1 to OBX 
     set tSC=tStream.Write("OBX|"_k1_"|")
     
     // set OBX2-2 to "ED"
     set tSC=tStream.Write("ED|")
     
     // copy source OBX-3 to target OBX-3
     set tSC=srcOBXStream.MoveTo(p3+1)
     set tSC=tStream.Write(srcOBXStream.Read(p4-p3-1))
     set tSC=tStream.Write("|")
     
     // copy source OBX-4 to target OBX-4
     set tSC=srcOBXStream.MoveTo(p4+1)
     set tSC=tStream.Write(srcOBXStream.Read(p5-p4-1))
     
     // copy source OBX-6 to OBX-5.3 & OBX-5.4
     set tSC=srcOBXStream.MoveTo(p7+1)
     set docType=srcOBXStream.Read(p8-p7-1)
     set tSC=tStream.Write("|^^"_docType_"^"_docType_"^")
     
     // copy source OBX-5.1 to target OBX-5.5
     // can only set up to 3,641,144 chars at once, so do while loop...
     set startPos=p5+1
     set remain=p6-p5-1
     // characters to read/write in each loop, max is 3,641,144 since .Write limit is a %String
     set charLimit=3000000
     while remain > 0 {
       set tSC=srcOBXStream.MoveTo(startPos)
       set toRead = charLimit
       if toRead > remain {
         set toRead=remain
       }
       set tSC=tStream.Write(srcOBXStream.Read(toRead))
       set remain=remain-toRead
       set startPos=startPos+toRead
     }
     set tSC=tStream.Write("|")

     set obxSegment=##class(EnsLib.HL7.Segment).%New()
     set obxSegment.SegType="2.5:OBX"
     set tSC=obxSegment.StoreRawDataStream(tStream)
     set tSC=target.setSegmentByPath(obxSegment,"OBXgrp("_k1_").OBX")
   }
 }
]]></code>
</transform>
}

}

Para desarrollar y probar esto, utilicé los plugins de VSCode para InterSystems porque las herramientas de prueba de integración no podían manejar el tamaño del mensaje.

También añadiré que, para enviar HL7 sobre HTTPS a InterConnect de Epic, fue necesario crear una clase HTTP personalizada y enviar el tipo de contenido personalizado x-application/hl7-v2+er7.

0
0 114
Artículo Javier Lorenzo Mesa · nov 23, 2022 2m read

Este artículo describe un diseño arquitectónico más flexible para DeepSee. Al igual que en el ejemplo anterior, esta implementación incluye bases de datos separadas para almacenar la memoria caché, la implementación y la configuración de DeepSee y la sincronización de los globals. Este ejemplo introduce una nueva base de datos para almacenar los índices de DeepSee. Redefiniremos los mapeos globales para que los índices de DeepSee no se mapeeen junto con las tablas de hechos y dimensiones.

0
1 145
Artículo Javier Lorenzo Mesa · jul 8, 2022 4m read

Este artículo describe un diseño arquitectónico de complejidad intermedia para DeepSee. Al igual que en el ejemplo anterior, esta implementación incluye bases de datos separadas para almacenar la información, la implementación y la configuración de DeepSee. También presenta dos nuevas bases de datos: la primera para almacenar los globals necesarios para la sincronización, la segunda para almacenar tablas de hechos e índices.

0
0 195
Artículo Ricardo Paiva · dic 3, 2021 2m read

Si defines una tabla/clase persistente, el compilador de clases genera una definición de almacenamiento adecuada. Otra opción es definir un mapeo SQL para un almacenamiento global que ya existe.  Esto ya se explicó estupendamente en otra serie de artículos:  El arte del mapeo de globales para Clases 1 de 3

Después de definir el diagrama de almacenamiento, el compilador de la clase lo podrá ampliar, pero los parámetros fundamentales de almacenamiento no cambiarán.
Esto no significa que no puedas cambiarlo manualmente.

Mi artículo El mapa de bits adoptado incluye un caso como este. Y en combinación con algunas Condiciones WHERE estáticas, como se describió anteriormente, permite que esté disponible también para SQL.

La definición habitual de tus globals de almacenamiento se parecerá a este ejemplo:

<DataLocation>^User.PersonD</DataLocation>
<IdLocation>^User.PersonD</IdLocation>
<IndexLocation>^User.PersonI</IndexLocation>
<StreamLocation>^User.PersonI</StreamLocation
>

Ahora cambiaremos esta definición por una dinámica

<DataLocation>@%MyData</DataLocation>
<IdLocation>@%MyId</IdLocation>
<IndexLocation>@%MyIndex</IndexLocation>
<StreamLocation>@%MyStream</StreamLocation>

y añadimos algo de comodidad

Parameter MANAGEDEXTENT As INTEGER = 0;
ClassMethod SetStorage(ref As %String) As %Integer [ SqlName = SetStorage, SqlProc ]
{
    set %MyData=ref_"D"
      , %MyId=%MyData
      , %MyIndex=ref_"I"
      , %MyStream=ref_"S"
    quit $$$OK
}

Para acceder a los objetos, dirigimos nuestro almacenamiento y lo llenamos

write ##class(Person).SetStorage("^mtemp.Person")
write ##class(Person).Populate(5)

y en SQL:

SELECT  from Person where SetStorage('^mtemp.Person')=1

Funciona con PPG  (^||Person)
mediante el namespace (^|"USER"|Person)
también corresponde al subíndice que se utilizó en El mapa de bits adoptado  con otra variante de ClassMethod SetStorage() 

Para acceso a objetos (sin SQL), funciona incluso para variables locales.
No es el uso deseado, pero demuestra la flexibilidad de esta función. 

Pero ten cuidado. NO funcionará con sharding. Pero eso no es una sorpresa.

0
0 222
Pregunta Robert Cemper · mayo 31, 2021

Cuando inicio una instalación nueva de IRIS o de un contenedor, siempre encuentro los paquetes de interoperabilidad mapeados en el namespace USER.

¿Existe alguna utilidad para quitar este mapeo de forma sencilla, con un clic?
Quitar el mapeo global a global, rutina a rutina, paquete a paquete es muy aburrido.

Es decir: busco una utilidad dentro de IRIS.

La utilidad externa es obvia: Notepad (o cualquier otro editor de texto) 
- limpiar iris,cpf,
- reiniciar IRIS

Es rápido, es eficiente, pero es muy tedioso.

1
0 138
Artículo Alberto Fuentes · abr 7, 2021 6m read

Hola a todos! Os traigo hoy un ejemplo de código que compartía Robert Cemper para mostrar por SQL los registros de error almacenados en ^ERRORS.

Este es un ejemplo de código que funciona en Caché 2018.1.3 e IRIS 2020.2 
No se mantendrá sincronizado con las nuevas versiones. 
Es un ejemplo de código y como tal no está soportado por el Soporte de InterSystems

Los errores en IRIS/Caché/Ensemble se registran entre otros en la global ^ERRORS. Como este mecanismo se remonta a muchas versiones atrás (décadas del milenio anterior!) su estructura está lejos de las estructuras de almacenamiento de SQL típicas.

El global se escribe mediante la rutina ^%ETN.int y el contenido se hace visible desde la línea de comandos del terminal mediante la rutina ^%ERN o en el Portal de Administración como Log de Errores de la Aplicación.

Por defecto no está disponible a través de SQL ya que no hay ninguna clase que lo presente como tabla. Por varias razones:

  • Cuando se diseñó, era una buena práctica tener estructuras similares a índices en los mismos globals que los datos. Si digo "similares", significa que no sirve para SQL.
  • El contenido de los objetos va a niveles más profundos que el resto. En consecuencia, la profundidad de los subíndices (normalmente IdKey) varía de 3 a 11.

^ERRORS es independiente en cada namespace. Está estructurado por Day, SequenceByDay, Type, ItemName (Variable, OREF),Value.

zrcc.ERRORStack muestra esta información como tabla SQL.
El contenido más profundo de los objetos se hace visible por la consulta personalizada incluida.
El procedimiento SQL zrcc.ERRORStack_Dump(Day,Sequence) devuelve todo el contenido disponible y presenta subíndices y valores como se ve en la lista de globals.

A continuación veremos cómo sacar partido de estas utilidades:

Primero: localiza el día que te interese y el número de secuencia con la ayuda de SQL

Por ejemplo: SELECT * FROM zrcc.ERRORStack where item='$ZE'

        Day  Seq    Stk Type    Item    Value
    2020-07-02  1    0     V      $ZE   &lt;WRITE>zSend+204^%Net.HttpRequest.1
    2020-07-07  1    0     V      $ZE   &lt;WRITE>zSend+204^%Net.HttpRequest.1
    2020-07-15  1    0     V      $ZE   &lt;WRITE>zSend+204^%Net.HttpRequest.1
    2020-07-20  1    0     V      $ZE   &lt;LOG ENTRY>
    2020-07-26  1    0     V      $ZE   &lt;WRITE>zSend+204^%Net.HttpRequest.1

A continuación, llama al procedimiento en SQL:   CALL zrcc.ERRORStack_Dump('2020-07-26',1)

Row count: 541 Performance: 0.026 seconds  6557 global references
Ref                                 Value
2020-07-26,1,"*STACK",0,"V","Routine")  zSend+204^%Net.HttpRequest.1
2020-07-26,1,"*STACK",1,"I")    1^S^^^0^
2020-07-26,1,"*STACK",1,"L")    1 SIGN ON
2020-07-26,1,"*STACK",1,"S")    
2020-07-26,1,"*STACK",1,"T")    SIGN ON
2020-07-26,1,"*STACK",1,"V","%dsTrackingKeys","N","""Analyzer""")   6
2020-07-26,1,"*STACK",1,"V","%dsTrackingKeys","N","""Architect""")  7
2020-07-26,1,"*STACK",1,"V","%dsTrackingKeys","N","""DashboardViewer""")    8
2020-07-26,1,"*STACK",1,"V","%dsTrackingKeys","N","""ResultSet""")  9
2020-07-26,1,"*STACK",1,"V","%objcn")   2
2020-07-26,1,"*STACK",3,"V","Task") &lt;OBJECT REFERENCE>[1@%SYS.Task]
2020-07-26,1,"*STACK",3,"V","Task","OREF",1)    142
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,0)  3671
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,1)  +----------------- general information ---------------
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,2)  | oref value: 1
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,3)  | class name: %SYS.Task
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,4)  | %%OID: $lb("13","%SYS.Task")
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,5)  | reference count: 5
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,6)  +----------------- attribute values ------------------
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,7)  | %Concurrency =
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,8)  4 &lt;Set>
- - - 
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,53) | EmailOutput =
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,54) 0
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,55) | EndDate =
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,56) ""
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,57) | Error =
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,58) "&lt;WRITE>zSend+204^%Net.HttpRequest.1"
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,59) | Expires =
- - -
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,111)    | Status =
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,112)    "0 "_$lb($lb(5002,"POST to Server Failed",,,,,,,,$lb(,"%SYS",$lb("$^zSend+204^%Net.HttpRequest.1 +1","$^zPost+1^%Net.HttpRequest.1 +1","$^zSendData+20^FT.Collector.1 +1","$^zTransfer+12^FT.Collector.1 +1","$^zOnTask+3^%SYS.Task.FeatureTracker.1 +1","D^zRunTask+74^%SYS.TaskSuper.1 +1","$^zRunTask+54^%SYS.TaskSuper.1 +1","D^zRun+26^%SYS.TaskSuper.1 +1"))),$lb(6085,"ISC.FeatureTracker.SSL.Config","SSL/TLS error in SSL_connect(), SSL_ERROR_SSL: protocol error, error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed",,,,,,,$lb(,"%SYS",$lb("e^zSend+303^%Net.HttpRequest.1^1","e^zPost+1^%Net.HttpRequest.1^1","e^zSendData+20^FT.Collector.1^1","e^zTransfer+12^FT.Collector.1^1","e^zOnTask+3^%SYS.Task.FeatureTracker.1^1","e^zRunTask+74^%SYS.TaskSuper.1^1","d^zRunTask+54^%SYS.TaskSuper.1^1","e^zRun+26^%SYS.TaskSuper.1^1","d^^^0"))))/* ERROR #5002: Cache error: POST to Server Failed- ERROR #6085: Unable to write to socket with SSL/TLS configuration 'ISC.FeatureTracker.SSL.Config', error reported 'SSL/TLS error in SSL_connect(), SSL_ERROR_SSL: protocol error, error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed' */
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,113)    | SuspendOnError =
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,114)    0
2020-07-26,1,"*STACK",3,"V","Task","OREF",1,115)    | Suspended =
- - -
2020-07-26,1,"*STACK",6,"V","Status1")  1
2020-07-26,1,"*STACK",6,"V","Task") &lt;OBJECT REFERENCE>[1@%SYS.Task]
2020-07-26,1,"*STACK",6,"V","Task","OREF",1)    142
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,0)  3671
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,1)  +----------------- general information ---------------
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,2)  | oref value: 1
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,3)  | class name: %SYS.Task
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,4)  | %%OID: $lb("13","%SYS.Task")
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,5)  | reference count: 5
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,6)  +----------------- attribute values ------------------
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,7)  | %Concurrency =
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,8)  4 &lt;Set>
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,9)  | DailyEndTime =
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,10) 0
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,11) | DailyFrequency =
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,12) 0
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,13) | DailyFrequencyTime =
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,14) ""
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,15) | DailyIncrement =
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,16) ""
2020-07-26,1,"*STACK",6,"V","Task","OREF",1,17) | DailyStartTime =
- - - 
2020-07-26,1,"*STACK",12,"V","%00000","N","""JournalState""")   12
2020-07-26,1,"*STACK",13,"I")   13^Z^ETNERRB^%ETN^0
2020-07-26,1,"*STACK",13,"L")   13 ERROR TRAP S $ZTRAP="ETNERRB^%ETN"
2020-07-26,1,"*STACK",13,"S")   S $ZTRAP="ETNERRB^%ETN"
2020-07-26,1,"*STACK",13,"T")   ERROR TRAP

541 row(s) affected
0
0 200
Artículo Nancy Martínez · jul 22, 2020 8m read

Ejemplos de Mapeos

Obviamente, si tienes un cuatro artículo en la trilogía, debes apostar por las ganancias económicas y escribir el quinto, ¡así que aquí está!

Nota: Hace muchos años, Dan Shusman me dijo que el mapeo de globals es una forma de arte. No existe una manera correcta o incorrecta de hacerlo. El modo en que interpretes los datos te llevará al tipo de mapeo que realizas. Como siempre, existe más de una forma de llegar a la respuesta final. Según vayas revisando mis ejemplos, verás que hay algunos en los que se mapean el mismo tipo de datos, pero de distintas maneras.

0
0 190
Artículo Ricardo Paiva · nov 14, 2019 15m read
¡Hola Comunidad!

Suponga que desarrolló su propia aplicación con la tecnología de InterSystems y ahora quiere realizar varias implementaciones en sus distintos clientes. Durante el proceso de desarrollo usted escribió una guía de instalación detallada para aplicarla, ya que no solo necesita importar clases, también configurar el entorno de acuerdo a sus necesidades. 

Para atender esta tarea específica, InterSystems creó una herramienta especial llamada %Installer. Siga con la lectura para saber cómo utilizarla.

0
0 400
Artículo Estevan Martinez · oct 22, 2019 9m read

El DBMS de InterSystems incorpora una tecnología para trabajar con datos no estructurados (iKnow) y una tecnología de búsqueda de textos completos (iFind). Decidimos profundizar en ambas y hacer algo útil. Como resultado, tenemos DocSearch, una aplicación web para realizar búsquedas en la documentación de InterSystems utilizando iKnow y iFind.

Cómo funciona la documentación de Caché

0
1 237
Artículo Estevan Martinez · jul 1, 2019 7m read

¿Alguno de ustedes desea traer de vuelta una antigua aplicación en MUMPS? Siga estos pasos para mapear sus globales ya establecidos para clases y mostrar todos esos hermosos datos en Objects y SQL.

Si lo anterior no le suena conocido, comience leyendo estos artículos previos:

En este ejemplo mostraré cómo mapear una estructura clásica de padre e hijo.

0
0 237
Artículo Estevan Martinez · jun 19, 2019 8m read

¿Alguno de ustedes desea traer de vuelta una antigua aplicación en MUMPS? Siga estos pasos para mapear sus globales ya establecidos para clases y mostrar todos esos hermosos datos en Objects y SQL

En este ejemplo se estudiarán 4 o 5 cosas diferentes más allá de lo que se cubrió en la Parte 1 

Todo lo que falta por hacer después de esto, es el ejemplo del mapeo de padres e hijos, entonces irá por buen camino.

0
0 207
Artículo Estevan Martinez · jun 5, 2019 7m read

¿Alguno de ustedes desea traer de vuelta una antigua aplicación MUMPS? Siga estos pasos para mapear sus globales ya establecidos para clases y mostrar todos esos hermosos datos en Objects y SQL

Al seguir los sencillos pasos en este artículo y los dos siguientes, podrá mapear todas las variables globales, excepto las más difíciles, para clases de Caché. Para los que estén locos pondré un archivo zip con diferentes mapeos que he coleccionado a lo largo de los años. Esto NO es para datos nuevos; si no cuenta con datos globales ya establecidos, por favor, utilice el almacenamiento predeterminado.

0
0 293
Artículo Jose-Tomas Salvador · mayo 30, 2019 2m read

¿Qué pasaría si pudieras serializar/deserializar objetos en cualquier formato: JSON, XML, CSV,...; siguiendo diferentes criterios, exportar/importar unas propiedades y no otras, transformar valores de una u otra forma antes de exportarlos/importarlos,... y todo ello sin tener que cambiar la definición de la clase? ¿No sería genial si pudieras hacer todo eso?

Bueno, quizás sea un objetivo demasiado ambicioso para cumplirlo al 100% pero, al explorar esta idea, desarrollé muchas clases que pensé que sería bueno compartir. Si deseas probar, cambiar, modificar o mejorar el código, o simplemente echarle un vistazo, puedes hacerlo aquí. También encontrarás una explicación más detallada (consulta Readme.md)

Debes tener presente que esto es una prueba de concepto y la realicé durante mis ratos libres, por lo tanto, seguramente no es lo suficientemente robusta o puede mejorarse... pero, solo estaba jugando!....ok, podría haber esperado al lanzamiento del nuevo JSON Adaptor que seguro resuelve muchos escenarios de una manera más limpia, pero... mientras llegaba... :-) ...
2
0 985