martes, 21 de abril de 2015

Ideas de reporting con xDocReport y Gx-Java

Algo que me motiva es hacer aplicaciones que tengan opciones de configuración potentes, que den a los usuarios buenas posibilidades de ajustarse a diferentes escenarios, a cambiantes contextos de negocios. O sea, a lo que tenemos por estos tiempos, cada día.
Tengo la idea de que cada vez es más común y aceptado que usuarios de configuración dispongan de más recursos en este sentido, de tal forma que los sistemas se puedan ajustar y adaptar rápido a determinados cambios. Por ejemplo, cuando se necesita definir un producto nuevo o cuando cambian reglas de cálculo de una nómina.
Otra veta a explotar en este camino es el reporting. En este post les cuento un poco sobre xDocReport, una api Java que nos permite trabajar con templates diseñados por cualquier usuario, usando MS Word, insertando campos de "combinación de correspondencia" (mergefields) que asumirán los valores de un xml al momento de crear el reporte. Usaremos la sintaxis de FreeMarker para los campos en el template docx.

Bueno, lo primero: Descargar xDocReport desde aquí.
Luego crear una KB GX-Java (yo usé la XEv2) con un webpanel que llame a un procedure, copiar todos los Jar a la carpeta web y setear la Path List de la siguiente forma:


A continuación preparar el template con MS Word.
Debe quedar más o menos así:


Dejé resaltados los campos para que se aprecie bien, así que no esperen que el archivo les quede exactamente igual. Los campos se crean usando Ctrl-F9 y se completan con la opción "Editar Campo" que aparece con el click derecho. Ahí deben dejarlo de tipo MergeField y en el nombre poner el identificador o directiva que corresponda (por ejemplo ${doc.project.name}).


El xml lo podemos generar con un sdt y el método ToXml, pero en este ejemplo lo pondremos en un archivo:
<devs>
    <developer>
        <devname>ZERR</devname>
        <devlastname>Angelo</devlastname>
        <devmail>angelo.zerr@gmail.com</devmail>
    </developer>           
    <developer>
        <devname>Leclercq</devname>
        <devlastname>Pascal</devlastname>
        <devmail>pascal.leclercq@gmail.com</devmail>
    </developer>           
</devs>           
El siguiente sería el código del procedure:

&tmpString = !"C:" + chr(92) + "xDocReportFiles" + chr(92)
&fileIN  = &tmpString + !"xDocRptTemplate.docx"
&fileXml = &tmpString + !"xDocRptData.xml"
&fileOUT = &tmpString + !"xDocRptOut.docx"
java try {
// 1) Load Docx file by filling  template engine and cache it to the registry
java     java.io.InputStream in = new java.io.FileInputStream([!&fileIN!]);
java     fr.opensagres.xdocreport.document.IXDocReport report;
java     fr.opensagres.xdocreport.template.TemplateEngineKind kind = fr.opensagres.xdocreport.template.TemplateEngineKind.Freemarker;
java     report = fr.opensagres.xdocreport.document.registry.XDocReportRegistry.getRegistry().loadReport(in,kind);
// 2) Create context Java model
java     fr.opensagres.xdocreport.template.IContext context = report.createContext();
java     java.io.InputStream projectInputStream = new java.io.FileInputStream([!&fileXml!]);
java     org.xml.sax.InputSource projectInputSource = new org.xml.sax.InputSource( projectInputStream );
java     freemarker.ext.dom.NodeModel project = freemarker.ext.dom.NodeModel.parse( projectInputSource );
java     context.put( "doc", project );
// 3) Generate report by merging Java model with the Docx
java     java.io.OutputStream out = new java.io.FileOutputStream(new java.io.File([!&fileOUT!]));
java     report.process(context, out);
java     } catch (Exception e) {
java         System.out.println("Exception thrown: " + e);
java     e.printStackTrace();
java }
RESULTADO
Para ejecutar, asegurarse de que los archivos estén en el directorio que corresponda, especialmente los Jar que deben estar en la "lib" de nuestra aplicación en el Tomcat.
Si todo sale bien, aparecerá nuestro report con los datos provistos por el xml y debiera verse así:



Eso, por ahora.

Queda una lista interesante de cosas por hacer: generar data con SDTs/DataProviders, queries dinámicas con "For Xml" (usando un store proc en Sql Server), convertir a Pdf, convertir las librerías para poder usarlas con Gx-Csharp, etc..

salu2!!!

martes, 20 de enero de 2015

Scripting GX C# con Jint 2.4


Amigos, en esta ocasión hago un "update" del tema de scripting con Jint, uno de los engines javascript que podemos usar con C#, que desde un tiempo pasó a la versión 2.0 con importantes mejoras.

En esta pasada aprovecho de mostrarles cómo hacer para que desde el javascript puedan llamar a procedures gx que estén en la misma KB, lo que favorece de buena forma las posibilidades de este tipo de integración y su aplicación en casos reales.

Pondremos en un procedure jintEngine el código de más abajo, en el que se puede apreciar en las primeras líneas el truco para "cortar" el C# generado por GX y meter en medio una definición de "delegate" y un método que usaremos luego para llamar a un procedure gx desde el javascript. Nótese que el método recibe un string y retorna otro string, tal como el "delegate".

 parm(&jsVarchar,&returnVarchar,&isOK);
 csharp     executePrivate2();
 csharp }
 csharp public delegate string getObjectDeleg(string inStr);
 csharp private string getObject(string inStr)
 csharp {
         csharp [!&in!] = inStr;
         //acá la lamada a otro procedure de nuestra kb
         jintGetObjJsonString.call(&in,&ret)
         csharp return [!&ret!];
 csharp }
 csharp void executePrivate2( )
 csharp {
 
 // código para instanciar el engine Jint
 &returnVarchar = ""
 &isOK = true
 csharp try {
         csharp var engine = new Jint.Engine(cfg => cfg.AllowClr());
         //acá hacemos una instancia del delegate
         csharp getObjectDeleg getObjectDelegInstance = new getObjectDeleg(getObject);
         //y se lo pasamos al engine
         csharp engine.SetValue("getObjJsonStr",getObjectDelegInstance);
         //ejecutar el javascript que viene en &jsVarchar
         csharp engine.Execute([!&jsVarchar!]);
          //el resultado lo dejamos en &returnVarchar
         csharp [!&returnVarchar!] = engine.GetCompletionValue().ToString();
 csharp }
 csharp catch ( Exception e ){
         csharp [!&returnVarchar!] = e.ToString();
         &isOK = false
 csharp }  
El procedure jintGetObjJsonString podrá ser invocado desde el javascript con getObjJsonStr, de acuerdo a cómo se lo pasamos al engine con SetValue.
Para este procedure podríamos tener el siguiente código:
 parm(&in,&ret); //parms de tipo varchar
 //asumimos que en &in viene un json con la estructura de &sdtEntrada
 &sdtEntrada.fromjson(&in)
 ...
 // aquí lo que sea que se necesite para cargar el &sdtSalida
 ...
 &ret = &sdtSalida.tojson() 

Un ejemplo de javascript que podríamos ejecutar:
 //objeto a usar como parm (con la misma estructura de &sdtEntrada)
 var objSdtEntrada = {Id:1,............};
 //llamada al proc gx usando delegate
 var objDesdeGX = getObjJsonStr(JSON.stringify(objSdtEntrada));
 function mainFunction(){
     ...
     var otroDesdeGX = getObjJsonStr(JSON.stringify(objSdtEntrada));
     ...
     //el retorno puede ser un json o cualquier cosa string
     return JSON.stringify(otroDesdeGX);
 }
 //finalizar con la invocación a la función "main"
 mainFunction();
Lo importante de este ejemplo es mostrar cómo se invoca el método delegate mediante la función getObjJsonStr, que finalmente llama al procedure jintGetObjJsonString, y que debemos usar una función para hacer el return que Jint atrapa con GetCompletionValue.

Un abrazo y buen 2015 a todos.
salu2!

P.D.: para este año me gustaría que Artech nos anunciara un generador web basado en AngularJS.

lunes, 13 de octubre de 2014

Scripting GX .Net + Jint (JavaScript Server-Side)

Llevo un rato trabajando con el generador C# y en un determinado proyecto tuve que incorporar un mecanismo de scripting JavaScript server-side, que terminé implementando en base a Jint.
En este tipo de cosas se puede llegar a niveles de complejidad altos, según lo creativos que estemos, pero acá les dejo un par de ejemplos sencillos. A ver si algunos se animan y hacen sus primeras pruebas en este tema.

Caso 1

Se trata de cómo pasar variables sencillas al engine y ejecutar un JavaScript que use esas variables, en este caso sólo con una instrucción return:
 &idVarchar = "miVariable" //id de la variable en el script
 &miVarchar = "Hola Mundo!" //valor de la variable
 &jsVarchar = "return miVariable;" //script del usuario

Acá viene el código C#:
 csharp try {  
     csharp Jint.JintEngine engine = new Jint.JintEngine();  
     csharp engine.SetParameter([!&idVarchar!], [!&miVarchar!]);  
     csharp var result = engine.Run([!&jsVarchar!]);  
     csharp [!&returnVarchar!] = result.ToString();}  
 csharp catch (Jint.JintException e){  
     csharp [!&returnVarchar!] = e.Message;}  
 csharp catch (Exception e ){  
     csharp [!&returnVarchar!] = e.ToString();}  
- Instanciamos el engine
- Pasamos nuestra variable al engine
- Le damos run con el script de una línea
- Y el retorno lo pasamos a una variable varchar (debería traer "Hola Mundo!")
- Si hubiera errores, los atrapamos y retornamos la descripción.

Caso 2

En este ejemplo tomaremos una variable basada en un SDT y la pasamos al engine como un string Json.
 &xidVarchar = "Persona" //id del objeto a crear en el script
 &idVarchar = "jintJson" + &xidVarchar //id del string Json en el script
 &sdtPersona.Nombre = "John Doe Jr." //datos en la variable &sdtPersona
 &sdtPersona.Edad = 35
 &miVarchar = &sdtPersona.ToJson()  //pasamos la variable sdt a un string json
 //iniciamos el script con un eval que transforma el Json en un objeto JavaScript
 &jsVarchar = "var " + &xidVarchar + " = eval('(' + " + &idVarchar + " + ')');" + newline()
 + "//inicio del script del usuario" + newline()
 + "return Persona.Nombre;" + newline()
El código C# es igual al del Caso 1:
- Instanciamos el engine
- Pasamos nuestra variable string Json al engine
- Le damos run con el script que inicia transformando el Json en el objeto Persona
- Y retornamos Persona.Nombre a una variable varchar (debería traer "John Doe Jr.")
- Si hubiera errores, los atrapamos y retornamos la descripción.

Bonus Track

Si se requiere conocer el tipo del valor de retorno, podemos hacer algo apoyándonos en GetType():
 csharp Type typeX = result.GetType();
 csharp [!&typeVarchar!] = typeX.Name;
 do case
     case &typeVarchar = "Double"
         csharp Double returnDbl = (Double)result;
         csharp [!&returnVarchar!] = returnDbl.ToString("F4",System.Globalization.CultureInfo.InvariantCulture);
     case &typeVarchar = "DateTime"
         csharp [!&returnDateTime!] = (DateTime)result;
         &returnVarchar = &returnDtt.ToFormattedString()
     otherwise
         csharp [!&returnVarchar!] = result.ToString();
 endcase

viernes, 10 de octubre de 2014

Pendientes del Live Editing

Estuve la semana pasada en Montevideo con motivo del evento GeneXus 2014 y como siempre quedan muchas cosas dando vueltas por ahí, ideas nuevas, otras no tanto, novedades interesantes y la sensación de que iniciamos nuevamente un ciclo junto a GX.

Ahora les voy a comentar de una de las sorpresas del GX24: el “Live Editing”. Una funcionalidad que podremos ver en la próxima versión de GeneXus, ¿o tal vez antes, en alguna actualización de la Evolution 3?.

De qué se trata el “Live Editing”? Bueno, ni más ni menos que de permitirnos hacer cambios en un objeto y reflejarlos de inmediato en una instancia en ejecución, sin necesidad de build y deploy.


Por ejemplo, puedo estar modificando definiciones en el editor de temas y ver el resultado al instante en los formularios de una aplicación que está corriendo en Tomcat, sin necesidad de hacer build, en algo que parece ser “simplemente” el reemplazo de archivos CSS en el directorio de la aplicación.

Pero es bastante más que eso.

La presentación que vimos iniciaba con algo parecido, pero luego iba a más, derechamente modificando el layout en un panel de una aplicación corriendo en IOS en la que se apreciaba de inmediato el cambio de posición de los elementos. Y para coronar todo, más allá de colores y layouts, pudimos ver un live-editing de un evento de usuario donde el presentador cambió el código y tras un pestañeo la aplicación cambió su comportamiento de mostrar un detalle a mostrar un mensaje.
Luego en la keynote final pudimos ver un ejemplo más impactante: en vez de levantar un popup, ahora pedía tomar una foto y la tuiteaba!


Está muy buena la posibilidad de hacer, o afinar, el diseño con este mecanismo, aunque en lo personal no quiero tener nada que ver con el diseño. Yo preferiría que el diseñador me entregara unos templates html que yo incorporara a la KB y nada más.
También está muy bueno que podamos verificar y corregir el comportamiento de algunos programas sin tener que pasar por el build. En lo personal esto último me importa más.

Pero, bueno, independiente de las preferencias de cada uno, el "Live Editing" contribuirá de buena forma en hacernos más efectivos y productivos con GX.

Por otra parte, sería interesante conocer un poco de en qué se basa la implementación del “Live Editing” en los diferentes generadores. Creo que en general debe tratarse de algunas técnicas de “hot swap” de clases , que consiste en reemplazar las clases cargadas en la máquina virtual (hablando de Java) sin necesidad de reiniciar algo.

miércoles, 17 de septiembre de 2014

Superheroic GeneXus (GX+AngularJS)

He estado experimentando con una idea de pattern, descubriendo algo un tanto obvio: que se trata de una gran cantidad de trabajo!!!

Pero bueno, es mi idea de pattern, ver cómo generar automáticamente una aplicación basada en un template bootstrap que compré por ahí, y estaba en eso, pensando cómo iba a hacer para emular lo de la MasterPage y el ContentPlaceHolder, y me voy dando cuenta que lo ideal era usar otro template que tiene una versión basada en ¡AngularJS de Google!.

https://angularjs.org/

Siempre es bueno descubrir algo nuevo e investigar de qué se trata, así que me puse a leer por aquí, por allá viendo unos videos, de paso me enteré de que hay un Kindle para android, y resultó  que AngularJS pretende ser “lo que HTML debió haber sido”, “HTML mejorado para web apps”, “Superheroic JavaScript MVW Framework”. Lo de MVW me causó gracia.

Básicamente, AngularJS se trata de un conjunto de cosas JavaScript, que permiten, por ejemplo, agregar tus propios tags a la sintaxis HTML y también hacer algo de data binding.

El data binding es la funcionalidad para hacer que un elemento que se muestra en una pantalla esté ligado a un origen de datos, de tal forma que si en los datos hay un cambio de valor, dicho cambio se refleje en la pantalla automáticamente. No es nada nuevo en realidad. Lo interesante es cómo lo implementa AngularJS con templates HTML y objetos JavaScript.


Una forma de data binding en GeneXus es, por ejemplo, cuando ponemos atributos directo en un webform, o en un grid. Nada más es necesario. Sabemos que en la pantalla vamos a ver los valores. Otra forma es cuando cargamos valores en una variable SDT relacionada a un UserControl: el desarrollador se ocupó de lo necesario para que la data se visualize como corresponda, o sea, implementó el data binding.

De vuelta a mi proyecto, estaba en eso de pensar el pattern, construyendo las primeras cosas: templates html, procedures http, business components, data providers y otros, pero nada de webpanels. “Nada de webpanels” estaba diciéndome, pensando cómo lo hago con los eventos y las variables de sesión, y de pronto me salté para otro lado y me imaginé que bueno estaría que GX hiciera el HTML y JavaScript basado en este framework de Google.
Podría ser que estamos frente a un nuevo desafío para ARTech: el reemplazo del actual marco HTML-JavaScript de GeneXus por uno basado en AngularJS.

Con todo lo interesante que me pareció esto de AngularJS y la oportunidad que, a mi modo de ver, representa para GeneXus, también se me hace que este tipo de herramientas, que empiezan a ir en dirección de abstraernos del detalle, de automatizar muchas tareas, también podrían ser vistas como “amenazas”.

Y me vino de vuelta una frase que escuché por ahí: “el mayor problema de GeneXus es que no tiene competencia”. Porque es cierto, no existen competidores directos, que se puedan distinguir con claridad, que ofrezcan la efectividad y productividad que nosotros podemos lograr con GX.
Y es un problema, porque la competencia haría mejor y más fuerte a GX.

martes, 2 de septiembre de 2014

Múltiples condiciones por atributo

A propósito de un pattern que estoy ideando, reparé nuevamente en este asunto y me animé a dejar alguna idea aquí.

Se trata del tema de todos los Where que debemos escribir cuando queremos que la consulta tenga un cierto dinamismo:


Si todo el trabajo lo va a hacer el pattern, no habría de que preocuparse mucho, sean 10 o 50 repeticiones. Pero, claro, ese código no se ve muy "elegante".


A mi me gustaría que tuviésemos una función para usar en conjunto con la cláusula When. Algo así como esto:


La variable a usar estaría basada en un SDT con la estructura necesaria para cargarla con todos los valores y operadores, y esa información se usaría para construir el "where" del query, tal como se hace hoy con las condiciones que incluyen la cláusula When.

Saludos!!!




lunes, 1 de septiembre de 2014

Variables basadas en estructura local

Ando por estos días un tanto creativo, y se me ocurrió dejar este post con un deseable para el editor de variables:



Como se aprecia en la imagen, la idea es que podamos declarar variables basadas en una estructura, y colecciones para usar con las mismas.
Con algo así ganaríamos no tener que definir tantos SDTs que muchas veces sólo se usan puntualmente en uno o dos programas.

Saludos!!!