Wednesday, April 24, 2013

PhoneGap 2.x Mobile Application Development Hotshot - Review del libro

Tal como lo anuncie hace poco en mi post En mi librero - PhoneGap 2.x Mobile Application Development Hotshot, estaba leyendo este libro...y ahora...estoy listo para escribir un review.


Primero lo primero...ya lo he leido...pero realmente no inverti mucho tiempo desarrollando todos los proyectos, asi que de hecho que lo voy a volver a leer -:D

Asi que...este libro es bueno o malo? Debo decir...es realmente bueno!

Para mi...la mejor parte de aprender un nuevo lenguage de programacion o herramienta...es realmente construir algo con ella...y este libro esta lleno de ejemplos practicos que te hundiran de lleno en el desarrollo en PhoneGap.

Este libro incluye proyectos que interactuan con Twitter, Camara, Almacenamiento y hasta Geo Localizacion...incluso...incluye un juego! Que tan asombroso es eso?! Realmente asombroso -:)

Los proyectos son faciles de seguir, pero por supuesto...no son simples...son complejos y eso les agrega mas sabor...realmente debes enfocarte en lo que estas haciendo para poder obtener el mayor beneficio...pero nuevamente...haciendolo tu mismo, vas a aprenderlo mas rapido y no te lo vas a olvidar en mucho tiempo...

Quiero monstrarles un par de imagenes de dos proyectos incluidos en el libro...para que tengan una idea de que es lo que van a construir...



Por cierto...aun cuando estoy posteando imagenes basadas en Android...el libro se enfoca tanto en IOS como en Android.

Si nunca has utilizado PhoneGap...o eres un principiante con poca experiencia como yo...entonces este libro es realmente para ti...Feliz codigo!

Saludos,

Blag.

Tuesday, April 16, 2013

En mi librero - PhoneGap 2.x Mobile Application Development Hotshot


Yo siempre estoy leyendo nuevos libros de programacion, asi que pienso que es bueno alertar a la gente de que estoy leyendo algo nuevo y que estoy planeando escribir un review para que todos puedan saber si el libro es bueno o no...

Esta nueva seccion se va a llamar  "En mi librero" y quiero comenzar con un libro llamado:

PhoneGap 2.x Mobile Application Development Hotshot

Ya probe PhoneGap una vez cuando escribi acerca de su integracion con SAP HANA -:)

Este libro se ve realmente muy bueno puesto que cubre muchos Projectos lo que en mi humilde opinion...es la mejor manera de aprender a programando...programando! -;)

Voy a leer este libro y le dire que es lo que pienso -:)

Saludos,

Blag.

Wednesday, April 10, 2013

Un nuevo comienzo


Luego de 3 años  viviendo en Montreal, Canada...con 1 año y 8 meses con Beyond Technologies y 1 año y 3 meses con SAP Labs Canada...estoy haciendo mis maletas nuevamente...

Esta vez, me estoy mudando con mi familia a California...donde voy a trabajar para SAP Labs en Palo Alto.

Porque nos estamos mudando? Simplemente...porque mi equipo Developer Experience esta dividido entre Palo Alto y Waldorf...siendo mi colega Vitaliy Rudnyskiy y yo los unicos en nuestros paises (El en Polania y yo en Canada)...es por eso que tiene total sentido ir donde el equipo esta -;) (Yo no estaba por supuesto dispuesto a aprender Aleman).

Estoy volando este Sabado, asi que mis ultimos dias en Montreal ya estan aqui...aunque trabaje desde mi cada la mayor parte del tiempo, la veces que visite la oficina fueron realmente excelentes...hice muy buenos amigos como Pakdi, Jon, Elsa, Krista, Pierre y Nolwen. Me mantendre en contacto con ellos, claro esta -:)

Al moverme a la oficina de Palo Alto...ambos mi puesto Development Expert y mis tareas van a ser las mismas...bueno...en realidad...mis tareas se van a incrementar -:P Pero a quien le importa?...amo tanto mi trabajo que todas la nuevas tareas son bienvenidas -;)

Una de las cosas realmente buenas de esta mudanza es que voy a poder asistir a todas la conferencias, hackatons y reuniones que se dan en Estados Unidos...asi que estoy muy emocionado...

Por supuesto...despues de que toda la locura de la relocacion este terminada...continuare blogeando como loco, tratando de darles las mejores experiencias de programacion -:)

Saludos,

Blag.

Saturday, March 09, 2013

Volviendonos flexibles con SAP HANA


Muchos de ustedes no se habran dado cuenta de una caracteristica introducida en SAP HANA SPS5. Esta nueva caracteristica se llama "Flexible Tables", lo cual significa que puedes definir una tabla que va a crecer dependiendo de tus necesidades. Veamos un ejemplo...

Defines un tabla con ID, NAME y LAST_NAME. La tabla funciona bien, pero te das cuenta de que tambien tienes que agregar PhoneNumber y Address...en una situacion normal, tendrias que abrir la definicion y agregar esos campos...pero usando Flexibles Tables, so deberias agregar esos campos como parte del INSERT y dejar que SAP HANA haga su magia...

Por supuesto, este escenario es muy dificil que suceda, porque para un par de campos, no tiene ningun sentido...asi que...donde utilizamos las tablas Flexible Tables? Busquedas empresariales, donde necesitamos todos los productos en una sola tabla, y esta tabla puede tener una gran cantidad de columnas...puesto que diferentes productos puedes tener diferentes categorias...

Asi que...para este blog...realmente me rompi la cabeza tratando de buscar un escenario simple donde pudiera demostrar las Flexible Tables...que fue lo que se me ocurrio? SAP HANA, Flexible Tables, R y Twitter...

El codigo es bastante complejo, asi que no voy a explicar linea por linea como funcionar...pero por supuesto, voy a dar una explicacion interesante...

El API de Twitter (Estoy usando version 1 aun cuando esta deprecada, simplemente porque la version 1.1 maneja autenticacion y realmente no queria invertir mucho tiempo en eso...) nos permite obtener informacion desde Twitter...asi que en este caso, estaba interesado en los Hashtags...los que comienzan con un "#" y son usados para identificar y organizar ciertos tweets relacionados con un evento, tecnologia o una persona famosa. (Solo estoy utilizando Tweets, sin tener en cuenta los Retweets)...


Utilizando R, leo el User Timeline para obtener los ultimos 200 tweets de una cuenta en particular. Esta informacion sera enviada de vuelta a  SAP HANA para ser almacenada.



Con los 200 tweets, extraigo los Hashtags, los sumarizo y guardo la informacion tanto en la tabla final como en un tabla intermedia (para ser usada por el siguiente usuario). En esta tabla intermedia, voy a almacenar un String bastante largo con todos los Hashtags separados por coma.

Cuando llega el siguiente usuario, los 200 tweets van a ser leidos, los Hashtags extraidos, combinados con los que estaban almacenados en la tabla intermedia, sumarizados (Esto es muy importante porque queremos llevar un control de los Hashtags anteriores, tanto los que son comunes a ambos usuarios como los que solamente existen en el primer o segundo usuario) y la informacion va a ser guadarda en la tabla final y la informacion del siguiente usuario va a ser grabada (reemplazando a la anterior) como un String largo en la tabla intermedia.

Porque necesitamos esto? Simple...digamos que el primer usuario tiene 3 Hashtags...#SAPHANA, #R y #Python con los valores 3, 2 y 1. El siguiente usuario tendra 5 Hashtags...#SAPHANA, #SAP, #Ruby, #IPhone y #Android con los valores 2, 5, 1, 4 y 3.

Cuando guardemos al primer usuario tendremos:

UserName SAPHANA R Python
First_User 3 2 1

Cuando guardemos al segundo usuario tendremos:

UserName SAPHANA R Python SAP Ruby Android IPhone
First_User 3 2 1 ? ? ? ?
Second_User 3 2 1 5 1 4 3

Ahora...se preguntaran...porque R tiene "0" para el siguiente usuario y SAP tiene "?" para el primer usuario? Facil...como pueden ver...como hemos agregado mas campos (en tiempo de ejecucion) la tabla crece...el campo R ya estaba ahi para el primer usuario asi que tiene un "0" para el siguiente usuario, sin embargo SAP no estaba antes ahi, asi que no sabemos exactamente cuando deberia ser el valor para el primer usuario asi que un "?" va a ser utilizado.

Estoy seguro de que van a tener las cosas mas claras cuando vean las imagenes de la tabla despues de que les muestre el codigo fuente...

Primero, necesitamos crear una tabla llamada "TWITTER_USERS", que va a almacenar a los usuarios con los cuales queremos trabajar...


Luego, necesitamos otra table donde vamos a almacenar los Hashtags y su valores un String largo. Esta tabla se llamara "FIRST_HASH".


Ahora, las cosas se ponen interesantes, puesto que vamos a crear nuestra Flexible Table utilizando un comando muy simple...esta tabla se llamara "TWITTER_HASHTAGS".

Twitter_Hashtags.sql
CREATE COLUMN TABLE TWITTER_HASHTAGS(
USERNAME NVARCHAR(10)
) WITH SCHEMA FLEXIBILITY;

La tabla se ve bastante normal cuando revisamos su definicion...pero es una Flexible Table...como pueden ver...solo hemos definido un campo...asi que esta tabla crecera, crecera y crecera -;)


Lo siguiente que necesitamos crear es un par de TABLE TYPES que nos ayuden en la interaccion de SAP HANA y R...

Table_Types.sql
CREATE TYPE T_COL_NAMES AS TABLE(
COL_NAMES NVARCHAR(1000)
);
 
CREATE TYPE T_COL_VALUES AS TABLE(
COL_VALUES NVARCHAR(1000)
);

Y ahora...estamos listos para comenzar con el codigo...un procedimiento R y dos procedimientos SQLScript...

Get_Hashtags.sql
CREATE PROCEDURE GET_HASHTAGS(IN twittername TWITTER_USERS,IN first_hash FIRST_HASH,
                              OUT out_col_names T_COL_NAMES, OUT out_col_values T_COL_VALUES)
LANGUAGE RLANG AS
BEGIN
UserName = twittername$USERNAME
hashline = first_hash$HASH_LINE
hashvalues = first_hash$HASH_VALUES
 
Get_Twitter<-function(p_source,p_pattern){
  datalines = grep(p_pattern,web_page,value=TRUE)
  getexpr = function(s,g)substring(s,g,g+attr(g,'match.length')-1)
  g_list = gregexpr(p_pattern,datalines)
  matches = mapply(getexpr,datalines,g_list)
  result = gsub(p_pattern,'\\1',matches)
  names(result) = NULL
  return(result)
}
 
Get_Hashtags<-function(p_source){
  check<-!length(grep('\\"([^,\\"]+)\\"', as.character(p_source)))
  if(!check){
    mypattern = '\\"([^,\\"]+)\\"'
    datalines = grep(mypattern,p_source,value=TRUE)
    getexpr = function(s,g)substring(s,g,g+attr(g,'match.length')-1)
    g_list = gregexpr(mypattern,datalines)
    matches = mapply(getexpr,datalines,g_list)
    result = gsub(mypattern,'\\1',matches)
    names(result) = NULL
    return(result)
  }else{
    result<-p_source
    return(result)
  }
}
 
url<-paste("http://api.twitter.com/1/statuses/user_timeline.xml?count=200&screen_name=",UserName,sep="")
mypattern = '<text>([^<]*)</text>'
web_page<-readLines(url)
tweets<-Get_Twitter(web_page,mypattern)
mypattern = '[^\\&]#(\\.?\\w+)'
hash_list<-Get_Twitter(tweets,mypattern)
hashtags<-sapply(hash_list,Get_Hashtags)
hashtags<-as.vector(unlist(hashtags))
hashtags<-toupper(hashtags)
 
dt.hashtags<-data.frame(UserName,hashtags)
tab.hashtags<-table(dt.hashtags)
dt.hashtags<-as.data.frame.matrix(tab.hashtags)
hashtags_names<-names(dt.hashtags)
hashtags_names<-gsub("^\\.",'',hashtags_names)
 
if(length(hashline>=1)){
          hash_line<-gsub("^(\\w)+\\,",'',hashline)
          hash_line<-unlist(strsplit(hash_line, split=","))
          hash_values<-gsub("^(\\'+\\w+\\')+\\,",'',hashvalues)
          hash_values<-as.numeric(unlist(strsplit(hash_values, split=",")))
          hash_frame<-data.frame(names=hash_line,values=hash_values)
          hash_frame["values"]<-0
 
          Col_Names<-""
          Col_Values<-""
 
          for(i in 1:length(hashtags_names)){
                      Col_Names<-paste(Col_Names,hashtags_names[i],sep=",")
                      Col_Values<-paste(Col_Values,dt.hashtags[,i],sep=",")
          }
 
           Col_Names<-gsub("^\\,|\\.",'',Col_Names)
          Col_Values<-gsub("^\\,|\\.",'',Col_Values)
          Col_Names<-unlist(strsplit(Col_Names, split=","))
          Col_Values<-as.numeric(unlist(strsplit(Col_Values, split=",")))
          new_hash_frame<-data.frame(names=Col_Names,values=Col_Values)
          new_hash_frame<-rbind(hash_frame,new_hash_frame)
          new_hash_frame<-aggregate(values ~ names, FUN = "sum", data = new_hash_frame)
          new_hash_names<-new_hash_frame$names
          new_hash_values<-new_hash_frame$values
 
          Col_Names<-"USERNAME"
          Col_Values<-paste("'",UserName,"'",sep="")
 
           for(i in 1:length(new_hash_names)){
                      Col_Names<-paste(Col_Names,new_hash_names[i],sep=",")
                      Col_Values<-paste(Col_Values,new_hash_values[i],sep=",")
          }
}else{
          Col_Names<-"USERNAME"
          Col_Values<-paste("'",UserName,"'",sep="")
 
          for(i in 1:length(hashtags_names)){
                      Col_Names<-paste(Col_Names,hashtags_names[i],sep=",")
                      Col_Values<-paste(Col_Values,dt.hashtags[,i],sep=",")
          }
}
 
col_names<-gsub("^\\,\\.?",'',Col_Names)
col_values<-gsub("^\\,",'',Col_Values)
 
out_col_names<-data.frame(COL_NAMES=col_names)
out_col_values<-data.frame(COL_VALUES=col_values)
END;

Save_Hashtags.sql
CREATE PROCEDURE SAVE_HASHTAGS(IN in_col_names T_COL_NAMES, IN in_col_values T_COL_VALUES)
LANGUAGE SQLSCRIPT AS
v_select VARCHAR(2000);
v_col_names_char NVARCHAR(1000);
v_col_values_char NVARCHAR(1000);
CURSOR c_cursor1 FOR
SELECT COL_NAMES FROM :in_col_names;
CURSOR c_cursor2 FOR
SELECT COL_VALUES FROM :in_col_values;
BEGIN
                    OPEN c_cursor1;
                    FETCH c_cursor1 into v_col_names_char;
                    CLOSE c_cursor1;
                    OPEN c_cursor2;
                    FETCH c_cursor2 into v_col_values_char;
                    CLOSE c_cursor2;
                    DELETE FROM FIRST_HASH;
                    INSERT INTO FIRST_HASH VALUES(:v_col_names_char,:v_col_values_char);
                    v_select := 'INSERT INTO TWITTER_HASHTAGS (' || v_col_names_char || ') 
                                VALUES (' || v_col_values_char || ')';
                    EXEC v_select;
END;

Get_Twitter_Users.sql
CREATE PROCEDURE GET_TWITTER_USERS(UserName NVARCHAR(10))
LANGUAGE SQLSCRIPT AS
BEGIN
          Twitter_Users = SELECT USERNAME FROM TWITTER_USERS WHERE USERNAME = :UserName;
          First_Hash = SELECT HASH_LINE, HASH_VALUES FROM FIRST_HASH;
          CALL GET_HASHTAGS(:Twitter_Users,:First_Hash,T_COL_NAMES,T_COL_VALUES);
          CALL SAVE_HASHTAGS(:T_COL_NAMES,:T_COL_VALUES);
END;

Para que esto funcione, debemos insertar algunos valores en nuestra tabla "TWITTER_USERS"...


Y luego, simplemente llamar al procedimiento "GET_TWITTER_USERS"...

Call_Get_Twitter_Users.sql
CALL GET_TWITTER_USERS('Blag');
CALL GET_TWITTER_USERS('Schmerdy');
CALL GET_TWITTER_USERS('ggread');

Cuando ejecutamos la primera llamada...esto es con el usuario @Blag tendremos la informacion en la tabla  "FIRST_HASH"...


Y esto en nuestra Flexible Table "TWITTER_HASHTAGS"...


Como pueden ver...nuestra tabla comenzo solamente con USERNAME...pero como le pasamos los Hashtags y sus valores...la tabla crece para poder almacenarlos...

Cuando llamamos al siguiente usuario...es decir @Schmerdy tendremos esto en nuestra Flexible Table...


Como pueden ver...en todos los Hashtags que pertenecen a @Blag pero no pertenecen a @Schmerdy tenemos un valor "0"...asi que, que va a pasar con los que pertenecen a @Schmerdy pero no a @Blag?


Esos campos tendran un valor "?", puesto que no existian antes de que los agregaramos...y nuevamente...la tabla crece para almacenar los nuevos valores....

Ahora...algo interesante es que @Schmerdy tiene mas Hashtags que @Blag...asi que, que va a pasar cuando llamemos al ultimo usuario que es @ggread que dicho sea de paso...tiene menos Hashtags que @Schmerdy y @Blag...


@ggread tendra un valor "0" en todos los Hashtags que no perteneces a su usuario...pero tendra valores en aquellos que son similares a @Schmerdy...


Asi que...que pasara con los Hashtags que pertenecen a @ggread pero que no existen en @Schmerdy ni en @Blag? Facil...seran agregados y los valores "?" puesto en aquellos Hashtags que no existian antes...


Me gustaria poder poner la tabla completa...pero contiene mas de 50 columnas...asi que mejor...puedo exportarla a un archivo .CSV...y hacer algun analisis utilizando Visual Intelligence...


Aqui, podemos ver que tan seguido estos tres usuarios han utilizados los Hashtags #SAPHANA, #SAP y #SAPTECHED en sus ultimos 200 tweets...

Asi que...eso es todo...una simple y bonita forma de demostrar como funcionan las Flexible Tables en SAP HANA utilizando mi siempre amado R -:)

Saludos,

Blag.

SimpleDiagrams - Que mas puedes pedir?

Hoy dia, necesitaba una herramienta simple para hacer diagramas...descargue unos cuantos que no eran muy bueno, pero que hacian su trabajo...hasta que...descubri SimpleDiagrams...


Esta impresionante herramienta construida con Adobe Air, me hizo enamorarme a los pocos minutos de haber comenzado a utilizarla.

Contiene muchos graficos en modo dibujo a mano que haran que tus presentaciones se vean mejor que nunca -;)





Cuando lo instalas, puedes usarlo de forma gratuita por 7 dias o pagar el registro que son $25...yo por supuesto...compre la licensia sin pensarlo dos veces...por una herramienta como esta...el dinero es una inversion -;)


Saludos,

Blag.

Saturday, March 02, 2013

PowerBuilder y R se juntan


El otro dia estaba pensando escribir un blog utilizando PowerBuilder, pero no podia decidirme sobre con que otra tecnologia podia integrarlo...por supuesto...R me vino a la mente...

Mi viaje comenzo hace 4 dias...cuando comence a buscar formas de llamar a R desde un lenguaje externo...la ultima vez utilice Rook y Heroku para llamar a R desde SAP Mobile Platform tal como se explica en mi blog Consuming R from SAP Mobile Platform, pero esta vez sabia que tenia que hacer la cosas de una manera diferente.

Mi primera idea fue la de utilizar Rserve que es un servidor de R utilizado por SAP HANA Studio para conectarse a R tal como se explica en mi blog When SAP HANA met R - First kiss, asi que me descargue los archivos de REngine que son dos .jar de Java.

Para poder conectarnos a  Rserve necesitamos configurarlo...asi que basicamente con el paquete Rserve instalado en mi RStudio simplemente necesite crear un simple archivo...

Call_Rserve.R
library("Rserve")
Rserve()

Cuando lo ejecutamos...El servidor de R va a iniciarse como un proceso que puede ser visto en el Task Manager.

Con los archivos listos, me fui a PowerBuilder y simplemente los agrege en el classpath de Java tal como hice para el conector jdbc de SAP HANA tal como se explica en mi blog PowerBuilder - The new kid on Developer Center's block, no funciono...los archivos estaban en el classpath pero no podia llamar a ninguno de los metodos...asi que decidi seguir buscando alternativas y me encontre con Rcaller, un solo archivo .jar de Java que en vez de utilizar Rserve, llama directamente al ejecutable de R...tampoco funciono...la cosa es...en PowerBuilder (con la excepcion del connector jdbc de SAP HANA) necesitas utilizar el EAServer o un servidor que soporte EJB como JBoss...yo nunca he utilizado ninguno de los dos...ademas de que necesitas construir el archivo .jar tu mismo pues se necesita informacion adicional.

Tenia la impresion de que estaba llegando a un callejon sin salida...pero luego pense..."Hey...Estoy utilizando PowerBuilder.NET!" lo cual significa obviamente que se basa en el .NET Framework...lo cual significa que podia utilizar archivos dll de .NET...pero yo solo tenia archivo .jar de Java...

Haciendo un poco mas de investigacion me encontre con IKVM que es una aplicacion que nos permite convertir cualquier archivo .jar de Java en un bonito .dll de .NET...el uso es bastante simple...

Using IKVM
Open CMD
C:\>ikmvc -out:Rcaller.dll -target:module Rcaller.jar

Esto va a producir un archivo .dll llamado Rcaller.dll...pense que estaba en el camino correcto asi que inclui este archivo en mis referencias...no funciono puesto que lastimosamente, IKVM aun esta en desarrollo y no todo de Java has sido traducido a .NET, como por ejemplo la interfaz Java.IO...luego converti los dos archivos REngine .jar de Java en un solo .dll pero tuve la misma suerte...algunas traducciones no estaban presents...

De vuelta a Google a seguir buscando...esta vez...directamente en implementaciones .NET y encontre R.NET que tambien utiliza el ejecutable de R para hacer la integracion...se veia muy prometedor...sin embargo, R.NET aun esta en desarrollo y por alguna razon sin sentido...no podia hacerlo funcionar puesto que el .dll no podia ser encontrado por el motor de .NET...pense...bueno...quizas es culpa de PowerBuilder...vamos a probar en un verdadero entorno .NET...asi que instale Visual Studio Express 2012 for Desktop...la misma suerte...el .dll no podia ser encontrado...

Estaba cansado y molesto...nada parecia funcionar...pero cuando regrese a la pagina de Rserve me di cuenta de que habia un proyecto .NET llamado RserveCLI que utilizaba Rserve para hacer la integracion con R....cuando descargue el proyecto...no habia ninguna .dll disponible, pero eso no era ningun problema...puesto que simplemente lo compile en VS Express y obtuve una brillante .dll lista para ser probada...asi que cree un nuevo proyecto de C# Console y lo probe...funciono perfectamente...

Cuando intente utilizarlo en PowerBuilder.NET por primera vez...no funciono...asi que...como se imaginaran...estaba aun mas cansado y aun mas molesto...y por alguna razon inexplicable...termine borrando un archivo insignificante del .NET framework que simplemente malogro todo...ya no podia ejecutar PowerBuilder o VS Express...suerte maldita...otro viaje para arreglar mi desastre...Desinstale todas las referencias del .NET framework...compiladores VC++...Runtimes...etc...me tomo casi un dia completo para finalmente tener todo de vuelta y en su sitio...sin embargo...hasta el dia de hoy...VS Express murio completamente...no puedo ni siquiera instalarlo nuevamente...puesto que el instalador me muestra la pantalla del VS por unos segundos y luego desaparece sin ningun mensaje de error aparente...

En fin...por lo menos PowerBuilder estaba funcionando nuevamente...y finalmente supere mis problemas con el RserveCLI dll...

El programa que voy a mostrarles, es sin duda alguna bastante simple...tanto por la naturaleza de la integracion...como por el hecho de que RserveCLI aun esta en desarrollo...no hay realmente mucho que podamos hacer...pero aun asi...pienso que ayuda a ilustrar dos puntos interesantes...
  • PowerBuilder.NET puede interactuar con archivos .dll de .NET
  • R puede ser utilizado por una amplia gama de lenguajes de programacio 


Este programa nos va a pedir que llenemos dos arrays, Array A y Array B...cada uno con cuatro elementos (de alguna manera...y por alguna razon que sigo intentado comprender...cuando llamamos a R desde PowerBuilder, necesitamos pasar un array bi-dimensional arrays...siendo el minimo [2,2]...)
Veamos el layout...


Asi que...basta de hablar...vamonos al codigo fuente...
Primero, vamos a definir una variable global puesto que vamos a utilizarla en muchos lugares...

Global Variables
RserveCli.RConnection conn

Luego, vamos a llamar al evento Open de la ventana principal (w_window).

w_window.Open
#if DEFINED PBDOTNET then
@System.Net.IPAddress ipadd
byte ip[4] = {127,0,0,1}
ipadd = create @System.Net.IPAddress(ip)
@System.Int32 port
port = create System.Int32
port = 6311
conn = create RserveCli.RConnection(ipadd,port,"","")
#end if

Aqui, estamos diciendo que vamos a utilizar codigo al estilo de .NET, asi que deberia ser ignorado por el compilador de PowerBuilder...como estamos utilizando Rserve en nuestra laptop, utilizamos la direccion IP del localhost y asignamos el puerto por defecto de Rserve que es 6311. Definimos una conexion con el servidor utilizando el metodo RConnection. Si se preguntan por que estoy utilizando la @ es porque quiero estar seguro de que estoy llamando al System de .NET y no de PowerBuilder...Yo se que dije que el compilador de PowerBuilder deberia ignorarlo...pero es mejor prevenir que lamentar....

Tenemos un boton llamado cb_load que va a cargar los arrays definidos en la pantalla.

cb_load.Clicked
double varA[2,2], varB[2,2]
varA[1,1] = double(Array_A_1.Text)
varA[1,2] = double(Array_A_3.Text)
varA[2,1] = double(Array_A_2.Text)
varA[2,2] = double(Array_A_4.Text)
varB[1,1] = double(Array_B_1.Text)
varB[1,2] = double(Array_B_3.Text)
varB[2,1] = double(Array_B_2.Text)
varB[2,2] = double(Array_B_4.Text)
 
#if DEFINED PBDOTNET then
::conn["A"] = RserveCli.Sexp.Make(varA)
::conn["B"] = RserveCli.Sexp.Make(varB)
#end if

Aqui, simplemente creamos dos arrays y los llenamos. Llamamos a  ::conn con el extra "::" para dejarle saber al compilador que es una variable global. Cuando utilizamos el Sexp.Make estamos diciendo que queremos crear un vector de R utilizando el array que pasamos como parametro. Asi que un vector llamdo A and y otro llamado B.

Ahora que tenemos los arrays cargados en memoria, podemos proseguir con los otros botones.

cb_sum.Clicked
#if DEFINED PBDOTNET then
txtResult.Text = ::conn["A+B"].ToString()
#end if

Esto es bastante simple, simplemente estamos diciendo...envia el comando "A+B" a R y dame el resultado como un String. En R, A+B va a producir un suma de vectores...lo cual significa que en vez de crear un  factor conteniendo los valores de A y B, va a tomar el primer elemento de A y sumarlo con el primer elemento de B, va a hacer lo mismo con el resto de los elementos. Ven a ver esto mas claramente, mas adelante.

cb_multiply.Clicked
#if DEFINED PBDOTNET then
txtResult.Text = ::conn["A*B"].ToString()
#end if

Esto es basicamente lo mismo, con la excepcion de que vamos a multiplicar en vez de sumar.

cb_min.Clicked
#if DEFINED PBDOTNET then
RserveCli.Sexp varC = ::conn["min(c(A,B))"]
txtResult.Text = varC.ToString()
#end if

Esto es un poco mas interesante...vamos a crear una variable Sexp y luego ejecutar una operacion en R...al hacer "c(A,B)" vamos a tomar los elementos de B y agregarlos a A, asi que A va a contener los valores de ambos...al utilizar "min()" vamos a obtener el valor minimo.

cb_max.Clicked
#if DEFINED PBDOTNET then
RserveCli.Sexp varC = ::conn["max(c(A,B))"]
txtResult.Text = varC.ToString()
#end if

Aqui es la mista historia, pero vamos a obtener el valor maximo en vez del minimo.

cb_mean.Clicked
#if DEFINED PBDOTNET then
RserveCli.Sexp varC = ::conn["mean(c(A,B))"]
txtResult.Text = varC.ToString()
#end if

La misma historia nuevamente...R es realmente facil de usar...calculamos el promedio que simplemente va a ser la suma de los elementos divida por la cantidad de elementos.

cb_summary.Clicked
#if DEFINED PBDOTNET then
RserveCli.Sexp varC = ::conn["summary(c(A,B))"]
string varS = "Min: " + varC[0].ToString() + " / 1st Qu: " + varC[2].ToString() + " / Median: " &
                                + varC[2].ToString() + " / Mean: " + varC[3].ToString() + " / 3rd Qu: " &
                                + varC[4].ToString() + " / Max: " + varC[5].ToString()
txtResult.Text = varS
#end if

Aqui, vamos a obtener el summary de mezclar A y B. El summary es una variable compleja que va a retornarnos los valores minimo y maximo, el 1er y 3er quadrantes, la media y el promedio. Simplemente llamandolo como un array, podemos extraer todos los valores...

Finalmente...y para hacer que nuestro programa se ejecute...necesitamos llamar al ultimo pedazo de codigo...

wfpapp.Open
open(w_window)

wpfapp es el nombre de nuestra aplicacion (por defecto de PowerBuilder). Aqui decimos, abre nuestra ventana principal llamada w_window (otro por defecto de PowerBuilder)...

Ejecutemos la aplicacion y veamos como funciona...


Estamos simplemente llenado las cajas de texto con datos inventados...Array A va a contener 1,2,3,4 y Array B va a contener 5,6,7,8.


Cuando presionamos el boton sum...1 y 5 van a sumarse, 2 y 6 van a sumarse, 3 y 7 van a sumarse y finalmente, 4 y 8 van a sumarse.


Cuando presionamos el boton Multiply, lo mismo va a pasar, pero los numeros van a ser multiplicados en vez de ser sumados.



Ya deben saber que pasa cuando presionamos los botones Minimum, Maximum y Mean...asi que vamos a movernos directamente al boton Summary. los valores minimo y maximo, el 1er y 3er quadrantes, la media y el promedio
Comp pueden ver...este es un ejemplo muy simple...pero creanme...despues de 4 dias completos de trabajo...estoy feliz de finalmente haberlo hecho funcionar...y ustedes pensaban que mi trabajo era sencillo? Pienselo de nuevo -;) 4 dias y noches de full stress...

Saludos,

Blag.

Monday, February 18, 2013

Cuando SAP HANA conocio a R - Que hay de nuevo?


Desde que escribi mi blog Cuando SAP HANA conocio a R - El primer beso he recibido muchos buenos comentarios...y uno de esos comentarios fue..."Que hay de nuevo?"...

Bueno...como ya deben saberlo SAP HANA trabaja con R utilizando Rserve, un paquete que permite comunicarse con un servidor R, asi que realmente...no pueden haber muchas cosas nuevas...pero...lo bueno es que SAP HANA ha sido probado con R 2.15 y Rserve 0.6-8 asi que cualquier nueva caracteristica agregada en R o Rserve es instaneamente disponible en SAP HANA -;)

Pero! No escribiria un blog si que no hubiera al menos una nueva caracteristica, no? Pueden leer mas sobre eso aqui SAP HANA R Integration Guide.

Por supuesto...para esto necesitas SAP HANA rev. 48 (SPS5)

Asi que, cual es la nueva caracteristica? Bueno...puedes guardar un modelo de entrenamiento como lm() o ksvm() directamente en la base de datos para usarla despues. Esto es realmente excelente, porque si tienes un calculo muy grande y complejo que hacer, solo necesitas guardar el modelo y utilizarlo despues sin tener que volver a hacer todo el procesamiento nuevamente.

Hagamos un ejemplo...y espero que todos los fanaticos de R no me maten por esto...porque cuando se trata de Estadistica...estoy realmente perdido en el bosque -:(

Digamos que tenemos dos tablas del paquete SFLIGHT...SPFLI y STICKET, asi que queremos predecir cuantas veces un cliente va a viajar a diferentes destinos (CITYFROM-CITYTO) dependiendo de cuantas veces todos los clientes han viajado a los mismos destinos.

Vamos a crear un archivo SQLScript para obtener la informacion, transformarla, crear el model y guardarlo en la base de datos...

Build_Flight_Model.sql
--Creamos un TYPE T_FLIGHTS y tomamos la informacion de las tablas SPFLI y STICKET.
 
DROP TYPE T_FLIGHTS;
CREATE TYPE T_FLIGHTS AS TABLE (
CARRID NVARCHAR(3),
CUSTOMID NVARCHAR(8),
CITYFROM NVARCHAR(20),
CITYTO NVARCHAR(20)
);
 
--Creamos un TYPE FLIGHT_MODEL_T y una tabla FLIGHT_MODEL para guardar el modelo
--en la base de datos
 
DROP TYPE FLIGHT_MODEL_T;
CREATE TYPE FLIGHT_MODEL_T AS TABLE (
ID INTEGER,
DESCRIPTION VARCHAR(255),
MODEL BLOB
);
 
DROP TABLE FLIGHT_MODEL;
CREATE COLUMN TABLE FLIGHT_MODEL (
ID INTEGER,
DESCRIPTION VARCHAR(255),
MODEL BLOB
);
 
--Este procedimiento R va a recibir la informacion de T_FLIGHTS, 
--crear una tabla con el campo FLIGHT_NAME que a contener la concatenacion de
--los campos CARRID, CITYFROM and CITYTO. ejm: AA-NEW YORK-SAN FRANCISCO.
--Vamos a convertir la tabla en un data.frame, asi que todos los valores iguales en FLIGHT_NAME
--van a ser sumarizados.
--Utilizamos la funcion subset(), vamos deshacernos de todos los FLIGHT_NAME's que tienen una frequencia 
--menor o igual que 0.
--Vamos a utilizar la funcion nrow() para contar todas las ocurrencias de FLIGHT_NAME y multiplicarla por 
--10 (Lo guardamos en f_rows)
--Vamos a utilizar la funcion sum() para sumar todas las frequencias y luego dividirlas por f_rows 
--(Lo guardamos en f_sum)
--Vamos a utilizar la funcion mapply() para dividir cada una las frecuencias por f_sum
--Vamos a utilizar la funcion order() para ordenar por FLIGHT_NAME
--Vamos a utilizar la funcion colnames() para asignar nombres a nuestro data.frame
--Vamos a utilizar la funcion lm() para generar una Regresion Lineal basada en el FLIGHT_NAME 
--y su frecuencia
--Finalmente, vamos a utilizar la funcion personalizada generateRobjColumn() para guardar el resultado 
--del modelo en el buffer.

DROP PROCEDURE FLIGHT_TRAIN_PROC;
CREATE PROCEDURE FLIGHT_TRAIN_PROC (IN traininput "T_FLIGHTS", OUT modelresult FLIGHT_MODEL_T)
LANGUAGE RLANG AS
BEGIN
generateRobjColumn <- function(...){
          result <- as.data.frame(cbind(
                    lapply(
                              list(...),
                              function(x) if (is.null(x)) NULL else serialize(x, NULL)
                    )
          ))
          names(result) <- NULL
          names(result[[1]]) <- NULL
          result
}
tab<-table(FLIGHT_NAME=paste(traininput$CARRID,traininput$CITYFROM,traininput$CITYTO,sep="-"))
df<-data.frame(tab)
ss<-subset(df,(df$Freq>0))
freq<-ss$Freq
f_rows<-(nrow(ss)) * 10
fsum<-sum(freq) / f_rows
ss$Freq<-mapply("/",ss$Freq, fsum)
flights<-ss[order(ss$FLIGHT_NAME),]
colnames(flights)<-c("FLIGHT_NAME","FREQUENCY")
lmModel<-lm(FREQUENCY ~ FLIGHT_NAME,data=flights)
modelresult<-data.frame(
ID=c(1),
DESCRIPTION=c("Flight Model"),
MODEL=generateRobjColumn(lmModel)
)
END;
 
--Este procedimiento SQLSCRIPT va a tomar toda la informacion necesaria de las tablas SPFLI y STICKET 
--y la va a asignar a flights
--Vamos a llamar al procedimiento R FLIGHT_TRAIN_PROC
--Vamos a hacer un INSERT para finalmente grabar el buffer en la base de datos
 
DROP PROCEDURE POPULATE_FLIGHTS;
CREATE PROCEDURE POPULATE_FLIGHTS ()
LANGUAGE SQLSCRIPT AS
BEGIN
flights = SELECT SPFLI.CARRID, CUSTOMID, CITYFROM, CITYTO
             FROM SFLIGHT.SPFLI INNER JOIN SFLIGHT.STICKET
             ON SPFLI.CARRID = STICKET.CARRID
             AND SPFLI.CONNID = STICKET.CONNID;
CALL FLIGHT_TRAIN_PROC(:flights, FLIGHT_MODEL_T);
INSERT INTO "FLIGHT_MODEL" SELECT * FROM :FLIGHT_MODEL_T;
END;
 
CALL POPULATE_FLIGHTS();

Cuando llamamos a POPULATE_FLIGHTS(), nuestra tabla FLIGHT_MODEL se deberia ver asi...



Si se preguntan porque tenemos una "X"...es porque el contenido esta serializado y grabado como un campo BLOB...si inspeccionamos el contenido, vamos a recibir varios y raros numeros hexadecimales...

En fin...le tomo 6.165 segundos a SAP HANA procesar 1,842,160 registros.

Ahora que tenemos nuestro modelo guardado de forma segura en la base de datos, podemos movernos hacia nuestro siguiente archivo SQLScript...

Get_and_Use_Flight_Model.sql
--Vamos a crear un TYPE T_PREDICTED_FLIGHTS y una tabla PREDICTED_FLIGHTS para guardar la informacion
--del actual numero de vuelos y el estimado (de acuerdo con nuestra prediccion).
 
DROP TYPE T_PREDICTED_FLIGHTS;
CREATE TYPE T_PREDICTED_FLIGHTS AS TABLE (
CUSTOMID NVARCHAR(8),
FLIGHT_NAME NVARCHAR(60),
FREQUENCY INTEGER,
PREDICTED INTEGER
);
 
DROP TABLE PREDICTED_FLIGHTS;
CREATE TABLE PREDICTED_FLIGHTS (
CUSTOMID NVARCHAR(8),
FLIGHT_NAME NVARCHAR(60),
FREQUENCY INTEGER,
PREDICTED INTEGER
);
 
--En este procedimiento de R, vamos a recibir los vuelos para un cliente en particular, el modelo almacenado 
--en la base de datos y vamos a retornar el resultado para que pueda ser
--guardado en nuestra tabla PREDICTED_FLIGHTS.
--Vamos a utilizar la funcion unserialize() para extraer el modelo.
--Vamos a crear una tabla conteniendo un campo llamado FLIGHT_NAME que sera la concatenacion de los campos
--CARRID, CITYFROM and CITYTO.
--ejm: AA-NEW YORK-SAN FRANCISCO. y tambien el CUSTOMID
--Vamos a convertir la tabla en un data.frame, asi que todos los valores FLIGHT_NAME iguales van a ser
--sumarizados.
--Vamos a utilizar la funcion colnames() para asignar nombres a nuestro data.frame
--Vamos a utilizar la funcion nrow() para obtener el numero de registros en nuestro data.frame (Se guarda en dfrows)
--Vamos a utilizar la funcion rep() para repetir el valor de CUSTOMID del primer registro dfrows veces
--Vamos a utilizar la funcion predict() para predecir la cantidad de vuelos basados en nuestro model (obtenido de 
--la base de datos) y la nueva informacion que hemos obtenido
--Finalmente, vamos a crear un data.frame conteniendo toda la informacion que deberia ser guardada en nuestra
--tabla PREDICTED_FLIGHTS

DROP PROCEDURE USE_FLIGHT;
CREATE PROCEDURE USE_FLIGHT(IN flights T_FLIGHTS, IN modeltbl FLIGHT_MODEL_T, OUT out_flights T_PREDICTED_FLIGHTS)
LANGUAGE RLANG AS
BEGIN
lmModel<-unserialize(modeltbl$MODEL[[1]])
tab<-table(FLIGHT_NAME=paste(flights$CARRID,flights$CITYFROM,flights$CITYTO,sep="-"),CUSTOMID=flights$CUSTOMID)
df<-data.frame(tab)
colnames(df)<-c("FLIGHT_NAME","CUSTOMID","FREQUENCY")
dfrows<-nrow(df)
customid<-rep(df$CUSTOMID[1],dfrows)
prediction=predict(lmModel,df,interval="none")
out_flights<-data.frame(CUSTOMID=customid,FLIGHT_NAME=df$FLIGHT_NAME,FREQUENCY=df$FREQUENCY,PREDICTED=prediction)
END;
 
--Este procedimiento SQLSCRIPT seleccionara informacion de la tabla FLIGHT_MODEL y la guardara en la variable
--flight_model
--Vamos a seleccionar toda la informacion necesaria de las tablas SPFLI y STICKET basada en el customer ID
--Vamos a llamar al procedimiento R USE_FLIGHT y este nos retornara PREDICTED_FLIGHTS que vamos a  
--guardar en la base de datos
 
DROP PROCEDURE GET_FLIGHTS;
CREATE PROCEDURE GET_FLIGHTS(IN customId NVARCHAR(8))
LANGUAGE SQLSCRIPT AS
BEGIN
flight_model = SELECT * FROM FLIGHT_MODEL;
out_flights = SELECT SPFLI.CARRID, CUSTOMID, CITYFROM, CITYTO FROM SFLIGHT.SPFLI INNER JOIN SFLIGHT.STICKET
                   ON SPFLI.CARRID = STICKET.CARRID AND SPFLI.CONNID = STICKET.CONNID
                   WHERE CUSTOMID = :customId;
CALL USE_FLIGHT(:out_flights, :flight_model, PREDICTED_FLIGHTS);
INSERT INTO "PREDICTED_FLIGHTS" SELECT * FROM :PREDICTED_FLIGHTS;
END;

Ahora que tenemos todos nuestros Stored Procedures listos...podemos crear el ultimo archivo SQLScript para finalmente llenar nuestra tabla PREDICTED_FLIGHTS con algunos registros...

Predict_Flights_for_Customers.sql
CALL GET_FLIGHTS('00000156');
CALL GET_FLIGHTS('00002078');
CALL GET_FLIGHTS('00002463');

Como pueden ver...solo necesitamos llamar al procedimiento GET_FLIGHTS, pasando el Customer ID...

Este proceso tomo solamente 970ms y genero 122 registros para los 3 clientes...

Ahora estoy seguro de que se dan cuenta lo impresionante que es esto...si no hubieramos guardado nuestro modelo en la base de datos...tendriamos que calcular el modelo para cada cliente...y nos tomaria aproximadamente 7 segundos para cada uno (obtener el lm(), ademas de la prediccion)...eso seria alrededor de 21 segundos para los 3 clientes...mientras que podemos decir que todo el proceso nos tomo algo de 7 segundos...si tuvieran que calcular la prediccion para todos los mas de un millon de clientes...estarias en un problema serio -:)

Veamos el contenido de nuestra tabla PREDICTED_FLIGHTS...por suepuesto, solo voy a mostrar una parte...


Podemos volcar esta informacion un archivo CSV desde SAP HANA Studio y digamos...utilizarlo en SAP Visual Intelligence para generar un bonito grafico...


Espero que les guste -:)

Saludos,

Blag.

Wednesday, February 13, 2013

Un ejemplo brillante - SAP HANA, R y Shiny


Como ya deben saberlo...amo R...un impresionante y open souce lenguaje de programacion estadistico. Asi que hoy dia, decidi aprender algo nuevo utilizando R.

No hay muchos servidores web para R, pero hay uno que realmente me gusta, llamado Rook, el cual ya cubri en mi blog RSAP, Rook y ERP.

Hoy dia, comence a aprender uno que esta causando conmocion en la comunidad de R, llamado Shiny. Debo decirlo...me enamore de el a primera vista...

Asi que se preguntaran...que es tan bueno acerca de Shiny? Bueno...ademas del hecho de que te permite crear aplicacion web utilizando R...es completamente dinamico...lo cual significa que una vez que cambiar el valor de un parametro, el grafico y no toda la pagina web completa es recargada automaticamente...impresionante, no?

Asi que...cuando escribi sobre Ruby y SAP en mi blog Ruby se une a la fiesta de SAP HANA, intente emular la SE16 para ver las tablas del paquete SFLIGHT...esta vez...voy a hacer lo mismo pero con un pequenho cambio...la aplicacion va a permitirte escoger una tabla, pero tambien elegir cuantos registros quieres mostrar...

Asi que, que necesitamos? Simplemente...si no lo has hecho ya...instalar estos dos paquetes..."shiny" y "RODBC"...


Luego de esto, debemos crear una carpeta llamada Shiny y luego de esto crear una nueva llamada SAP_HANA_R (Esto es solo para estar mas organizados).

Ahora, debemos crear dos archivos R, llamados ui.R y server.R


ui.R
library("shiny")
library("RODBC")
 
ch<-odbcConnect("HANA_TK",uid="SYSTEM",pwd="manager")
odbcQuery(ch,"SELECT table_name from SYS.CS_TABLES_ where schema_name = 'SFLIGHT'")
tables<-sqlGetResults(ch)
odbcClose(ch)
 
shinyUI(pageWithSidebar(
 
  headerPanel("SAP HANA and R using Shiny"),
 
  sidebarPanel(
    selectInput("Table", "Choose a table:",
                choices = tables$TABLE_NAME),
    numericInput("Records", "Number of Records to view:", 10)
  ),
 
  mainPanel(
    tableOutput("view")
  )
))

server.R
library("shiny")
library("RODBC")
 
shinyServer(function(input, output) {
 
  output$view <- reactiveTable(function() {
    ch<-odbcConnect("HANA_TK",uid="SYSTEM",pwd="manager")
    schema_table<-paste("SFLIGHT.",input$Table,sep="")
    query<-paste("SELECT TOP",input$Records,"* FROM",schema_table)
    odbcQuery(ch,query)
    result<-sqlGetResults(ch)
    odbcClose(ch)
 
    head(result, n = input$Records)
  })
})

Cuando hemos terminado con estos dos archivos...podemos crear uno nuevo, solo para llamar a nuestra aplicacion.

Shiny_HANA.R
library(shiny)
setwd("C:/Blag/R_Scripts")
runApp("Shiny/SAP_HANA_R")

Tengan en cuenta que el setwd("C:/Blag/R_Scripts") es mi carpeta principal para scripts de R Script, puesto que setwd significa "Set Working Directory" (Establecer el directorio de trabajo)...

Cuando ejecutamos Shiny_HANA.R, el browser sera llamado mostrandonos los parametros y la tabla por defecto.


Como pueden ver, podemos escoger una nueva tabla si queremos.


Tambien podemos escoger cuantos registros queremos mostrar...



Espero que les guste... -:)

Saludos,

Blag.

Saturday, February 09, 2013

PHP tambien la rompe con SAP HANA! (Version Linux)


Si me conocen...saben que no fanatico de Linux...sin embargo...todavia tengo mi laptop LG T1 Express Dual que compre en el 2007...para mi primer SAP TechEd...como se daran cuenta...correr Windows e instalar mas software en esta laptop era una manera de matarla lentamente...asi que la laptop se quedo en mi ropero por un buen tiempo...hasta que decidi instalarle Ubuntu y regresarla a la vida una vez mas -:)

Lo que dicen es cierto...aun la mas vieja y lenta laptop se comporta de maravilla con Linux...y para su informacion, estoy escribiendo este blog en mi laptop con Linux -;)

Hace un par de semanas escribi un blog llamado PHP tambien la rompe con SAP HANA! puesto que me di cuenta de que no habria escrito sobre PHP y SAP HANA y tambien porque sabia que habia mucha gente que tenia muchos problemas tratando de hacerlos funcionar juntos...pero...lo escribi para Windows...y los usuarios de Linux continuan luchando para lograr hacer esto funcionar...asi que...hora de un nuevo blog...

Debo confesarlo...sin este increible blog HANA with odbc on Ubuntu 12.04 escrito por Ethan Zhang seguramente seguiria peleando con las opciones de conexion...pero en fin...no siendo un fanatico de Linux...pense que seria una buena idea detallar todos los pasos y problemas que tuve que superar para hacer que esto finalmente funcione...

Por supuesto...no tenia PHP instalado en mi Linux...asi que el primer paso era instalarlo...sin embargo, no queria pasar demasiado tiempo instalando todo por separado, asi que decidi utilizar LAMP.

LAMP Installation
$ sudo apt-get install tasksel
$ sudo tasksel install lamp-server


Luego de estas dos simples lineas...PHP estaba listo y funcionando...asi que el siguiente paso era instalar Cliente de SAP HANA. Version Linux de 32bits.

Ahora, neesitaba algo para conectarme con mi Servidor SAP HANA Server...asi unixODBC era la mejor opcion.

unixODBC Installation
$ sudo apt-get install unixODBC unixODBC-dev

Con esto, era hora de configurar la conexion ODBC...

odbc.ini configuration
$ sudo vi odbc.ini
 
[HDB]
driver = /usr/sap/hdbclient32/libodbcHDB32.so
ServerNode = hana_server:30115

Era hora de hacer la primera prueba...asi que hice lo siguiente...

ODBC Testing
$ isql -v HDB SYSTEM manager

Luego de esto...tuve un error diciendo que libodbcHDB32.so no podia ser encontrado porque el archivo o directorio no existian...era muy raro...decidi darle una revisada a las dependencias...

Checking Dependencies
$ ldd /usr/sap/hdbclient32/libodbcHDB32.so

Esto me trajo al hecho de que libaio.so no se encontraba en mi sistema...asi que usando el Ubuntu Software System...simplemente lo instale...


Mi proximo test del isql fue exitoso, asi que...siendo un poco flojo...simplemente copie el codigo de mi blog anterior y lo ejecute -;)


PHP_HANA.php
<?php
$conn = odbc_connect("HDB","SYSTEM","manager",SQL_CUR_USE_ODBC);
if (!($conn))
{
          echo "<p>Connection to DB via ODBC failed: ";
        echo odbc_errormsg ($conn);
        echo "</p>\n";
}
else{
          if(isset($_POST["CARRID"]) == false){
                  $sql = "SELECT CARRID, CARRNAME FROM SFLIGHT.SCARR WHERE MANDT = 300";
               $rs = odbc_exec($conn,$sql);
               print("<DIV ALIGN='CENTER'>");
               print("<H1>SAP HANA from PHP</H1>");
               print("<FORM NAME='Get_Data' ACTION='$_SERVER[PHP_SELF]' METHOD='POST'>");
               print("<SELECT NAME='CARRID'>");
               while($row = odbc_fetch_array($rs)){
                     $carrid = $row["CARRID"];
                     $carrname = $row["CARRNAME"];
                     print("<OPTION VALUE='$carrid'>$carrname");
               }
        print("</SELECT>");
        print("<INPUT TYPE='SUBMIT' VALUE='Get Data'>");
        print("</FORM>");
        print("</DIV>");
        }
        else{
                  $carrid_param = $_POST["CARRID"];
                $sql = "SELECT * FROM \"_SYS_BIC\".\"blag/AV_FLIGHTS\"
                        WHERE CARRID = '$carrid_param'";
                $rs = odbc_exec($conn,$sql);
                print("<DIV ALIGN='CENTER'><TABLE BORDER=1>");
                print("<TR><TH>MANDT</TH><TH>CARRID</TH><TH>CONNID</TH>
                       <TH>COUNTRYFR</TH><TH>CITYFROM</TH>
                       <TH>AIRPFROM</TH><TH>COUNTRYTO</TH>
                       <TH>CARRNAME</TH><TH>DISTANCE</TH></TR>");
                while($row = odbc_fetch_array($rs)){
                          $mandt = $row["MANDT"];
                $carrid = $row["CARRID"];
                $connid = $row["CONNID"];
                $countryfr = $row["COUNTRYFR"];
                $cityfrom = $row["CITYFROM"];
                $airpfrom = $row["AIRPFROM"];
                $countryto = $row["COUNTRYTO"];
                $carrname = $row["CARRNAME"];
                $distance = $row["DISTANCE"];
                print("<TR><TD>$mandt</TD><TD>$carrid</TD>
                         <TD>$connid</TD><TD>$countryfr</TD>
                         <TD>$cityfrom</TD><TD>$airpfrom</TD>
                         <TD>$countryto</TD><TD>$carrname</TD>
                         <TD>$distance</TD></TR>");
               }
               print("</TABLE>");
               print("<A HREF='PHP_HANA.php'>Go Back</A></DIV>");
          }
}
?>



Como pueden ver...no fue muy dificil...aun para un fanatico de Windows -:D

Saludos,

Blag.

Tuesday, January 29, 2013

CodeJam - SAP HANA


He realizado con exito dos SAP Code Jams...uno en Montreal y el otro en Lima. Para eso, cree un Workbook para que todos pudieran ganar experiencia y seguirme con la parte de Hands-On section del evento.

Luego de eso dos eventos...decidi que era hora de que este documento pase al retiro...que significa esto? No voy a volver a usarlo...asi que voy a compartirlo con la comunidad.

Me tomo mucho tiempo y esfuerzo y sobre todo pasion escribirlo...asi que espero que todos lo encuentren util.


Saludos,

Blag.