martes, 31 de diciembre de 2013

Getting Started with Meteor.js JavaScript Framework - Book Review

Since I got this book I got some interest in reading it...mainly because with so many JavaScript Frameworks...I was skeptic...but...Meteor just totally blown my mind...this is my review...



Two things comes to mind one when I think about this book...both Meteor.js and the book...totally rocks! Why? Simply because the book is really well explained...it's funny...yes...it's funny...and it comes with a real full blown application to build...I will show the screens later...

If you're a Windows user like me...follow this link Meteor on Windows. Otherwise...just use the regular link...

The book is not really long...as it's only 131 pages...but it covers everything you need to know to go out and build your first applications...really...after reading the book you just want to build stuff...and I already have a couple of things in mind -;)

The book introduce you to the following themes...
  • Templates
  • Data, Meteor Style
  • Security and Accounts
  • Packaging and Deploying
So...one cool thing about Meteor...is that it comes bundled with Minimongo...a minimized version of the document based MongoDB...which is course more than enough for simple applications...

Now...I guess that you don't believe me...you still think that Meteor is just another JavaScript Framework...well...let me tell you...it has become for sure...my favorite JavaScript Framework...sorry AngularJS....


This is the application that you build with the book...a simple "Lending" application...so you can keep track of your stuff...



Not impressed yet? Well...Meteor comes with a Login UI package...



So you don't have to deal with internal stuff...just create your user...and have fun -:)

The only problem I see right now with Meteor...is that there are not enough books...but hopefully that's going to change while the framework becomes more popular and widely used -;)

And of course...I would buy this book right away if I was you...

Greetings,

Blag.
Developer Empowerment and Culture.

jueves, 26 de diciembre de 2013

Mastering Web Application Development with AngularJS - Book Review

I just finished reading Mastering Web Application Development with AngularJS and I gotta say...I have some mixed feelings -:P


First things first...this book is huge...372 pages...and it covers a lot of things...but...there's always a downside...

The book talks about building an SCRUM application...and it provides a lot of snippets that are related to that application...however...even when you download the book's source code...the application is nowhere to be found...meaning that either you figure out yourself...or just end with a bunch of nice but not very useful snippets...

To be fair...this can be seen as either good or bad...good because it forces you to try it yourself and in the end...learn more...but also bad because without proper experience...there's no way you can build it and that leads to frustration...

Anyway...there are some nice and complete examples that really make you keep going...also...the book covers many interesting aspects like...

  • Model transformations with filters
  • Advanced forms creation
  • Forms validation
  • Navigation
  • Application Security
  • Internationalization
  • Custom build directives

It would say...if you really have AngularJS experience...then this book is for you...as it has some really nice tricks...otherwise...grab a more basic book...build applications...gain experience...and then read this one...otherwise...you will get lost more than once...

Let's take a look at some pictures...





What I like this book however is the fact that it introduces us to many "external" JavaScript libraries...without actually introducing us to them...which is funny...

  • Twitter's Bootstrap
  • Grunt.JS
  • Jasmine
  • Express
  • Passport
  • Restler
At least...by knowing about them...we can explore them and make use of them -;)

This said...I'm glad I read it...but...if I ever put myself on the situation where I actually need to build an AngularJS application...I would read it again...hopefully next time...I would be able to get more out of it...

Greetings,

Blag.
Developer Empowerment and Culture.

Cookoo Watch - Product Review

So...what do a geek like me get for Christmas?


A Cookoo watch of course! -:D

So...let's review it -;)


The watch comes in a nice Birdhouse like case...that can be actually used as a real Birdhouse...or at least...that's what they propose...nice I should say -:)


The watch is a little bit big but doesn't feel annoying when wearing it...after the first 5 minutes...you simply get used to it...also...but being able to link it with your phone...it couldn't be small...


One of the things that I like about the watch is that it allows you to control the phone camera...this by using the Cookoo Life software that it's of course...available for free...actually...I obviously took the picture by using the "Command button" on the watch -;)

Another cool thing...is that it allows you to control the music...one single click to Pause/Play...but longer to change the song and a bit long to actually stop it...

Regarding the battery life...well...the watch connects to your phone by using Bluetooth Smart...so it really doesn't drain too much battery...I made a small experiment today...

I charged my phone up to 100%...then leave home and walk to the office...which takes me around 40 minutes...I had the music on and was checking email, Facebook and Foursquare...when I hit the office...only 8% was drained...so we could say...around 1% every 5 minutes...something like 8 hours till the next recharge...of course...not taking into account "Phone calls"...that will drain your battery quickly...I gotta say...not bad at all...

Also...the watch itself comes with a regular watch battery...so there's no need to charge it...it's supposed to last for at least a year and it's very easy to replace...it's water resistant as well -;)

So...what else does this nice watch does?


As you can see...it can tell you if you have a missing call...if you have new emails...if you have a calendar appointment...if you have a Facebook message...and if you're forgetting your phone...

It's also supposed to allow you to do Facebook check-ins...but I haven't been able to make it work on the latest Cookoo Life update....as the current version is a new one designed specially for IOS 7...I guess they keep working on making it better...

One thing that caught my attention was this....


So...they will release an API to allow developers to create new way to connect the Phone with the watch...I already have some nice ideas but I prefer for now to wait for API to be released before I drop out something...

So far...I love this watch...haven't got any "disconnect" problems...and it's working as promised...can't wait to see how this evolves in the future -:)

Greetings,

Blag.
Developer Empowerment and Culture.

lunes, 23 de diciembre de 2013

Instant AngularJS Starter - Book Review

I have used AngularJS before when I wrote my blog AngularJS, PHP and SAP HANA. But of course...I only learnt what I need at that time...so I really wanted to learn a little bit more -;)

Instant AngularJS Starter is the my first "Instant" book...and I can say...I couldn't be happier with it -:)



Even when the book is fairly short...only 66 pages...it's just the kind of book I love...it comes with a quick introduction to AngularJS and what it's more important...the whole book guides you through the building of a complete application...so you can learn how to build it from the scratch and get a perfect base to build your own projects...

The author explains everything is a nice and easy way...so it's easy to follow and understand...let's see some images of the application you will get to build...




As you can see...it's just a simple application but in the end it uses all the AngularJS settings so you can quickly get use to them and learn them as well...

If you haven't use AngularJS before...then this book is totally for you...if you have some experience...then just go for something more advanced...this book is aimed for the total newbie...and...even when I wasn't a total newbie...I learnt a lot from it...

I got another AngularJS book that I just start reading...so stay tuned for more... -;)

Greetings,

Blag.
Developer Empowerment and Culture.

jueves, 19 de diciembre de 2013

Packt Publishing Book Bonanza!

My friends at Packt Publishing had release a cool campaign!


From December 19th, 2013 to January 3rd, 2014...all books and videos are just $5...without a limit!

Cool, huh? Just follow this link http://bit.ly/1jdCr2W and fill your library -;)

Greetings,

Blag.
Developer Empowerment and Culture.

miércoles, 18 de diciembre de 2013

PhoneGap 3 Beginner's Guide - Book review

This week...I read PhoneGap 3 Beginner's Guide which is a nice book, so here goes my review...


To begin with...in my opinion...this is not a beginner's book at all...it's a book that will walk you through the new and cool things implemented on PhoneGap 3...so if you haven't used PhoneGap before, I will recommend you to choose another book like PhoneGap 2.x Mobile Application Development Hotshot...

If you have previous PhoneGap experience...then this a good book for you...as it cover many cool things like:
  • App Templates
  • JavaScript compression
  • Web DB
  • Sensor
  • GeoLocation
  • Files
  • PhoneGap Plug-Ins.  
There are many examples but they are used to achieve certain things, like using the Camera or dealing with GeoLocation...so no end to end examples are given...but if you have previous PhoneGap experience...that shouldn't be so much of a problem...

One good thing about the book is that it takes care of showing how to achieve things on Mac OS, Linux and Windows, including alternatives when a certain tool can be used only on one of the Operating Systems...giving everyone the ability to try it out.

Let's look at some pictures...




The book also comes with exercises and pop up quizzes, so that's a good way to improve our knowledge and keep coding.

In a overall...I have to say this again...if you're a newbie...be aware that this book is not meant for you...if you have previous experience...then this book is good enough to put you on the right track and show you some PhoneGap goodies...

Greetings,

Blag.
Developer Empowerment and Culture. 


viernes, 13 de diciembre de 2013

Playing with schema flexibility on SAP HANA

The other day, a colleague from the Labs asked me an interesting question..."Let's say you have a table with schema flexibility...with a lot of fields that might be empty...how do you know which are fields that have at least one value?"...of course...I didn't knew how to answer that...so of course...I put myself to work...as expected -;)

First...let's create our table with a couple of values and some "missing" values...

Create table Products
DROP TABLE Products;

CREATE COLUMN TABLE Products(
PRODUCT_CODE VARCHAR(3),
PRODUCT_NAME NVARCHAR(20),
PRICE DECIMAL(5,2)
) WITH SCHEMA FLEXIBILITY;

INSERT INTO Products values ('001','Blag Stuff', 100.99);
INSERT INTO Products (PRODUCT_CODE,PRODUCT_NAME,PRICE,COLOR) 
values ('002','More Blag Stuff',100.99,'Black');
INSERT INTO Products (PRODUCT_CODE,PRODUCT_NAME,PRICE,COLOR,TYPES) 
values ('003','More Blag Stuff',100.99,null,null);

When we check the contents of the table we will have this...


As we can see...the last field has no values at all...so we will need to retrieve the names of the other fields...for this...we're going to create a table to hold them...then...we're going to create a Procedure to retrieve them...

Create table FIELDS_RECORDS
DROP TABLE Fields_Records;

CREATE COLUMN TABLE FIELDS_RECORDS(
FIELD_NAME VARCHAR(256)
);

For the Procedure...we're going to read all the fields that compose the table...then we're going to count all the values that are not null for each field...for this...we're going to use the help of a temporal table and dynamic SQL as sadly...SQLScript doesn't support scarlar binding yet...

GET_FIELDS_WITH_RECORDS
DROP PROCEDURE GET_FIELDS_WITH_RECORDS;

CREATE PROCEDURE GET_FIELDS_WITH_RECORDS(IN TableName VARCHAR(256))
LANGUAGE SQLSCRIPT AS
v_column_name VARCHAR(256);
v_select VARCHAR(256);
v_count INTEGER := 0;
CURSOR c_cursor(v_column_name VARCHAR(256)) FOR
SELECT COLUMN_NAME FROM SYS.CS_COLUMNS_ A inner join SYS.CS_TABLES_ B
on A.table_oid = B.table_oid where schema_name = 'SYSTEM'
and table_name = :TableName
and internal_column_id > 200
order by internal_column_id;
BEGIN
FOR cur_row AS c_cursor(v_column_name) DO
create local temporary table #temptable(found INTEGER);

v_select := 'INSERT INTO #temptable (SELECT COUNT(' || cur_row.column_name || ') 
             FROM ' || :TableName || ' WHERE ' || cur_row.column_name || ' IS NOT NULL)'; 
exec v_select;

SELECT found INTO v_count FROM #temptable;
IF v_count > 0 THEN
v_select := 'INSERT INTO FIELDS_RECORDS VALUES(''' || cur_row.column_name || ''')';
drop table #temptable;
exec v_select;
END IF;
END FOR;
END;

When we call our Procedure, we simply need to pass the name of the table...and then read it's content...

Call Procedure and get info
CALL GET_FIELDS_WITH_RECORDS('PRODUCTS');

SELECT * FROM FIELDS_RECORDS;

Let's see what do we have on FIELDS_RECORDS -;)


Just as expected -:) Nice, huh?

Greetings,

Blag.
Developer Empowerment and Culture.

miércoles, 11 de diciembre de 2013

Learning jQuery, Third Edition - Book Review

I started reading this book in order to learn JQuery...just because...

I didn't had high expectations but this book really blown me away...

Learning jQuery, Third Edition is just amazing...it's a really long book full with practical examples and things that you could use right away...


The book starts with the very basics and slowly moves into more complex examples and external JQuery plug-ins...after reading it...I just love JQuery -;)





With 13 chapters and 2 appendixes...this is a huge book that will have you both hook and entertained for a long time -:) 

This book should be on your book shelve for sure! JQuery can really boost up your old HTML pages -;)

Greetings,

Blag.
Developer Empowerment and Culture.

lunes, 2 de diciembre de 2013

Twitter Battle

I love to have fun with R and Twitter...there are a lot of cool things that you can do with it...so I just thought of having a small Twitter Battle application...something that will grab the number of followers and lists from two users...apply some crappy algorithms and determine the Twitter importance between those users...using our .RData file that holds the Twitter OAuth info...

Twitter Battle
require("Rook")
library("ROAuth")
library("twitteR")

setwd("C:/Blag/R_Scripts/Important_Scripts")
load("credentials.RData")

Get_Percentages<-function(p_one,p_two,flag){
      if(p_one > p_two){
        if(flag == 0){
          OneFollowPercent<-100
          TwoFollowPercent<-round((p_two * 100) / p_one)
        }else{
          TwoFollowPercent<-100 - (round((p_two * 100) / p_one))
          if(TwoFollowPercent <= 49){
            OneFollowPercent<-50 + TwoFollowPercent
            TwoFollowPercent<-100 - OneFollowPercent
            flag<-0
          }else{
            OneFollowPercent<-50 + TwoFollowPercent
            TwoFollowPercent<-100 - TwoFollowPercent
            TwoFollowPercent<-100 - round((round(TwoFollowPercent * 100) / OneFollowPercent))
            OneFollowPercent<-TwoFollowPercent
            TwoFollowPercent<-100 - OneFollowPercent
            flag<-1
          }
        }
      }
      if(p_one < p_two){
        if(flag == 0){
          OneFollowPercent<-round((p_one * 100) / p_two)
          TwoFollowPercent<-100
        }else{
          OneFollowPercent<-100 - (round((p_one * 100) / p_two))
          if(OneFollowPercent <= 49){
            TwoFollowPercent<-50 + OneFollowPercent
            OneFollowPercent<-100 - TwoFollowPercent
            flag<-0
          }else{
            TwoFollowPercent<-50 + OneFollowPercent
            OneFollowPercent<-100 - OneFollowPercent
            OneFollowPercent<-100 - round((round(OneFollowPercent * 100) / TwoFollowPercent))
            TwoFollowPercent<-OneFollowPercent
            OneFollowPercent<-100 - TwoFollowPercent
            flag<-1
          }
        }
      }
      if(p_one == p_two){
        OneFollowPercent<- 50
        TwoFollowPercent<- 50
      }
      percents<-c(OneFollowPercent,TwoFollowPercent,flag)
      return(percents)
}

newapp<-function(env){
  req<-Rook::Request$new(env)
  res<-Rook::Response$new()
  res$write('<form method="POST">\n')
  res$write('Enter your Twitter username: <input type="text" name="YourUserName">')
  res$write('</BR>')
  res$write('Enter your his/her Twitter username: <input type="text" name="HisHerUserName">')
  res$write('</BR>')
  res$write('<input type="submit" name="Start the Battle!">')
  res$write('</form>')
    
  if (!is.null(req$POST())) {
    YourUserName = paste("@",req$POST()[["YourUserName"]],sep="")
    HisHerUserName = paste("@",req$POST()[["HisHerUserName"]],sep="")
    
    reg<-registerTwitterOAuth(credentials)
    
    GetYourUser<-getUser(YourUserName,cainfo="cacert.pem")
    GetHisHerUser<-getUser(HisHerUserName,cainfo="cacert.pem")
    
    GetYourFollowers<-GetYourUser$followersCount
    GetYourLists<-GetYourUser$listedCount
    GetHisHerFollowers<-GetHisHerUser$followersCount
    GetHisHerLists<-GetHisHerUser$listedCount

    FollowPercents<-Get_Percentages(GetYourFollowers,GetHisHerFollowers,0)
    ListPercents<-Get_Percentages(GetYourLists,GetHisHerLists,0)

    YourPercents<-FollowPercents[1] + ListPercents[1]
    HisHerPercents<-FollowPercents[2] + ListPercents[2]

    FinalPercents<-Get_Percentages(YourPercents,HisHerPercents,1)

    YourPercents<-FinalPercents[1]
    HisHerPercents<-FinalPercents[2]
    if(FinalPercents[3] == 0){
      DiffPercent<-abs(YourPercents - 50)
    }else{
      DiffPercent<-FinalPercents[1]
    }
    
    if(YourPercents > HisHerPercents){
      message<-paste(req$POST()[["YourUserName"]],"is",DiffPercent,
                     " % more important on Twitter than",req$POST()[["HisHerUserName"]],sep=" ")
      res$write(paste('<H1>',message,'</H1>'))   
    }
    if(YourPercents < HisHerPercents){
      message<-paste(req$POST()[["YourUserName"]],"is",DiffPercent,
                     " % less important on Twitter than",req$POST()[["HisHerUserName"]],sep=" ")
      res$write(paste('<H1>',message,'</H1>'))
    }
    if(YourPercents == HisHerPercents){
      message<-paste(req$POST()[["YourUserName"]],
                     "is equally important on Twitter as",req$POST()[["HisHerUserName"]],sep=" ")
      res$write(paste('<H1>',message,'</H1>'))
    }
    
    pieValues<-c(YourPercents,HisHerPercents)
    pieNames<-c(paste(req$POST()[["YourUserName"]],YourPercents,"%"),
                      paste(req$POST()[["HisHerUserName"]],HisHerPercents,"%"))
    
    png("Twitter_Battle.png",width=1000,height=700)
    pie(pieValues,labels=pieNames,main="Twitter Battle")
    dev.off()
    res$write("<div align='center'>")
    res$write(paste("<img src='", server$full_url("pic"), "/", 
                    "Twitter_Battle.png'", "/>", sep = ""))
    
    res$write("</div>")
}
  res$finish()
}

server = Rhttpd$new()
server$add(app = newapp, name = "Twitter_Battle")
server$add(app = File$new("C:/Blag/R_Scripts/Important_Scripts"), name = "pic")
server$start()
server$browse("Twitter_Battle")

When we execute it...we're going to have some interesting results...




Have fun with it -;)

Greetings,

Blag.

Twitter unfollowers with R and Rook - Revisited

Some time ago I wrote a post called Twitter unfollowers with R and Rook where I used R and Twitter to get a list of the people that we follow...but that doesn't follow us back...

Right now...that post is obsolete as Twitter changed its API to API 1.1 which means that OAuth authentication must be used...

So...of course...we're going to use that for our new version of Twitter Unfollowers -;)

Getting Twitter Auth
library("ROAuth")
library("twitteR")

setwd("C:/Blag/R_Scripts/Important_Scripts")

options(RCurlOptions = list( capath = system.file("CurlSSL", "cacert.pem", package = "RCurl"), ssl.verifypeer = FALSE))

reqURL<-"https://api.twitter.com/oauth/request_token"

accessURL<-"http://api.twitter.com/oauth/access_token"

authURL<-"http://api.twitter.com/oauth/authorize"

consumerKey<-"Your own Consumer Key"

consumerSecret<-"Your own Consumer Secret"

download.file(url="http://curl.haxx.se/ca/cacert.pem", destfile="cacert.pem")

credentials<-OAuthFactory$new(consumerKey=consumerKey,
                              consumerSecret=consumerSecret,
                              requestURL=reqURL,
                              accessURL=accessURL,
                              authURL=authURL)

credentials$handshake(cainfo = system.file("CurlSSL", "cacert.pem", package = "RCurl"))

save(credentials, file="credentials.RData")

What are we doing here is simply get Authorization from Twitter and save that information as an RData file that we can use later on. This script is needed to be executed only once...and will ask you to enter a PIN number to validate the connection...


After that...we're ready to go -;)

Twitter Unfollowers
require("Rook")
library("ROAuth")
library("twitteR")

setwd("C:/Blag/R_Scripts/Important_Scripts")
load("credentials.RData")
registerTwitterOAuth(credentials)
Get_Screen_Name<-function(p_userid){
  SomeUser<-getUser(p_userid,cainfo="cacert.pem")
  SomeUser<-SomeUser$screenName
  return(SomeUser)
}

newapp<-function(env){
  req<-Rook::Request$new(env)
  res<-Rook::Response$new()
  res$write('<form method="POST">\n')
  res$write('Enter your Twitter username: <input type="text" name="UserName">\n')
  res$write('<input type="submit" name="Get Bad People!">')
  res$write('</form>')
  
  People_Id<-""
  Bad_People<-c()
  Bad_Names<-c()
  j<-0  
  
  if (!is.null(req$POST())) {
    UserName = req$POST()[["UserName"]]
    
    User<-userFactory$new(screenName=UserName)
    followers<-User$getFollowerIDs(n=NULL,cainfo="cacert.pem")
    following<-User$getFriendIDs(n=NULL,cainfo="cacert.pem")

    for(i in 1:length(following)) {
      Match<-following[i] %in% followers
      if(Match == TRUE){
      }
      else{
        Bad_Person<-Get_Screen_Name(following[i])
        res$write(paste(' ',Bad_Person,sep=' '))
        res$write('</BR>')
      }
    }
}
  res$finish()
}

server = Rhttpd$new()
server$add(app = newapp, name = "Twitter_Rook")
server$start()
server$browse("Twitter_Rook")

Thanks to this...I was able to shrink the code from 85 lines of the previous version to only 51 lines of code...nice achievement if you ask me -;)

So...when we execute the code...we will have this...


Of course...I don't expect them all to follow me back as they are mostly companies...but for people...follow me back or get unfollowed -:P

Greetings,

Blag.

Goodbye external developers...hello internal developers...

I joined SAP Labs almost two years ago...first in SAP Labs Montreal and now in SAP Labs Palo Alto. During that time, my team changed names from PEDA (Platform Evangelism and Developer Adoption) to TIDE (Technology Innovation and Developer Adoption) to DevEx (Developer Experience) and were focused on External Developers...which meant...bring developers to our SAP Ecosystem.

Now...we have changed our name again, and we are called Developer Empowerment and Culture. Not only our name has changed but also our focus, and now we're going to move away from External Developers and focus on Internal Developers instead...

Like I like to say..."We're changing SAP from the inside"...

What this means? For you my readers...not so much...I will continue to write blogs and get the most out of both SAP and External technologies...but for the SAP Ecosystem it will mean that I will focus on our own Developers...make them happy and more productive is our new goal...

While it has been awesome to work with External Developers and write blogs, organize events and spread the word about SAP Technologies...our new focus will also bring a lot of challenges and satisfactions and we will help SAP to become a better company and truly work..."Like never before"...

See you around -;)

Blag.
Developer Empowerment and Culture.

miércoles, 27 de noviembre de 2013

Web Application Development with R Using Shiny - Book review

As may already know (As I have said this a thousand times) I love R -:) And of course...I love Shiny -;)

Today I had the opportunity to read a nice little book called Web Application Development with R using Shiny.


This book starts of course with a small introduction to R, which is more than enough to get started as Shiny will have you coding in no time. Shiny makes it really easy to create web applications using R as its backend.

The explanation of Shiny and its components is pretty well detailed with easy to medium to hard examples...and even when I'm myself a seasoned Shiny developer...there were a lot of things for me to learn...which is always something cool -;)

For example...although I suspect about this...I never tried it and this book just confirm that we can indeed use custom HTML, JavaScript and even JQuery in interaction with Shiny...which for sure open a new way of possibility when developing web applications with R.

Well...let's see a couple of examples shown in the book, so you can get an idea of what Shiny can do...



For sure, a book covering a single package can't be too long...that's why I call this a "nice little book"...but in the end...I'm surprise that the author managed to actually make a book out of it...with examples and lots of code to give us a full overview of Shiny and more than a better starting point to make us start developing our own applications...

If you haven't used R before or you have use it but have never deal with Shiny...this book is totally for you...go ahead and buy it! It's even on sale -;)

Greetings,

Blag.

martes, 19 de noviembre de 2013

Calling R from the ERP - A dirty little hack

Since I joined SAP around 2 years ago, I simply stopped using ABAP…even when I use it for almost 11 years when I was a consultant…

A week ago, I was thinking about writing a new blog…something nice…some hacky…something that would allow me to just rest and don’t blog for the rest of the year…

I thought about ERP and R…while some things have been done already like Analytics with SAP and R from Piers Harding. I wanted to actually call R from the ERP instead of the ERP from R -;)

While thinking about this…I thought the best way to do it would be to reuse something that I before on my blog Consuming R from SAP Mobile Platform where I used Rook on Heroku to create a REST page that could be consumable…

So…for this particular blog…I wanted to something simple…call R, generate a graphic and show it on my ABAP program…here’s a small diagram to get things a little bit more clear…


Of course, when I tried this the first time…it failed…miserably…simply because the current implementation of Rook on Heroku doesn't have graphic capabilities on it…so it was impossible to actually generate a graphic…

Let’s get our hands dirty and let’s separate this blogs into steps so it become easier to handle -:)

Compile R with graphic capabilities on Heroku

This step was really a pain…it took me almost a week to get it done…and funny enough the rest of the steps took me about 6 hours…anyway…let’s keep going…

I started by looking at this two awesome projects on Github…rookonheroku and heroku-buildpack-r-build-r I took some chips from both and then create my own script and steps…

If you don’t have Heroku Tool Belt installed…go ahead and grab it…Log into Heroku and do the following…

Steps to install R on Heroku with graphic capabilities
mkdir myproject && cd myproject
mkdir bin
echo "puts 'OK'" > config.ru
echo "source 'http://rubygems.org'\n gem 'rack'" > Gemfile
#Open your project folder and modify the Gemfile in Notepad…replace the "\n" by an actual line break.
bundle install
git init . && git add . && git commit -m "Init"
heroku apps:create myproject --stack=cedar
git push heroku master
#Copy and paste the content of my installR.sh file into your project folder, 
#also copy the files features.h #and features-orig.h from 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

Here's the code for the installR.sh script...

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

With that…you will have a full R installation with graphic capabilities…of course…you will need to delete all the garbage generated…but we will get there later…

Now…we need to install Rook…but we need version 1.0-2 so we need to download it and install it from the source…

Download 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

Then launch R…simply call it by saying…

Calling R
R

As it will launch the R environment…

Installing Rook
install.packages(/Rook_1.0-2.tar.gz, repos = NULL, type="source")
#Now we can simply quit R
q()

As I was lazy to work more on the script to delete those files automatically…basically…the “bin” folder should look like this…


And the “vendor” folder like this…


Please remove the folders by using…

Removing folders
rm –rf name_of_folder

Once we’re done…we need to pack our stuff and sent it somewhere else, as Heroku is read only…once we log off…we lost everything…

Backup your work
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
 
#For scp you actually need a server…and I had my R on AWS…so I did used that…it might look like this…
scp -i XXX_X.pem bin.tar.gz root@XX.XX.XXX.XXX:/Blag/bin.tar.gz

Once we have everything saved…we can close the Heroku session and keep going…we can use WinSCP to copy our packed folders from our AWS server into our local project folder…uncompressed them and copy the following files from rookonheroku…
  • config.ru
  • demo.R
  • Procfile
Then…you can simply send everything back to Heroku…

Last push on Heroku
git add . && git commit -am "message" && git push heroku master
heroku ps:scale web=1

Of course…this is a lot of work…and I mean it…lot

So…I have upload the final compiled version to GitHub -;) To get it…simply do this…

Getting Blagrook
git clone git:// github.com/atejada/BlagRook.git yourproject 
#(This will clone my Github and create a folder called yourproject to store the codes)
heroku create myapp #(Create an application for the Rook script)
git push heroku master #(This allows to pass everything from my yourproject folder to your Heroku account)

Create the code for the Rook application

On last thing we need to do…is to create the Rook application that it’s going to work on our Rook server…

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)
}

In a few words what is happening here is simple...we receive the parameters "carrid" and "seats"...both came as a string with "," so we use strsplit to separate them and then create a factor with the values. In the case of "seats" we need it to be numeric so we use the as.numeric function. Finally we create a data.frame combining "carrid" and "seats". Using "Cairo" (That's why we needed graphic capabilities on R) we create a Bitmap file which is going to be the result of creating a dotchart. Once it has been created, we read it, using the readBin function we get it's Hexadecimal representation. Using the function paste and its collapse method, we put everything on a big string and send it back...easy as pie -;)

I’m sure that you will wonder how I came out with the number 231488 on the readBin line…well…I download a nice Hexadecimal Editor called HxD then I create the graphic on my local R and load it…the I went to the last line and took the Hex number…


Using the calculator I simply changed the Hex value 38830 to decimal, multiply it by 16 and then add 16 more as the first line doesn’t count…simply and easy -;)

Create the function module ZUPLOAD_GRAPHICS_TABLE

This function module will allow us to upload the generated graphic into 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.

Create the ABAP program to make it all happen…


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

When we run our program…we will call our Rook application on Heroku, which will receive the sum of seats for three carriers and then create a graphic, read it’s hexadecimal representation, give back to ABAP which will in turn create it on SE78 and then read it to show it on screen…now…you will not get the best resolution ever…and will assume it’s because “Cairo” doesn’t really support bmp files, or because the graphic is getting transported and changed all the way…so…it doesn't look good…but heck! We’re calling R from the ERP…what else do you want? -:D


It was really fun…so see you next year -;)

Greetings,

Blag.
Developer Experience.

miércoles, 16 de octubre de 2013

Columbus Day on Packt Publishing!

Get a 50% discount on Packt Publishing books



Greetings,

Blag.
Developer Experience.

jueves, 3 de octubre de 2013

SAP HANA - Let's talk about Rows and Columns

As we all know...SAP HANA can handle table creation in two ways...Rows and Columns. Row tables are suppose to be better for tables that get updated very often and Column tables for tables that doesn't get updated very often...Column Tables are supposed to be faster when it comes to read their contents.

As I have never actually test this...I create two Python scripts to generate 1 million records for two tables...DOC_HEADER and DOC_DETAILS. These two tables share the DOCUMENT_ID, YEAR and AREA but as the information get created randomly, chances that both tables have some values in common are not really known...

For the sake of the example I create both tables DOC_HEADER and DOC_DETAILS using both Rows and Column types and build another Python script to measure the reading time...

Column_Row_SAPHANA_Test.py
import dbapi
import time

conn = dbapi.connect('*****', 30015, 'SYSTEM', '****')
cur = conn.cursor()

query = '''SELECT H."DOCUMENT_ID", H."YEAR", H."AREA", SUM("AMOUNT") AS AMOUNT
           FROM "BLAG"."DOC_HEADER_ROW" AS H INNER JOIN "BLAG"."DOC_DETAIL_ROW" AS D
           ON H."DOCUMENT_ID" = D."DOCUMENT_ID"
           AND H."YEAR" = D."YEAR"
           AND H."AREA" = D."AREA"
           GROUP BY H."DOCUMENT_ID", H."YEAR", H."AREA"'''

start = time.clock()
cur.execute(query)
ret = cur.fetchall()
end = time.clock()

counter = 0

for row in ret:
    counter += 1
time_taken = end - start
print("Row Store - %s records in %s seconds") %(counter, time_taken)

query = '''SELECT H."DOCUMENT_ID", H."YEAR", H."AREA", SUM("AMOUNT") AS AMOUNT
           FROM "BLAG"."DOC_HEADER_COLUMN" AS H INNER JOIN "BLAG"."DOC_DETAIL_COLUMN" AS D
           ON H."DOCUMENT_ID" = D."DOCUMENT_ID"
           AND H."YEAR" = D."YEAR"
           AND H."AREA" = D."AREA"
           GROUP BY H."DOCUMENT_ID", H."YEAR", H."AREA"'''

start = time.clock()
cur.execute(query)
ret = cur.fetchall()
end = time.clock()

counter = 0

for row in ret:
    counter += 1
time_taken = end - start
print("Column Store - %s records in %s seconds") %(counter, time_taken)

Both queries do the exact same thing...they do an INNER JOIN between the two tables...using ROW store for the first one and COLUMN store for the second one...

Let's run this 3 times and see what happens -;)

Benchmark
Row Store - 7669 records in 2.45972177882 seconds
Column Store - 7669 records in 1.59409638594 seconds

Row Store - 7669 records in 2.32650395252 seconds
Column Store - 7669 records in 1.68382533783 seconds

Row Store - 7669 records in 2.44930342075 seconds
Column Store - 7669 records in 1.52396673192 seconds

As we can see...we have a constant gain of about 38% faster when using Column store...

Greetings,

Blag.
Developer Experience.

martes, 17 de septiembre de 2013

Software Development on the SAP HANA Platform - Book review

Some time ago I did a technical review for SAP HANA Starter from Mark Walker.

This time I had the pleasure to read Software Development on the SAP HANA Platform a really nice book for beginners on SAP HANA.


The book is really good and full of pictures, examples and clear explanation that will get from an SAP HANA noob to a well versed SAP HANA enthusiast.

Mark cover everything from a single but powerful comparison between SAP HANA and MySQL to XS Development passing of course between the creation of Attribute Views and data provisioning on SAP HANA.

The full book is around 303 pages so you will get what you pay for...a comprehensive guide and tour through the amazing wold of SAP HANA.

So...if you still haven't get your hands dirty with these awesome SAP In-Memory technology, don't waste more time and get the book, you don't be disappointed.

And truth to be said...with the rapid and mass adoption of SAP HANA, many people just want to jump on the wagon and make some money by selling books without real content...Mark Walker does an awesome job of delivering a great learning experience...just in the way I would do it myself...clear examples full of images so you can know exactly what to expect and know exactly what to do next.

Greetings,

Blag.
Developer Experience.

viernes, 6 de septiembre de 2013

Running R inside SAP HANA Studio

*DANGER* This is just a silly blog and shouldn't be taken any seriously.*DANGER*

Ok...you may wonder...why is Blag writing a silly blog? Well...I'm a Developer Evangelist...and it's my job to keep developers entertained  Also...I have so free time to spare

As you all may know...SAP HANA Studio runs on top of Eclipse...which means that if we can run R on Eclipse...then we can obviously run R on SAP HANA Studio...that's exactly what we're going to do

As easy as it gets...follows this instructions...


  • Go to Help --> Install New Software
  • Put the following repository

  • Choose Walware - StatET



  • Once installed, your SAP HANA Studio will restart and you will need to configure R.
  • Go to Windows --> Preferences.
  • StatET --> Run/Debug --> R Environments --> Add (In my picture, I'm using Edit as I already add it).



  • Either on the Command Line or in RStudio, install the following...
  • install.packages(c("rj", "rj.gd"), repos="http://download.walware.de/rj-1.1")
  • Also install RJava as well...install.packages("RJava")

 Now...you need to start the R console...so...
  • Go to the little Green Arrow button and press "R_Console".


Once that it's done, we can go ahead and create our R script...
  • Go to File --> New Project --> R Project.
  • On the created R Project, right click and select New --> R Script File.



Copy this code inside your new file...

R_from_SAP_HANA.sql
result<-read.table('clipboard',header=TRUE,sep=';')
result<-subset(result, !duplicated(DISTANCE))
flights<-data.frame(DISTANCE=as.numeric(result$DISTANCE))
row.names(flights)<-result$FLIGHT
d<-dist(as.matrix(flights))
hc<-hclust(d)
plot(hc)

Now...go to the "Modeler" perspective...open a SQL Console and write the following...

SAP_HANA_Select.sql
SELECT DISTINCT CITYFROM || '/' || CITYTO "FLIGHT", DISTANCE
FROM SFLIGHT.SPFLI
WHERE DISTANCE > 0
  AND CARRID = 'SQ'

Run it...and here's come the funny part...go to the Result tab and select all records...then press Copy Rows, so the selection gets copied into the Clipboard.


Go back to the StatET perspective and click the "Run file in R via command" button...



When you run the code...you're not going to see any response, unless there's an error...so just go to the R Graphics tab which is at the bottom where the Console window seats...expand it...and you will see a Cluster Dendogram graphic generated -;)


I told you...this was a very silly post...but at least...we never left the SAP HANA Studio -:P

See you next time!

Blag.
Developer Experience.