Tuesday, November 19, 2013

Llamando a R desde el ERP - Un pequeño y sucio hack

Desde que me uní a SAP hace cerca de 2 años, simplemente dejé de utilizar ABAP…aún cuando lo utilice por casi 11 años cuando era consultor…

Hace una semana, estaba pensando en escribir un nuevo blog…algo alucinante…con un hack…algo que me permitiera simplemente descanzar y no volver a blogear por el resto del año…

Pense sobre el ERP y R…a pesar que algunas cosas ya han sido hechas como el Analytics with SAP and R de Piers Harding. Yo quería llamar a R desde el ERP y no al ERP desde R -;)

Mientras pensaba en esto…pensé que la mejor manera de hacerlo sería reutilizar algo que ya había utilizado en mi blog Consuming R from SAP Mobile Platform donde utilice Rook en Heroku para crear una página REST que pudiera ser consumible…

Así que…para este blog en particular…quería hacer algo realmente simple…llamar a R, generar un gráfico y mostrarlo en mi programa ABAP…aquí les dejo un pequeño diagrama para tener las cosas más claras…


Por supuesto, cuando intenté esto la primera vez…falló…miserablemente…simplemente porque la implentación de Rook en Heroku no tiene capacidades gráficas instaladas…así que era imposible poder generar un gráfico…

Vamos a ensuciarnos las manos y separemos este blog en pasos para que se vuelva más fácil y sencillo -:)

Compilar R con capacidades gráficas en Heroku

Este paso fué largo y doloroso…me tomó casi una semana tenerlo listo…y graciosamente, el resto de pasos me tomaron solo algo de 6 horas…en fín…sigamos adelante…

Empecé mirando estos dos asombrosos proyectos en Github…rookonheroku y heroku-buildpack-r-build-r Tomé algunas cosas de los dos y creé mi propio script y pasos…

Si no tienes el Heroku Tool Belt instalado…anda y descargalo…Logeate en Heroku y haz lo siguiente…

Pasos para instalar R en Heroku con capacidades gráficas
mkdir myproject && cd myproject
mkdir bin
echo "puts 'OK'" > config.ru
echo "source 'http://rubygems.org'\n gem 'rack'" > Gemfile
#Abre la carpeta de tu proyecto y modifica el Gemfile en Notepad…reemplaza el "\n" por un salto de línea.
bundle install
git init . && git add . && git commit -m "Init"
heroku apps:create myproject --stack=cedar
git push heroku master
#Copia y pega el contenido de mi script installR.sh en la carpeta de tu proyecto, 
#también copia los archivos features.h y features-orig.h de heroku-buildpack-r-build-r)
git add . && git commit -am "message" && git push heroku master
heroku ps:scale web=0
heroku run bash
cd bin/
./installR.sh

Aquí esta el código del script installR.sh...

installR.sh
#!/bin/bash
 
# NOTE: Heroku Cedar Stack
#  gcc needs to be 4.3
 
# See http://cran.r-project.org/doc/manuals/R-admin.html for details on building R
 
## HELPERS
 
function download() {
  if [ ! -f "$2" ]; then
    echo Downloading $2...
    curl $1 -o $2
  else
    echo Got $2...
  fi
}
 
function build() {
  echo ----------------------------------------------------------------------
  echo Building $@...
  echo ----------------------------------------------------------------------
  echo
  pushd $1
    ./configure --prefix=$vendordir/$2 ${@:3} && make && make install && make clean
  popd > /dev/null
  echo
  echo
 
  # add to libraries and pkg-config
  export LD_LIBRARY_PATH="$vendordir/$2:$LD_LIBRARY_PATH"
  export PKG_CONFIG_PATH="$vendordir/$2/lib/pkgconfig:$PKG_CONFIG_PATH"
 
}
 
## SCRIPT
 
set -e
 
r_version="${1:-2.13.1}"
r_version_major=${r_version:0:1}
 
if [ -z "$r_version" ]; then
  echo "USAGE: $0 VERSION"
  exit 1
fi
 
basedir="$( cd -P "$( dirname "$0" )" && pwd )"
 
# create output directory
vendordir=/app/vendor
mkdir -p $vendordir
 
echo ======================================================================
echo Downloading and unpacking dependancies...
echo ======================================================================

# We need to install xz to be able to unzip the gcc package we're going to download in a minute
curl http://gfortran.com/download/x86_64/xz.tar.gz -o xz.tar.gz
tar xzvf xz.tar.gz 

# Get and unpack gcc-4.3 binary, including gfortran
curl http://gfortran.com/download/x86_64/snapshots/gcc-4.3.tar.xz 
-o gcc-4.3.tar.xz
./usr/bin/unxz gcc-4.3.tar.xz 
tar xvf gcc-4.3.tar 
 
# http://www.freetype.org/freetype2/
freetype_version=2.5.0
download http://www.mirrorservice.org/sites/download.savannah.gnu.org/releases/freetype/freetype-$freetype_version.tar.gz 
freetype-$freetype_version.tar.gz
tar xzf freetype-$freetype_version.tar.gz
 
# http://directfb.org/
directfb_version=1.2.9
directfb_ver_major=${directfb_version:0:3}
download http://directfb.org/downloads/Core/DirectFB-$directfb_ver_major/DirectFB-$directfb_version.tar.gz 
DirectFB-$directfb_version.tar.gz
tar xzf DirectFB-$directfb_version.tar.gz
 
# http://www.libpng.org/pub/png/libpng.html
libpng_version=1.2.50
download ftp://ftp.simplesystems.org/pub/libpng/png/src/libpng12/libpng-$libpng_version.tar.gz 
libpng-$libpng_version.tar.gz
tar xzf libpng-$libpng_version.tar.gz
 
# http://www.cairographics.org
pixman_version=0.29.4
download http://www.cairographics.org/snapshots/pixman-$pixman_version.tar.gz 
pixman-$pixman_version.tar.gz
tar xzf $basedir/pixman-$pixman_version.tar.gz
 
# http://www.freedesktop.org/software/fontconfig
fontconfig_version=2.9.0
download http://www.freedesktop.org/software/fontconfig/release/fontconfig-$fontconfig_version.tar.gz 
fontconfig-$fontconfig_version.tar.gz
tar xzf $basedir/fontconfig-$fontconfig_version.tar.gz
 
# http://www.cairographics.org
cairo_version=1.9.14
download http://www.cairographics.org/snapshots/cairo-$cairo_version.tar.gz 
cairo-$cairo_version.tar.gz
tar xzf $basedir/cairo-$cairo_version.tar.gz
 
# http://www.pango.org
#pango_ver=1.35
#pango_version=$pango_ver.0
#download http://ftp.gnome.org/pub/GNOME/sources/pango/$pango_ver/pango-$pango_version.tar.xz 
#pango-$pango_version.tar.xz
#tar xJf $basedir/pango-$pango_version.tar.xz
 
# R
download http://cran.r-project.org/src/base/R-$r_version_major/R-$r_version.tar.gz 
R-$r_version.tar.gz
tar xzf R-$r_version.tar.gz
 
# http://gcc.gnu.org/wiki/GFortran
gcc_version=4.3
download http://gfortran.com/download/x86_64/snapshots/gcc-$gcc_version.tar.xz 
gcc-$gcc_version.tar.xz
tar xJf $basedir/gcc-$gcc_version.tar.xz -C $vendordir
 
# patch gcc features.h file
# see http://permalink.gmane.org/gmane.comp.gcc.help/40166
mkdir -p $vendordir/gcc-$gcc_version/lib/gcc/x86_64-unknown-linux-gnu/$gcc_version/include-fixed
cp $basedir/features.h $vendordir/gcc-$gcc_version/lib/gcc/x86_64-unknown-linux-gnu/$gcc_version/include-fixed/features.h
 
# https://www.gnu.org/software/libc/
glibc_version=2.7
glibc_version_x=2.7ds1
download ftp://ftp.gunadarma.ac.id/linux/debian/pool/main/g/glibc/glibc_2.7.orig.tar.gz 
glibc_$glibc_version.tar.gz
tar xzf $basedir/glibc_$glibc_version.tar.gz -C $vendordir
tar xjf $vendordir/glibc-$glibc_version/glibc-$glibc_version_x.tar.bz2 -C $vendordir
 
echo ============================================================
echo Building dependencies...
echo ======================================================================
 
build "$basedir/freetype-$freetype_version" freetype
build "$basedir/DirectFB-$directfb_version" DirectFB
build "$basedir/libpng-$libpng_version" libpng
build "$basedir/pixman-$pixman_version" pixman
build "$basedir/fontconfig-$fontconfig_version" fontconfig
build "$basedir/cairo-$cairo_version" cairo
 
# copy over missing header files
cp $basedir/cairo-$cairo_version/src/*.h $vendordir/cairo/include
 
#build $basedir/pango-$pango_version pango
 
# build R
echo ============================================================
echo Building R
echo ============================================================
cd $basedir/R-$r_version/
 
export LDFLAGS="-L$vendordir/gcc-$gcc_version/lib64"
export CPPFLAGS="-I$vendordir/glibc-$glibc_version/string/ -I$vendordir/glibc-$glibc_version/time"
export PATH="$vendordir/gcc-$gcc_version/bin:$PATH"
 
echo ----------------------------------------------------------------------
echo LD_LIBRARY_PATH=$LD_LIBRARY_PATH
echo PKG_CONFIG_PATH=$PKG_CONFIG_PATH
echo ----------------------------------------------------------------------
 
./configure --prefix=$vendordir/R --enable-R-shlib --with-readline=no --with-x=yes
make

cd /app/bin
#ln -s R-2.15-1/bin/R
ln -s R-2.13.1/bin/R 

rm gcc-4.3.tar 
rm glibc_2.7.orig.tar.gz 
rm R.tar.gz 
rm xz.tar.gz 
rm -rf usr/
rm -rf gcc-4.3/bin
rm -rf gcc-4.3/lib
rm -rf gcc-4.3/libexec
rm -rf gcc-4.3/info
rm -rf gcc-4.3/man
rm -rf gcc-4.3/share
rm -rf gcc-4.3/include

rm glibc-2.7/*.tar.bz 
cd bin/glibc-2.7/
rm -rf abilist/ abi-tags aclocal.m4  argp assert/ b* BUGS  C* c* d* e* F* g* h* i* I* l* m* M* N* aout/ 
rm -rf LICENSES  n* o* p* P* R* r* scripts/ setjmp/ shadow/ shlib-versions signal/ socket/ soft-fp/ 
rm -rf stdio-common/ stdlib/ streams/ sunrpc/ sysdeps/ sysvipc/ termios/ test-skeleton.c timezone 
rm -rf tls.make.c version.h Versions.def wcsmbs/ wctype/ WUR-REPORT 

cd ../R-2.13.1
rm -rf src
rm Make*
rm -rf doc
rm NEWS*
rm -rf test
rm config*
rm O* README ChangeLog COPYING INSTALL SVN-REVISION VERSION
NEED etc

Con eso…tendrás una instalación completa de R con capacidades gráficas…por supuesto…necesitarás borrar toda la basura generada…pero llegaremos a eso después…

Ahora…necesitamos instalar Rook…pero necesitamos la versión 1.0-2 así que necesitamos descargarla e instalarla desde la fuente…

Descargar Rook 1.0-2
Wget http://cran.r-project.org/src/contrib/Archive/Rook/Rook_1.0-2.tar.gz -o Rook_1.0-2.tar.gz

Cargamos R…simplemente diciendo…

Llamando a R
R

Puesto que así cargaremos el entorno de R…

Instalando Rook
install.packages(/Rook_1.0-2.tar.gz, repos = NULL, type="source")
#Ahora ya podemos salirnos de R
q()

Como fuí lo suficientemente flojo como para no trabajar en el script para que los archivos se borren automáticamente…basicamente…la carpeta “bin” se debería ver así…


Y la carpeta “vendor” así…


Por favor eliminen las carpetas utilizando…

Eliminando Carpetas
rm –rf nombre_de_carpeta

Una vez que hemos terminado…necesitamos guardar nuestro trabajo y enviarlo a otro lado, puesto que Heroku es solo de lectura…una vez que nos vamos…perdemos todo…

Respalda tus archivos
tar -cvzf bin.tar.gz bin
tar -cvzf vendor.tar.gz vendor
scp bin.tar.gz me@myserver.com:/myproject/bin.tar.gz
scp vendor.tar.gz me@myserver.com:/myproject/vendor.tar.gz
 
#Para scp necesitas un servidor…y yo tenía mi R en AWS…así que lo usé…debería verse así…
scp -i XXX_X.pem bin.tar.gz root@XX.XX.XXX.XXX:/Blag/bin.tar.gz

Una vez que hemos guardado todo…podemos cerrar nuestra sesión en Heroku y seguir trabajando…podemos utilzar WinSCP para copiar los archivos de nuestro servidor AWS a nuestra carpeta local del proyecto…descomprimirlos y grabar los siguientes archivos de rookonheroku…
  • config.ru
  • demo.R
  • Procfile
Luego…simplemente debemos enviar todo de vuelta a Heroku…

Último push en Heroku
git add . && git commit -am "message" && git push heroku master
heroku ps:scale web=1

Por supuesto…esto es mucho trabajo…y lo digo en serio…mucho

Así que…he subido la versión compilada y final a GitHub -;) Para obtenerla…simplemente hagan esto…

Obtener Blagrook
git clone git:// github.com/atejada/BlagRook.git yourproject 
#(Esto clonará mi Github y creará un folder llamado yourproject para guardar los códigos)
heroku create myapp #(Create an application for the Rook script)
git push heroku master #(Esto permite pasar todo lo de la carpeta yourproject a tu cuenta en Heroku)

Crear el código para la aplicación Rook

Una última cosa que debemos hacer…es crear una aplicación Rook que va a trabajar en nuestro servidor Rook…

demo.R
library(Rook)
 
newapp<-function(env){
 req<-Rook::Request$new(env)
 res<-Rook::Response$new()
 
 carrid_param<-c(req$params()$carrid)
 seats_param<-c(req$params()$seats)
 
 carrid_param<-strsplit(carrid_param,",")
 carrid_param<-c(carrid_param[[1]][1],carrid_param[[1]][2],
                 carrid_param[[1]][3])
 seats_param<-strsplit(seats_param,",")
 seats_param<-c(as.numeric(seats_param[[1]][1]),as.numeric(seats_param[[1]][2]),
                   as.numeric(seats_param[[1]][3]))

 params<-data.frame(carrid_param,seats_param)

 bmp("R_Plot.bmp",type=c("cairo"))
 dotchart(params$seats_param,labels=params$carrid_param,
          xlab="Number of seats",ylab="Carriers")
 dev.off()
  
 to.read = file("R_Plot.bmp", "rb")
 x<-readBin(to.read, raw(),n=231488)
 hex<-paste(x, collapse = "")

  res$write(hex)
 
  res$finish()
}
 
 
server = Rhttpd$new()
server$add(app = newapp, name = "summarize")
server$start(listen="0.0.0.0", port=as.numeric(Sys.getenv("PORT")))
 
 
while(T) {
  Sys.sleep(10000)
}

En pocas palabras lo que pasa aquí es simple...recibimos los parámetros "carrid" y "seats"...ambos vienen como una cadena separada por "," así que utilizamos strsplit para separarlas y crear un factor con los valores. En el caso de "seats" necesitamos que sea numérico así que utilizamos la función as.numeric. Finalmente creamos un data.frame combinando "carrid" y "seats". Utilizándo "Cairo" (Es por esto que necesitamos capacidades gráficas en R) creamos un archivo Bitmap que va a ser el resultado de crear un dotchart. Una vez que ha sido creado, lo leemos, utilizando la función readBin obtenemos su representación Hexadecimal. Utilizando paste y su método collapse, ponemos todo en una gran cadenas y lo enviamos de vuelta...más facil no puede ser -;)

Estoy seguro que se preguntarán como llegué al número 231488 en la línea de readBin…bueno…descargué un interesante editor Hexadecimal llamado HxD luego creé el gráfico en mi R local y lo cargué…luego me fuí a la última linea y tomé el número Hexadecimal…


Utilizando la calculadora simplemente cambié el valor Hexadecimal 38830 a decimales, lo multipliqué por 16 y luego agregué otros 16 puesto que la primera línea no se cuenta…simple y sencillo -;)

Crear el módulo de funciones ZUPLOAD_GRAPHICS_TABLE

Este módulo de funciones va a permitirnos cargar el gráfico generado, en la transacción SE78.

ZUPLOAD_GRAPHICS_TABLE
FUNCTION ZUPLOAD_GRAPHICS_TABLE.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(OBJECT) TYPE  TDOBJECT DEFAULT 'GRAPHICS'
*"     REFERENCE(NAME) TYPE  TDOBNAME
*"     REFERENCE(BTYPE) TYPE  TDBTYPE
*"     REFERENCE(ID) TYPE  TDID DEFAULT 'BMAP'
*"     REFERENCE(RESOLUTION) TYPE  TDRESIDENT OPTIONAL
*"     REFERENCE(RESIDENT) TYPE  TDRESIDENT OPTIONAL
*"     REFERENCE(AUTOHEIGHT) TYPE  TDAUTOHGHT OPTIONAL
*"  TABLES
*"      T_BITMAP OPTIONAL
*"----------------------------------------------------------------------

  TYPES: TY_BOOLEAN(1) TYPE C.

  TYPES: BEGIN OF TY_SBDST_CONTENT.
          INCLUDE STRUCTURE BAPICONTEN.
  TYPES: END OF TY_SBDST_CONTENT.

  TYPES: SBDST_COMPONENTS LIKE BAPICOMPON OCCURS 1,
         SBDST_PROPERTIES LIKE BAPIPROPER OCCURS 1,
         SBDST_SIGNATURE LIKE BAPISIGNAT OCCURS 1,
         SBDST_CONTENT LIKE BAPICONTEN OCCURS 1.

  DATA: T_BDS_CONTENT TYPE STANDARD TABLE OF
        TY_SBDST_CONTENT,
        WA_BDS_COMPONENTS TYPE LINE OF SBDST_COMPONENTS,
        L_BDS_PROPERTIES  TYPE SBDST_PROPERTIES,
        L_BDS_COMPONENTS  TYPE SBDST_COMPONENTS,
        T_STXBITMAPS TYPE STANDARD TABLE OF STXBITMAPS
        WITH HEADER LINE,
        WA_BDS_SIGNATURE TYPE LINE OF SBDST_SIGNATURE,
        L_BDS_SIGNATURE TYPE SBDST_SIGNATURE,
        L_BDS_CONTENT TYPE SBDST_CONTENT,
        WA_STXBITMAPS TYPE STXBITMAPS,
        WA_BDS_PROPERTIES TYPE LINE OF SBDST_PROPERTIES.

  DATA: W_BYTECOUNT TYPE I,
        W_COLOR(1) TYPE C,
        W_WIDTH_TW TYPE STXBITMAPS-WIDTHTW,
        W_HEIGHT_TW TYPE STXBITMAPS-HEIGHTTW,
        W_WIDTH_PIX TYPE STXBITMAPS-WIDTHPIX,
        W_HEIGHT_PIX TYPE STXBITMAPS-HEIGHTPIX,
        W_RESOLUTION TYPE STXBITMAPS-RESOLUTION,
        W_BDS_BYTECOUNT TYPE I,
        W_TYPE TYPE STXBITMAPS-TDBTYPE,
        W_DOCID TYPE STXBITMAPS-DOCID.

  DATA: L_BDS_OBJECT TYPE REF TO CL_BDS_DOCUMENT_SET.

  CONSTANTS:
    C_TRUE  TYPE TY_BOOLEAN VALUE 'X',
    C_FALSE TYPE TY_BOOLEAN VALUE SPACE,
    C_BDS_MIMETYPE  TYPE BDS_MIMETP VALUE 'application/octet-stream',
    C_BDS_CLASSNAME TYPE SBDST_CLASSNAME VALUE 'DEVC_STXD_BITMAP',
    C_BDS_CLASSTYPE TYPE SBDST_CLASSTYPE VALUE 'OT'.

  IF BTYPE = 'BMON'.
    W_COLOR = C_FALSE.
  ELSE.
    W_COLOR = C_TRUE.
  ENDIF.

  CALL FUNCTION 'SAPSCRIPT_CONVERT_BITMAP_BDS'
    EXPORTING
      COLOR                    = W_COLOR
      FORMAT                   = 'BMP'
      BITMAP_BYTECOUNT         = W_BYTECOUNT
      compress_bitmap          = 'X'
    IMPORTING
      WIDTH_TW                 = W_WIDTH_TW
      HEIGHT_TW                = W_HEIGHT_TW
      WIDTH_PIX                = W_WIDTH_PIX
      HEIGHT_PIX               = W_HEIGHT_PIX
      DPI                      = W_RESOLUTION
      BDS_BYTECOUNT            = W_BDS_BYTECOUNT
    TABLES
      BITMAP_FILE              = T_BITMAP
      BITMAP_FILE_BDS          = T_BDS_CONTENT
    EXCEPTIONS
      FORMAT_NOT_SUPPORTED     = 1
      NO_BMP_FILE              = 2
      BMPERR_INVALID_FORMAT    = 3
      BMPERR_NO_COLORTABLE     = 4
      BMPERR_UNSUP_COMPRESSION = 5
      BMPERR_CORRUPT_RLE_DATA  = 6
      OTHERS                   = 7.

  CREATE OBJECT L_BDS_OBJECT.

  WA_BDS_COMPONENTS-DOC_COUNT  = '1'.
  WA_BDS_COMPONENTS-COMP_COUNT = '1'.
  WA_BDS_COMPONENTS-MIMETYPE   = C_BDS_MIMETYPE.
  WA_BDS_COMPONENTS-COMP_SIZE  = W_BYTECOUNT.
  APPEND WA_BDS_COMPONENTS TO L_BDS_COMPONENTS.

  W_TYPE = 'BSD'.

  SELECT SINGLE *
  INTO T_STXBITMAPS
  FROM STXBITMAPS WHERE TDOBJECT = OBJECT
                                  AND   TDNAME   = NAME
                                  AND   TDID     = ID
                                  AND   TDBTYPE  = BTYPE.
  IF SY-SUBRC = 0.
    READ TABLE T_STXBITMAPS INDEX 1.
    W_DOCID = T_STXBITMAPS-DOCID.
  ELSE.
    CLEAR W_DOCID.
  ENDIF.

  IF W_DOCID IS INITIAL.
    WA_BDS_SIGNATURE-DOC_COUNT = '1'.
    APPEND WA_BDS_SIGNATURE TO L_BDS_SIGNATURE.

    CALL METHOD L_BDS_OBJECT->CREATE_WITH_TABLE
      EXPORTING
        CLASSNAME  = C_BDS_CLASSNAME
        CLASSTYPE  = C_BDS_CLASSTYPE
        COMPONENTS = L_BDS_COMPONENTS
        CONTENT    = T_BDS_CONTENT
      CHANGING
        SIGNATURE  = L_BDS_SIGNATURE
      EXCEPTIONS
        OTHERS     = 1.

    READ TABLE L_BDS_SIGNATURE INDEX 1 INTO WA_BDS_SIGNATURE
    TRANSPORTING DOC_ID.
    IF SY-SUBRC = 0.
      W_DOCID = WA_BDS_SIGNATURE-DOC_ID.
    ENDIF.
  ELSE.
    CALL METHOD L_BDS_OBJECT->UPDATE_WITH_TABLE
      EXPORTING
        CLASSNAME     = C_BDS_CLASSNAME
        CLASSTYPE     = C_BDS_CLASSTYPE
        DOC_ID        = W_DOCID
        DOC_VER_NO    = '1'
        DOC_VAR_ID    = '1'
      CHANGING
        COMPONENTS    = L_BDS_COMPONENTS
        CONTENT       = T_BDS_CONTENT
      EXCEPTIONS
        NOTHING_FOUND = 1
        OTHERS        = 2.
  ENDIF.

  WA_STXBITMAPS-TDNAME     = NAME.
  WA_STXBITMAPS-TDOBJECT   = OBJECT.
  WA_STXBITMAPS-TDID       = ID.
  WA_STXBITMAPS-TDBTYPE    = BTYPE.
  WA_STXBITMAPS-DOCID      = W_DOCID.
  WA_STXBITMAPS-WIDTHPIX   = W_WIDTH_PIX.
  WA_STXBITMAPS-HEIGHTPIX  = W_HEIGHT_PIX.
  WA_STXBITMAPS-WIDTHTW    = W_WIDTH_TW.
  WA_STXBITMAPS-HEIGHTTW   = W_HEIGHT_TW.
  WA_STXBITMAPS-RESOLUTION = RESOLUTION.
  WA_STXBITMAPS-RESIDENT   = RESIDENT.
  WA_STXBITMAPS-AUTOHEIGHT = AUTOHEIGHT.
  WA_STXBITMAPS-BMCOMP     = 'X'.
  INSERT INTO STXBITMAPS VALUES WA_STXBITMAPS.

ENDFUNCTION.

Crear el programa ABAP para hacer que todo pase…


ZROOK
*&--------------------------------------------------------------------&*
*& Report  ZROOK                                                      &*
*&--------------------------------------------------------------------&*
*& Author: Alvaro "Blag" Tejada Galindo                               &*
*& Company: SAP Labs Palo Alto                                        &*
*& Date: November 11, 2013                                            &*
*& Modified on: November 18, 2013                                     &*
*& Reason: Finish the code -;)                                        &*
*&********************************************************************&*

REPORT  ZROOK NO STANDARD PAGE HEADING.

TYPES : BEGIN OF ty_bitmap,
        l(64) TYPE x,
        END OF ty_bitmap.

TYPES: BEGIN OF TY_GRAPHIC_TABLE,
       LINE(255) TYPE X,
       END OF TY_GRAPHIC_TABLE.

types: begin of ty_flights,
       carrid type sflight-carrid,
       seatsocc type sflights-seatsocc,
       end of ty_flights.

DATA: it_bitmap TYPE STANDARD TABLE OF ty_bitmap,
      i_po_data TYPE STANDARD TABLE OF string,
      i_bitmap TYPE STANDARD TABLE OF string,
      T_GRAPHIC_TABLE TYPE STANDARD TABLE OF TY_GRAPHIC_TABLE,
      t_flights type STANDARD TABLE OF ty_flights,
      bitmap type string,
      client TYPE REF TO if_http_client,
      container TYPE REF TO cl_gui_custom_container,
      lv_length   TYPE i,
      lv_content  TYPE xstring,
      lr_mime_rep TYPE REF TO if_mr_api,
      OUTXSTRING type xstring,
      URL(255) TYPE C,
      PICTURE TYPE REF TO CL_GUI_PICTURE,
      L_BYTECOUNT TYPE I,
      L_CONTENT TYPE STANDARD TABLE OF BAPICONTEN,
      GRAPHIC_SIZE TYPE I,
      bin TYPE xstring,
      conv TYPE REF TO cl_abap_conv_in_ce,
      response TYPE string,
      carrid type string,
      seats type string,
      seatsocc type string,
      param type string.

FIELD-SYMBOLS: <fs_bitmap> like line of i_bitmap,
               <fs_itbitmap> like line of it_bitmap,
               <fs_po_data> like line of i_po_data,
               <fs_flight> like line of t_flights.

START-OF-SELECTION.
  perform get_data.
  perform call_rook.
  perform create_graphic.
  perform show_graphic.

*&---------------------------------------------------------------------*
*&      Form  GET_DATA
*&---------------------------------------------------------------------*
FORM GET_DATA.

  select carrid sum( seatsocc ) into table t_flights
  from sflights
  group by carrid.

  loop at t_flights ASSIGNING <fs_flight>.
    seatsocc = <fs_flight>-seatsocc.
    CONDENSE seatsocc NO-GAPS.
    CONCATENATE carrid <fs_flight>-carrid into carrid SEPARATED BY ','.
    CONCATENATE seats seatsocc into seats SEPARATED BY ','.
  ENDLOOP.

  replace regex '\A,' in carrid with space.
  replace regex '\A,' in seats with space.

  CONCATENATE '/custom/summarize?carrid=''' carrid '''&seats=' seats into param.

ENDFORM.                    " GET_DATA

*&---------------------------------------------------------------------*
*&      Form  CALL_ROOK
*&---------------------------------------------------------------------*
FORM CALL_ROOK.

  CALL METHOD cl_http_client=>create
    EXPORTING
      host   = 'blagrook.herokuapp.com'
      scheme = 1
    IMPORTING
      client = client.

  client->request->set_method( if_http_request=>co_request_method_get ).
  cl_http_utility=>set_request_uri( request = client->request uri = param ).

  client->send( ).
  client->receive( ).

  bin = client->response->get_data( ).

  conv = cl_abap_conv_in_ce=>create( input = bin ).
  conv->read( IMPORTING data = response ).

  client->close( ).

ENDFORM.                    " CALL_ROOK

*&---------------------------------------------------------------------*
*&      Form  CREATE_GRAPHIC
*&---------------------------------------------------------------------*
FORM CREATE_GRAPHIC.

  bitmap = response.
  TRANSLATE bitmap TO UPPER CASE.

  CALL FUNCTION 'SOTR_SERV_STRING_TO_TABLE'
    EXPORTING
      TEXT        = bitmap
      LINE_LENGTH = 128
    TABLES
      TEXT_TAB    = i_bitmap.

  loop at i_bitmap ASSIGNING <fs_bitmap>.
    APPEND INITIAL LINE TO it_bitmap ASSIGNING <fs_itbitmap>.
    <fs_itbitmap>-l = <fs_bitmap>.
  ENDLOOP.

  CALL FUNCTION 'ZUPLOAD_GRAPHICS_TABLE'
    EXPORTING
      OBJECT   = 'GRAPHICS'
      NAME     = 'BLAG'
      BTYPE    = 'BMON'
      ID       = 'BMAP'
    TABLES
      T_BITMAP = it_bitmap.

ENDFORM.                    " CREATE_GRAPHIC

*&---------------------------------------------------------------------*
*&      Form  SHOW_GRAPHIC
*&---------------------------------------------------------------------*
FORM SHOW_GRAPHIC.

  CALL FUNCTION 'SAPSCRIPT_GET_GRAPHIC_BDS'
    EXPORTING
      I_OBJECT       = 'GRAPHICS'
      I_NAME         = 'BLAG'
      I_ID           = 'BMAP'
      I_BTYPE        = 'BMON'
    IMPORTING
      E_BYTECOUNT    = L_BYTECOUNT
    TABLES
      CONTENT        = L_CONTENT
    EXCEPTIONS
      NOT_FOUND      = 1
      BDS_GET_FAILED = 2
      BDS_NO_CONTENT = 3
      OTHERS         = 4.

  CALL FUNCTION 'SAPSCRIPT_CONVERT_BITMAP'
    EXPORTING
      OLD_FORMAT               = 'BDS'
      NEW_FORMAT               = 'BMP'
      BITMAP_FILE_BYTECOUNT_IN = L_BYTECOUNT
    IMPORTING
      BITMAP_FILE_BYTECOUNT    = GRAPHIC_SIZE
    TABLES
      BDS_BITMAP_FILE          = L_CONTENT
      BITMAP_FILE              = T_GRAPHIC_TABLE
    EXCEPTIONS
      OTHERS                   = 1.

  CALL FUNCTION 'DP_CREATE_URL'
    EXPORTING
      TYPE    = 'IMAGE'
      SUBTYPE = 'BMP'
    TABLES
      DATA    = T_GRAPHIC_TABLE
    CHANGING
      URL     = URL.

  CREATE OBJECT container
    EXPORTING
      container_name = ''
      repid          = 'SAPMSSY0'
      dynnr          = '0120'.

  container->set_top( -85 ).
  container->set_left( 120 ).
  container->set_width( 580 ).
  container->set_height( 580 ).

  CREATE OBJECT PICTURE
    EXPORTING
      PARENT = container.

  CALL METHOD PICTURE->LOAD_PICTURE_FROM_URL
    EXPORTING
      URL = URL.
  CALL METHOD PICTURE->SET_DISPLAY_MODE
    EXPORTING
      DISPLAY_MODE = PICTURE->DISPLAY_MODE_FIT_CENTER.

  WRITE ''.

ENDFORM.                    " SHOW_GRAPHIC

Cuando ejecutamos nuestro programa…llamamo a nuestra aplicación Rook en Heroku, la cual a recibir la suma de asientos para 3 líneas aéreas y luego generar un gráfico, leer su representación hexadecimal, enviar la información de vuelta a ABAP el cual lo creará en la transacción SE78 y luego lo leerá para mostrarlo en la pantalla…ahora…no van a tener la mejor resolución del mundo…y voy a asumir que es porque “Cairo” no soporta realmente archivos "bmp", o porque el gráfico ha sido transportado y modificado a lo largo del camino…así que…no se ve bonito…pero que diablos! Estamos llamando a R desde el ERP…que más pueden pedir? -:D


Ha sido muy divertido…así que, nos vemos el próximo año -;)

Saludos,

Blag.
Developer Experience.

No comments: