Posts Categorized: Design

pastedImage_15

Node.js dashboard for SAP HANA

This is what we will be developing in this blog …

Over the past few years Node.js has really caught my attention. The simplicity of Javascript with server side processing, Non-blocking-IO, Event Driven, and simple integration always intrigued me as a great combination for enterprise applications. (Somehow it sounds similar to HANA XS Engine)
A couple months ago I ran into a similar problem to Jon-Paul Boyd (HANA Forum Post) in which I wanted to use XS Engine for websocket/persistant connections to my HANA Instance, but due to the support not being included in SPS6, I decided to look elsewhere, and ended up using Node.js to fulfill this requirement.In the past, while developing HANA/Node apps, I resorted to creating a XSJS App which really just acted as a middleware layer to push and pull data from my HANA DB, until recently I noticed a great blog post from Ingo Sauerzapf which piqued my interest. The blog mentioned that Holger Koser had created a HANA DB Client for Node making life extremely easy to connect to HANA directly from Node. I thought it would be good share the small project I developed using Node.js and this new client with the community in the hopes that others will share their experiences with the technology.This blog is not necessarily an introduction to Node.js development as there are some nice tutorials and examples out there from Tobias Hoffman and Alessandro Spadoni. The blog is intended to cover a small app developed in Node.js and shows the development process behind it, taking it from conception through to reality. I encourage you to download a few of these components, and also the example out. This app, similar to another app I developed called Metric² (which you can read about here), it is a web based widget showing some friendly KPI’s on the performance of your HANA Instance. The app gets streaming data from our HANA backend displaying this in a friendly, simple dashboard. The dashboard is designed to be shown on a large format monitor in a Ops or IT center and can also very easily be modified to show any KPI’s relevant to your business or needs.

Requirements:

SAP HANA Instance (e.g. AWS Developer Image)

Node.js installed (this does not need to be on the HANA box but same network with access to the HANA port – normally 30015).

Node Dependencies

We will also use a couple of helpful dependencies from the node community including Socket.io, Express and obviously hdb. Installing these packages is as simple as running “npm install hdb”. Once you have the dependencies installed we can start creating our app.

https://nodei.co/npm/hdb.png?compact=true

App Design

For me, I always start mocking up in my favorite Image IDE (Photoshop), I used this image as inspiration for my app. I liked the simplicity, clean layout with focus on the numbers at the bottom. In our case, we will add a small chart in the center, giving us a basic visual representation of the numbers being displayed:

 

Photoshop HTML Mockup
Download the PSD

App Development

Index.html

In this case I decided to use Twitter Bootstrap to help with some of the layout/formatting of the page as well as some mundane tasks like Modal popups. From a coding perspective I started out developing the Single paged “index.html” file, doing my best to stick with my mockup which I previously created. I was sure to “id” all of my elements on this page as we will be updating these values from our node.js backend. This aspect of node development is strictly “traditional” web based development. No need to work with Node or any server for that matter. Once you have your page rendering the way you want, we can move on.

<html>

<head>

<meta http-equiv="X-UA-Compatible" content="IE=edge" />

<meta charset="UTF-8"/>

<meta name="viewport" content="width=device-width, maximum-scale=1.0" />

<title>Metric&#178;</title>

<!-- jQuery -->

<script src="https://code.jquery.com/jquery.js"></script>

<!-- Socket.IO -->

<script src='/socket.io/socket.io.js'></script>

<!-- Peity - a lightweight sparkline package for jQuery -->

<script src='js/jquery.peity.min.js'></script>

<!-- Client side code needed -->

<script src='js/client.js'></script>

<!-- Bootstrap CSS -->

<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">

<!-- Latest compiled and minified JavaScript -->

<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>

<!-- CSS -->

<link rel="stylesheet" href="css/style.css">

</head>

<body>

<div id="top">

<div>

<div>

<div>

<table>

<tr>

<td rowspan="2" style="text-align: center; width: 10%;" >

<img id="statusicon" src="img/OKIcon.png"/>

</td>

<td style="vertical-align: top;">

<h1><span id="info-name">SAP HANA Instance</span>

<button data-toggle="modal" data-target="#myModal">

<span></span>

</button>

</h1>

</td>

</tr>

<tr>

<td style="padding-top: 20px;">

<span style="margin-left: 0px;" /></span><span id="info-alerts">0</span> Alerts

<span></span><span id="info-version">1.0</span>

<span></span><span id="info-detail">Server Location</span>

</td>

</tr>

</table>

</div>

<div>

<span>0</span>

</div>

<div>

<table>

<tr>

<td id="infoUSERS" onClick="setChart('USERS');">

<!-- The ID of each of our <SPAN> tags is important for updating the data being returned from the server -->

<span id="info-users">0</span><br />

<span>Users</span>

</td>

<td> </td>

<td id="infoDISK" onClick="setChart('DISK');">

<span id="info-disk">0</span> <sup>GB</sup><br />

<span>Free Disk</span>

</td>

<td id="infoMEM" onClick="setChart('MEM');">

<span id="info-mem">0</span> <sup>GB</sup><br />

<span>Free Memory</span>

</td>

<td id="infoCPU" onClick="setChart('CPU');">

<span id="info-cpu">0</span> <sup>%</sup><br />

<span>CPU</span>

</td>

</tr>

</table>

</div> <!-- /.containerfooter -->

</div> <!-- /.container -->

</div> <!-- /.centercontainer -->

</div> <!-- /.top -->

<!-- Modal -->

<div id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">

<div>

<div>

<div>

<button type="button" data-dismiss="modal" aria-hidden="true">&times;</button>

<h4 id="myModalLabel">Settings</h4>

</div>

<div>

<form id="modalbox" role="form">

<div>

<label for="servername">Name</label>

<input type="text" id="servername" placeholder="Enter a reference server name">

</div>

<div>

<label for="serverdetail">Location</label>

<input type="text" id="serverdetail" placeholder="Description, Location or Other Information">

</div>

<div>

<label for="bg">Background</label><br />

<label>

<input type="radio" name="bg" value="../img/bg1.jpg" checked> Background 1

</label>

<label>

<input type="radio" name="bg" value="../img/bg2.jpg"> Background 2

</label>

<label>

<input type="radio" name="bg" value="../img/bg3.jpg"> Background 3

</label>

<label>

<input type="radio" name="bg" value="none;"> None

</label>

</div>

<div>

<label for="colorscheme">Color Scheme</label><br />

<label>

<input type="radio" name="colorscheme" value="Dark" checked> Dark

</label>

<label>

<input type="radio" name="colorscheme" value="Light"> Light

</label>

<label>

<input type="radio" name="colorscheme" value="Fiori"> Fiori

</label>

</div>

<div>

<button type="button" data-dismiss="modal">Close</button>

<button type="button" id="modalSave" onClick="saveSettings();">Save changes</button>

</div>

</div><!-- /.modal-content -->

</div><!-- /.modal-dialog -->

</div><!-- /.modal -->

</body>

</html>

App.js

Next we develop the app.js file which is the brains of our operation. This file is firstly going to act as our web server for our web site, and secondly provide the data from our HANA server to the web page, pushing the data via web sockets.

Below is the app.js code, here you can see how we process each request based on the type and subsequently respond with the requested data. You can also see how simple it is to call the HANA DB and respond with the results.

 

var express = require('express'),

http = require('http'),

hdb = require('hdb');

try {

var app = express();

var server = http.createServer(app);

server.listen(3000);

var io = require('socket.io').listen(server);

app.use(express.static(__dirname + '/'));

// development only

if ('development' == app.get('env')) {

app.use(express.errorHandler());

}

var client = hdb.createClient({

host     : 'Your HANA IP Address or DNS Name',

port     : 30015,

user     : 'username',

password : 'password'

});

client.connect(function (err) {

if (err) {

console.error('Connect Error:', err);

} else {

console.log('Connected to server');

}

});

process.on('uncaughtException', function (err) {

console.log('Caught exception: ' + err);

});

strContent = '';

io.sockets.on('connection', function (socket) {

socket.on('request', function (data) {

// Handle Service Requests

switch (data.service) {

case 'CPU':

client.exec("SELECT ABS(SUM(PROCESS_CPU)) as CPU from SYS.M_SERVICE_STATISTICS", function(err, rows) {

if (err) {

console.error('Error:', err);

} else {

socket.emit('response', {service: 'CPU', response: rows[0].CPU});

}

});

break;

case 'MEM':

client.exec("select TO_VARCHAR(ROUND((FREE_PHYSICAL_MEMORY) /1024/1024/1024, 2)) AS FREEMEM

from PUBLIC.M_HOST_RESOURCE_UTILIZATION", function(err, rows) {

if (err) {

console.error('Error:', err);

} else {

socket.emit('response', {service: data.service, response: rows[0].FREEMEM});

}

});

break;

case 'INFO':

client.exec("SELECT VALUE FROM SYS.M_SYSTEM_OVERVIEW WHERE NAME = 'Version'", function(err, rows) {

if (err) {

console.error('Error:', err);

} else {

socket.emit('response', {service: data.service, response: rows[0].VALUE});

}

});

break;

case 'DISK':

client.exec("select TO_VARCHAR((ROUND(d.total_size/1024/1024/1024, 2) - ROUND(d.used_size/1024/1024/1024,2))) as FREESPACE

from ( ( m_volumes as v1 join M_VOLUME_SIZES as v2 on v1.volume_id = v2.volume_id ) right outer join m_disks as d on d.disk_id = v2.disk_id )

where d.usage_type = 'DATA' group by v1.host, d.usage_type, d.total_size,    d.device_id, d.path, d.used_size", function(err, rows) {

if (err) {

console.error('Error:', err);

} else {

socket.emit('response', {service: data.service, response: rows[0].FREESPACE});

}

});

break;

case 'USERS':

client.exec("SELECT COUNT(CONNECTION_ID) as STATUS FROM SYS.M_CONNECTIONS

WHERE CONNECTION_STATUS = 'RUNNING'", function(err, rows) {

if (err) {

console.error('Error:', err);

} else {

socket.emit('response', {service: data.service, response: rows[0].STATUS});

}

});

break;

case 'ALERTS':

client.exec("SELECT COUNT(ALERT_DETAILS) as ALERTCOUNT FROM _SYS_STATISTICS.STATISTICS_CURRENT_ALERTS", function(err, rows) {

if (err) {

console.error('Error:', err);

} else {

socket.emit('response', {service: data.service, response: rows[0].ALERTCOUNT});

}

});

break;

}

});

});

} catch(err) {

console.log(err);

}

 

The App does have a couple of different themes which will hopefully make it fit with your office decor

Below you can see a couple of images of the app running and showing the output. You can obviously very easily modify the code to show anything relevant to your business case as well. The Node-hdb package for node.js really makes developing HANA connected Node apps a breeze! Thanks Holger!

Fiori Styled
Light Theme
Image5.png
Dark Theme
Image6.png
As usual – please feel free to comment on your experience with Node.js and if you feel like this type of technology is a good fit in the enterprise? Do you have any suggestions on what I could have done differently?You can download the app here: Metric² for Node
Credits: The Bokeh backgrounds are from devientArt

Image1

Metric² for SAP HANA

Metric² is a web based, realtime dashboarding platform for SAP HANA, on SAP HANA.I recently gave a demo of the app at Demojam in Las Vegas (You can read my blog post about the event here). Metric² is a free app/download and this blog gives some insight into how it works, and how you can download and install it in your own HANA system:

Overview

Metric² is made up of 3 key areas:

Dashboards: Metric² can have multiple dashboards. Dashboards are designed as blank canvases, are quite flexible, and can contain widgets which are added can be simply dragged and dropped into their needed locations and also sized accordingly.

Widgets: Dashboards can have multiple widgets displayed. There are a variety of widgets including a range of predefined datasources (CPU, Memory, Disk etc.) but also include custom widgets (SQL, JSON, Yahoo) which can display a myriad of information to your team.

Alerts: Certain widgets can have alerts “attached” to them, when the value meets the conditions, the alert is logged and displayed.

  1. Why realtime? The intent is to be proactive about your systems or applications health, having the Metric² dashboards up on a large format monitor ensures that everyone is well aware of how your system is performing, and more importantly, when it is not.
  2. Why Web Based? While I personally prefer Native/Traditional/Window types apps, having Metric² run as a web app (using SAPUI5) means it can be used and displayed on a variety of devices and formats.
  3. Why do you call it a platform? Its more than just a bunch of metrics, everything is customizable, you can create your own widgets, your own dashboards and alerts, it also acts as a large data warehouse for any of the data you are monitoring/tracking, taking it past being an app, to a small platform which you can build, and extend upon.
Image1.png

Architecture:

This was an interesting dilemma for me, I wanted to keep the app as simple as possible by not requiring that admins install *any* additional components (thus deemed as a “Native” XS HANA app). It has a DB schema which Metric² needs to have present, and subsequently a set of files which get installed in the content repository via a package.

This lead to an interesting challenge, because the XS Engine did not support web sockets for “true” realtime app runtime, the app uses timers and HTTP polling to fetch the data from the backend system (read xsjs file). This is less than perfect. I have high hopes that SPS7 will include WebSocket support and we can start writing true realtime web apps directly on XS Engine. Another alternative is the recent availability of Node.js library for HANA, while its awesome, still requires additional components outside of the core HANA platform.

Image2.png

Design:

The app is based on a “Fiori” themed SAP UI5 foundation. I also used a variety of JQuery add-on’s to help (read: not reinvent the wheel) with the display of  the data. I also used the standard D3 visualization library of UI5 along with a helper class called NVD3 which is very useful in simplifying the D3 charting experience.

Image3.pngWhats Missing:

- Currently the app does not support multiple users.

- Browser support … currently its developed for Firefox only, but does not look too bad on Safari or Chrome (CSS Changes needed).

- The polling aspect, because the data is being fetched from your XSJS file and returned to the main page, I am constantly having to eval the returned code to update the chart widgets. Eval is Evil

- Widget history: Because widgets are being polled, if the screen is not open, it will not capture any historical data. Not very useful for troubleshooting.

Whats Next:

I am currently working on porting the app to support the realtime abilities. Planning on a new theme (not so much Fiori like) to make it a little more differentiated. Also planning on adding a few more predefined widgets (suggestions welcomed!!!).

Download:

Here

Install Guide:

  1. Import the Metric² Package to the content repository in XS Engine (This can be done via Web or Studio)
    1. Open http://YOURIP:8000/sap/hana/xs/ide/
    2. Click on the Delivery Unit Menu and then Import Delivery Unit From File System
    3. Select the downloaded/unzipped file (REPO_20131109-203950826-HDB–METRIC2.tgz)
  2. Run the Install script (InstallScript.sql) for the DB which is included in the download – This can be done in either HANA Studio or the Web IDE
  3. Optional: Run the Overview dashboard script (Overview Dashboard.sql) from here to give you a sample dashboard  – This can be done in either HANA Studio or the IDE
  4. Open your browser to http://YOURIP:8000/lilabs/metric2/Index.html

- Optional: Predictive Analytics: You may need to enable PAL on your server in order to use the libraries and forecasting feature under some of the widgets.

Known Issues:

- The ping response widget is not functional currently

- The Total CPU reported on the “System overview” widget should be converted to a legitimate percentage (0 – 100%) as SLES displays a summed percentage of all the CPU’s.

- The Map of the US Widget – coordinates are not accurate, they need an offset due to the images size and padding within the widget

- The dashboard itself is not fully responsive, when displaying on an iPad the view is often too small to display all the widgets

- There is no mail server functionality built into XS Engine, so alerts are only displayed on the monitor and in the notifications area on the right of the screen.

You can submit an issue log on my website making it easier to track, share, resolve: http://blog.li-labs.com/issue-log/

Disclaimer:

Vishal Sikka made a humorous reference at the Las Vegas Keynote to a certain “Pre-release” Beta product, so I am going to do the same This version of Metric² is a “Prerelease” beta please use and test it thoroughly in your development environment.

Stay up to date:

Please head over to http://www.metric2.com and signup for the newsletter, this way I can stay in touch with people interested in learning more.

License:

This software is provided as is, and with no warranty. You may copy, modify and share the code for personal purposes. You may not sell or include, any parts, or as a whole, the software. Please share the download via the URL provided – Thanks.

Favor:

If you install Metric² and build an interesting dashboard, please tweet or share it in the comments. Also if you find any errors or issues on the installation process, please let me know and I will gladly update this guide. Thanks


pastedImage_24

Why Demo Jam ROCKS

It was Tuesday night sometime back in 2009 at my first TechEd where I was sitting in the audience and really wishing I was standing up on stage, presenting something inspiring and innovative which would encourage the votes of the demanding audience of Demojam. Well, it took 4 years to creep over personal hurdles, family time and to gain an ounce of courage which persuaded me to post an entry in 2013, this blog gives some insight into what I did, what I would have done differently, and also why Demojam really needs people like YOU!A couple of months ago (May through July) I went through the openSAP HANA course and was really impressed, not only by the DB, but more the HANA XS Engine as an Web/App server. Being a “learn by doing” kinda person I struggled to get the most out of the course content since it really didn’t apply to anything I was currently working on, until I realized the opportunity… As I described in my demojam presentation, I drank the HANA coolaid Personally, it was not so much of the big data aspect that intrigued me, but rather this concept of the DB and the app server really being a single entity from a platform, as well as infrastructure perspective. Simple. I spent a couple weeks learning a lot more, since I had something to apply it against and started developing an app called Metric²(pronounced squared).Image.png“I realized that while there are a lot of applications which are using HANA, there were very few for HANA.”My goal with Metric² was to build a web based realtime platform for showing how your HANA instance was performing, and subsequently, how your business was actually benefiting from having this type system and helping your business run better. The concept was to try and correlate performance with user impact.

OK , get to the demo jam part …

Around the end of August, Metric² was doing what I had intended and I decided to submit the app for Demojam, although, my original anticipation was to build something far more purpose built and “crowd” pleasing. However, I was really hoping that the free perspective, something useful – realtime dashboards – and a good use case – audience involvement – would hopefully win the votes and hearts of a couple techies in crowd. I created a submission video and a couple days after submissions closed, I got a welcoming, but equally nerve wracking acceptance email. Graham Robinson was actually the one who sparked the idea of a Demojam submission with his comment on a small Mac app I developed for HANA called xSync. I both cursed and thanked him for about 2 months straight when I was wondering what I had gotten myself into

After getting accepted, I had a couple interesting challenges, my 1st priority and goal was to deliver the app at Teched, which meant between the acceptance email (around the 20th of September) and October 20th (TechEd) I needed a fully function product. I also needed to put together, and have a solid presentation nailed down. I subsequently neglected the last point and as always, focused more on adding features and functionality to the app. I had conceptual ideas about what I was going to present, and set out to do something a little different by getting the audience involved by using the actual system I built to show its purpose and functionality, but also to make them feel a part of the demo.

Shortly after being accepted you also have the opportunity to have a online session with Tobias Queck and Sebastian Steinhauer, 2 really great guys and coaches who listen to your demojam pitch and provide some really solid feedback on what their thoughts and suggestions are. After speaking with them, it really calmed my nerves and gave me a certain reassurance that I was going to be OK. A great and well needed confidence boost, especially for a first timer.

Well the 30 days between the above mentioned dates just flew by, and before I knew it, it was TechEd time. The first two days of TechEd for me, included the awesome Innojam event – I thoroughly enjoyed myself, but ended up spending considerably more time helping out than I anticipated and did not get as much coding done as possible. Yes, I was still writing code 2 days before the presentation. Adding features, optimizing things and getting the website finished. Innojam wrapped up on Monday evening, I skipped the keynote to do additional coding and practicing my pitch, the Tuesday of the event unfortunately came pretty quickly and my nerves were starting to get the better of me. I was super stressed to go up on stage, especially knowing it was going to be tough to beat the other participants. I was optimistic and pressed on … on Tuesday the demojam rehearsals start around midday. You get to run through your presentation twice, with others watching and the stage/event crew helping you out with monitor setup, displays, etc. You get to meet the funny and friendly Ian Kimbell, and also Tobias and Sebastian who coach you through your pitch once again, providing some really helpful feedback.  This really helps ensuring that your demo goes  smoothly.

A word of praise – The events team are amazing people, they will literally do anything to make are you are comfortable and happy and EVERYTHING you need to be successful. I do not have enough praise for them. They really go above and beyond, and this image should give you an idea of just how far they go.

photo.JPG

Jen Abrahamson (part of the talented events team) gave me this shirt just before the event!

While going through my 1st rehearsal I was so nervous I left out close to 40% of the pitch/demo and still had 2 minutes on the clock, at that stage, i realized I needed more practice and I spent the rest of the afternoon going through my talk over and over again to engrain it as best I could. In between that – as you can guess, I was also doing last minute code changes making some small modifications. Having the audience involved proved to add its own set of challenges, since I didnt have a full time HANA system and using the developer image on AWS, it was far from a very powerful machine, I spent quite a lot of time running load tests, but was never 100% sure who and how users were going to influence the performance and subsequently the outcome of the demo … major stress factor.

Around 8pm the event started and I got my time on stage, I went through my presentation and felt it really went pretty well. It was reasonably well received, with the exception of me forgetting to mention that the app has support for gestures using the Leap Motion. During the demo I walked over to the large monitor to show this functionality, it did not work – making me look like i was swatting or catching flies Boo.


“Tell people what you are doing and why, that way when it does not work, people at least understand what you were attempting to do….”

With around 2 minutes left I asked the audience to open up the demo website on their mobile device and they got to pick which pill they wanted to purchase from my online store (running on HANA) which was being monitored by Metric². Up on the screen you could see the orders being completed in realtime and how the system was reacting to the new load. In the end around 700 orders were completed which was great, and to me, is what made the demo a success.

Link to my Demojam presentation

I was subsequently voted off the stage in round 1, I walked off the stage pretty disappointed … all I could think about was what I would need to do to win it next year. Its official, I am hooked. I now understand why John Astill has done this for so many years over and over again.

In the end, I *really* want to win. I already have around 5 ideas about what I could potentially do next year and I really hope i get the chance to redeem myself and write a sequel to this blog post, but from a winners perspective

Thinking of submitting an entry? Here are a couple tips which I learned:

  • Think small – my challenge was that I developed a full featured app, with loads of features. This is challenging at best just to demo in 6 mins, never mind apply a crowd friendly use case.
  • Your presentation is 50% of your demo – Have a creative and complimentary presentation and you can succeed very easily. (The running demo of John and Greg is a great example of a creative presentation).
  • Creative use of a technology, far outweighs technology being used for creativity. I.e. Don’t just use HANA for the sake of it
  • Come to TechEd prepared, have your 6 min pitch nailed down. I didn’t and it was far more stressful than I had hoped.
  • Even if its the same, make it look different: On Tuesday morning Vishal Sikka and Sam Yen gave some great introductions to the SAP UI5/Fiori programming paradigm. Guess what, my app had a very similar look and feel. When they were going through their presentations I was literally sinking in my seat … I was actually considering changing my color scheme and if I had just a couple hours more, I probably would have. Point is, even if your demo is a UI5 app, don’t make it look like UI5 app.
  • The devil is NOT in the details, its in the use case. Its far more important to have a sound, solid and relevant use case, than a phone number having the correct input format of (111) – 111 – 1111.
  • Tell people what you are doing – (Sebastian suggested this) – it is helpful and adds context to your presentation. Its also often hard to see what your mouse is clicking on, or what watch button you are pressing.
  • Your jokes are not as funny as you think they are – I threw a couple small jokes in and all I heard was crickets chirping, nothing breaks moral more One of Ian’s tips are not to start with a joke, and I fully agree.

The Demojam event needs some changes …

I noticed a couple of tweets a couple of days ago about changing rules, and doing things differently, having now participated in an event, here is are my feedback/comments to the demo jam organizers:

  • We need more demo jammers, way more. if it means we need to reduce the 6 minutes to 4, so be it. The event starts pretty late which also may limit the amount of Demojam presentations, the SCN event is also prior which takes up quite some time while being a big advocate for this, it might be best to restructure the timetable a little?
  • I would like to see a couple different categories. It would be great to have a “open source” category and a “regular” category. Any others you can suggest?
  • I think there should be more prizes for different criteria or per category, this could or could not be known prior to the event. At Innojam LV, there were multiple winners and this really encourages more entries across multiple disciplines.
  • I would like to see SAP employees back in, even alone. Maybe a category for best SAP employee demo? They have developed some great tech in the past, I understand it could be a slight advantage due to technology accessibility, early access, and a couple of other aspects but I also believe the audience wants to see how internal SAP employees are innovating in their respective areas. I know there was a huge thread back in 2011 from Demojam winner Matt Harding about this topic, and this is just my opinion
  • Limit previous winners? No way, I applaud jammers who can win multiple times, it encourages all other jammers to build something more creative, more innovative and deliver a great presentation.
  • Please change the voting – the “Clapometer”? I would prefer to see a slightly more transparent process … winners could be based on amount of tweets? Voting page from a mobile app? Even a panel like X Factor handing out tasteless feedback would be pretty cool and a bit more enjoyable.

In the end ….

Demojam ROCKS, I am truly grateful for an opportunity to participate. It was a great feeling to have completed a life long dream of mine. I got 6 minutes of fame up on the same stage as the greats of Vishal Sikka and Hasso Platner. Here’s the best part – YOU can too. All of us have done something outstanding, something creative and something I/WE/YOU/SCN/Attendees want to hear about. I encourage you to create or share a concept, a dream or just a downright outrageous idea for next years demo jam, and hopefully join me in showing your peers, friends and family that you are a dedicated and a creative innovator of awesome enterprise technology.

Metric² Update

For those of you who would like to download/check out Metric² – its coming! I had a huge amount of great feedback at TechEd and I am in the process of making some of the suggested changes to make it a little better before throwing it into the wild. (I developed the entire app on the concept of polling due to the XS Engine limitation of not supporting Web Sockets in SPS6 = not perfect.) You can stay up to date using the website here: http://www.metric2.com


Image+[20]

Microsoft Power View and Netweaver Gateway – A easy com*bi*nation

I was recently at a customer site where they were using a Microsoft Excel based BI Tool, Power View for in house adhoc BI reporting and I was quite impressed. The ease, simplicity and functionality was quite refreshing and I started to do some additional digging into the application and its functionality. My first and unfortunately, my biggest shock, came at the fact it required Silverlight. Yes, you read that right … this means that the reports/functionality is not available on your favorite mobile device. This did not deter me and I was interested on how this could apply to the SAP world from a desktop perspective and was pleasantly surprised when I started scratching the surface.

Firstly, a bit of an introduction: Power View is a relatively new product from the Microsoft BI Stack and was recently included in Excel 2013 (Professional Plus editions). It was previously available in Sharepoint Server 2010 and still is included with SP 2013. You can use Power View directly from a Excel workbook, within Sharepoint, as well as deploy the Power View Excel sheet to a Sharepoint 2013 Server. Power View is an interactive data exploration, visualization, and presentation experience (and can be compared to SAP Lumira). When I started to do some digging I found that this blog around SAP MII tool also uses it as part of its data analysis options. Power View has a variety of functions which are covered on this site and I will not repeat.

A powerful feature of Excel, which is often overlooked, is the fact that you can use OData as a data source and this allows us to use data from REST based services built using SAP Netweaver Gateway. Since Gateway can subsequently expose data from sources like BW, HANA, CRM, ERP and so on, it provides a great reusable foundation for casual BI, mobile devices or web apps (like Fiori) for lightweight consumption. SAP Lumira also offers OData support with its recent v1 SP11 service pack which now makes it a bit more appealing for customers who have implemented Gateway.

What does this mean for SAP customers? With Excel being widely adopted, why not reuse your existing investments and services from Netweaver Gateway to power some casual adhoc BI tools for your business?

Here is a quick walk through which can get you started on developing Power Views for your business (please keep in mind that creating Power View sheets from Excel and Sharepoint is slightly different, in this case we will use Excel). If you don’t have Excel 2013 Pro Plus, you can download a copy here: http://technet.microsoft.com/en-us/evalcenter/jj192782.aspx

Data Source: As mentioned previously we can use a OData service as a source for our Power View, in this case we will use a great new service recently developed by Andre Fischer and available on the Gateway demo servers. If you would like to develop your own Power View Sheets based on these demo services, you will need an account. (Its quick and easy!).

So lets jump in … Open Excel 2013 and click on the “Data tab”, “From Other Sources” and “From OData Data feed” and enter the Gateway service details along with your username and password. In this example we will be using the Sales order collection service : https://sapes1.sapdevcenter.com/sap/opu/odata/sap/ZGWSAMPLE_SRV/SalesOrderCollection

Image [2].png

 

Image [3].png

Click Finish

Image [4].png

Once completed, you should be presented with the Power View Sheet and a basic layout, along with your data source field list on the right side:

Image [5].png

You can kick things off by selecting a few columns from the sales order collections table which will show up on your report in a tabular format.

Image [7].png

First thing we will do is go across to our data set (Our power PowerPivot sheet) and add a SUM function to our Gross Sales Column. Click on PowerPivot -> Manage. Select the GrossAmount Column and be sure to set its type to decimal. (You should do this for all your “numeric” based columns). Then at the bottom in the Calculated field, select the first row and “AutoSum”

Image [6].png

 

If you save and close the Power Pivot, you should have something like this showing:

Image [7].png

Next we will clone this table and use it for a Pie Chart, select the table, copy and paste it. Then with the new table selected, click the “Design” tab -> “Other Chart” and Pie.

Image [8].png

Once again we will take our original table and clone it. We will use this to display a line chart of the average order size by using a Line Chart. Resize your charts appropriately, and select the line chart. Also modify the Value to be Average, rather than the SUM.:

Image [9].png

As I am sure you have noticed by now, is that filtering on charts is easy and they are all linked. Selecting a specific customer will actually change all three of our charts appropriately which is very slick.

Image [10].png

Progress

Next we will be adding a KPI, this can be done by heading back to our PowerPivot sheet, selecting a column and selecting AutoSum -> Count.

Image [11].png

This will add an additional calculated column to your table list:

Image [12].png

Then select PowerPivot -> KPIs -> New KPI

Image [13].png

Your KPI base field should be selected and you can define a Absolute Value (“Goal”) of 250 and any other options you may prefer. Select OK.

Image [14].png

This will convert your calculated column to a KPI, and selecting one of the fields will add it to your Sheet.

Image [16].png

Lastly what we will be doing is adding a few filters to our view, this way we can filter all charts/KPIs with a single selection. You can do this by selecting “View” on the filter sidebar, then dragging and dropping your needed columns.

Image [17].png

In the end, it is a fairly simple process to build a interactive and useful dashboard … here is my finished Power View …

Image [20].png           Image [18].png

A couple other cool things to check out …

-> The Map “Chart” …
-> Drill down features … (hint – you need to create a Hierarchy)

To Do ….

-> Change your column names from PowerPivot as soon as you have your data set created
-> Add Currency prefixes to the columns/dataset
-> Remember that the dataset is stored in your file.

Here is the Power View Chart … download it, enter your credentials under the PowerPivot “Existing Connections” -> Advanced and you should be able to use it as a template …

Image [19].png

Here are a couple more helpful links to get you started:

http://office.microsoft.com/en-us/excel-help/create-a-power-view-sheet-in-excel-2013-HA102899553.aspx

http://www.slideshare.net/idigdata/excel-2013-power-view-and-powerpivot-basics

http://download.microsoft.com/download/A/9/9/A9971C42-ECB6-4059-BA60-7E7B5B98BD40/Microsoft_BI_Interoperability_with_SAP_White_Paper.pdf

http://www.jenunderwood.com/presentations/PowerViewPowerPivotBasics2013SP1.pdf

If you made it this and are still engaged I am curious to know if you are using Excel Power View in your organization? What do you think of it? Would you consider using it in the future?


Mockup

SAP UI5 – A Photoshop template of GUI elements

Mockup.png

For me (and I am sure many others!), UI5 has become a really useful toolkit for easily developing web apps and POC’s with little effort around the SAP Ecosystem. The OData support, rich controls and business inspired examples lend itself well to the mantra of “Don’t reinvent the wheel”. At a recent SAP Inside Track event in NY, I made reference to the fact, that while developing a mobile app, I, or a graphic designer, can spend close to 40% of the total design & development phase/hours on the UI. Building “consumer grade” enterprise apps is a different way of thinking for app developers. I am sure many of you, like me, started out with a redefined canvas of boring, grey .NET controls or a “Enjoy” SAP CXTAB_CONTROL. But times are changing and we should all look to the creative and vibrant consumer world of both web and mobile apps to reconsider our methods and processes for developing great apps.

When I started working with the UI5 toolkit, I would more often than not, start coding right away, without going through my traditional methods, and ultimately ended up in a frustrating muddled mess. Why wouldn’t you start coding immediately …. everything you need is right there? Wrong, planning is everything when it comes to design. Properly designing a app which is going to judged, by today’s standards, is not an option – but a requirement. I am not saying every application in your organization needs a UI Designer, but planning should start with sketches, process flows, and subsequently lead to the UI design and  development. When you have a toolkit which has some great looking components, its easy to skip this phase and get cracking. Don’t do it. Since I have spent a lot of time developing both web, mobile and traditional apps for the enterprise, my approach and system development life cycle (SDLC) resembles more of a artistic approach and design definition, than a “traditional” enterprise app. Yours can be entirely different, but the point is that I would like to encourage you to consider that a *great* design should be a part of your project goals.

The Reason

That’s the reason I developed the PSD file of UI5 elements. To make that design planning process quicker, easier and to encourage all of us collectively to make great designs and awesome apps.   Screen Shot 2013-07-09 at 9.19.16 PM.png

The Process

I used the online test suite and replicated the majority of the objects using shapes in Photoshop. Each object has groups/folders categorizing and defining it, both for extensibility and this makes finding a component/object easier. Some objects are not included: Icons and some of the VIZ Charts are not individual objects. Maybe in the near future if I can get some form of ‘OK’ from SAP for not copying/redistributing their font I will consider it (hint?).

Screen Shot 2013-07-09 at 9.15.29 PM.png  Screen Shot 2013-07-09 at 9.20.44 PM.png

The UI Toolkit

95% of graphic designers will spend time working with a UX/UI in Photoshop prior to coding and this toolkit makes creating those mockups considerably easier. Each object is defined as a shape and can be moved, and styled as needed. Currently the file has 897 individual layers which makes up the +- 8.2 million pixels of UI goodness . Building a new UI mockup should not take long … create a new file, duplicate the objects you need and get creative. (Please also consider the fact someone is going to need to code this and that your imagination, should be limited by *their* capabilities!)

Whats next …

I am hoping someone else interested in the UI/UX aspect will be able to contribute to the project and consider extending this file and its objects to cover all the aspects. I also hope that SAP will consider releasing its “Master” file to the community as ultimately it will encourage better adoption and use of the product.

My challenge to you ….

Does you/your company/partner have a UI/UX designer? Do you think its important to design a UI before coding? Do *you* have any great UI/Designs you would like to share? Post a link in the comments, or better yet, create a post here: https://experience.sap.com/ (SAP Why is experience.sap.com not directly integrated/on SCN??? )

You can download the file here:http://www.li-labs.com/downloads/sap%20ui5%20gui.psd

Don’t have Photoshop? Here are a couple of the elements split up and are available in PNG format: http://www.li-labs.com/sap%20ui5%20png.zip


icon_256x256

SAP Notes Viewer for Mac

Since I have been spending the last few weeks getting some Netweaver instances running on my new Hyper V server, I have spent a considerable amount of time on the SAP Notes website debugging and troubleshooting everything from installation to master guides. Since my SAP Note Viewer apps for iPhone and iPad are fairly popular and I was tired of having to login so many times, I figured I would develop a “clone” for the Mac.

The free app features a pretty basic status bar menu and when you open it for the first time, requests your s-id and password and your search language (German and English). Once opened, you can enter a Note number or search term, submitting will automatically bring up the note in your browser. If you enter a search term, the app will search and return a list of results which you can select, opening the note. The search is fairly slow, as it is, in essence, making two round trips (one for the page and the second for the results).

I have also submitted the app to Mac App Store and it is currently in review, but if you would like to get your hands on it and give me your thoughts, it would be greatly appreciated. Here is a link to a download page: http://www.li-labs.com/SNVMac.zip (450Kb), just unzip and drag into your “Applications” folder. You can also download the Mac Store here: https://itunes.apple.com/us/app/sap-note-viewer/id575046244?mt=12

Here are a couple screenshots.

icon_256x256.png Screen Shot 2012-09-27 at 10.16.45 PM.pngScreen Shot 2012-09-27 at 10.19.01 PM.png

icon_128x128

xSync – Making HANA XS development easier

A couple of weeks ago, I started playing with HANA development after going through a couple of the opensap courses. One of my biggest surprises was that HANA Studio was windows only. As a Web/Mobile/App developer spending majority of my time on the Mac platform, having to get my Dell out was a walk down memory lane, and caused me to not spend as much time working on the projects as I would have liked. Since Rev 52 of HANA included a XS Engine Lite IDE, a useful web based IDE for your XSJS/SAPUI5 development. I was been looking forward to getting it installed on my AWS machine and giving it a test drive. Last weekend Juergen Schmerder posted a updated really easy to follow and simple guide of going through the process and within a hour, I was up and running on the newest available rev.

First off, the web IDE was great. Secondly, not having to use my old Dell was bliss. From a development perspective I am OK with the fact I need to do schema modeling/view generation input etc. through the Studio IDE, its well built for this and has functions and customizations which are not as necessarily as generic as web/java/jquery development. I spent the weekend working on a small POC/rewrite I have been working on and enjoyed the development experience. But after a while, found myself wondering about code completion, syntax highlighting, text formatting and a few more features which are generally available in your favorite IDE.

After some digging, I came up with the concept of being able to develop locally and yet still be able to execute the code online through my browser as if I was using either the Lite IDE or the Studio. I am not sure if #openSAP intend for students to hack HANA, but that’s exactly what happened …

icon_128x128.png

Enter xSync … xSync is a *little* like dropbox for your HANA repository. Its a Mac app which runs in the Status Bar and watches file system changes to a designated development folder. As soon as you save, it saves your file in XS Engine to your package repository. Development is no longer is choir of saving, syncing and activating Even if you are not a developer, it has some useful features, it shows if your HANA instance is online – avoiding unnecessary costs, and also allows you to easily download all files from a HANA package or development project. This is great for creating local backup/snapshots regularly. Below is a quick video of the app in action.

If you are interested in testing the app out, please download a copy here – all feedback and constructive criticism is welcome as usual. Please keep in mind, this is still *very* beta. I have done my best to test, but from conception to app in a couple spare hours is always a challenge = DISCLAIMER, use at own risk!

Some technical notes and features:

- Features:
- Use your favorite IDE to edit files locally
- Save, Create, Delete, Rename files locally and have them *sync* with your XS Engine package
- Quick way to ensure your Instance is available (watch costs on your cloud hosted system!)
- Download all package contents to your local HDD
- Easily upload files/folders to your packages

- Requirements:
- HANA Rev. 52

- Usage:

1.) Download the app here

2.) Enter your HANA XS Engine URL, package (I suggest creating a test package to test the app out with)

3.) Enter the folder location of your local development objects (I suggest a blank folder to start as it will not upload existing objects already in the folder)

3.) Enter HANA username/password

4.) The log will show that the folder is being watched

4.) copy/create a new file in the folder and check the log to ensure the file was synced

5.) develop something awesome …

- Bugs/Issues:
- Renaming/creating folders could be a little better, since XS engine does not require spaces, it can be problematic

- Future Ideas:
- Sync existing files
- Maintain a local “folder” structure db for reference
- Sync from XS Engine if changes made there
- Insert your idea here
- Less ‘buggy’ for release 1

For all the HANA hackers out there … how do *you* think the app is integrated with HANA XS Engine? Any guesses? I will follow up this blog in a couple of days with the technical details and how the app works.

Screen Shot 2013-06-15 at 8.43.30 AM.png

Image2

A peek inside xSync and the HANA XS Engine

icon_128x128.png

On saturday I published a blog about a small app I wrote called xSync – basically a XS Engine app for Mac developers where you can sync a local development folder with your HANA repository. This is for rapid development and to encourage the “bring your own IDE” approach to application development on HANA. Here is a look behind the scenes on how the app works and some of the challenges of the project.

Image.png

As mentioned in my previous blog – I started using the IDE Lightweight editor after doing the upgrade of my AWS HANA box last weekend. I enjoyed the experience but after working with it for nearly a full day was wanting a little more. Syntax highlighting, easy commenting, easy indentation, CSS autocomplete and hints, etc. etc. so I started doing some peaking around the editor itself and came to find the editor is something called ACE, a pretty nice little open source IDE (written in JS). This got me thinking … maybe I could insert text directly into the Lightweight IDE browser text box, and submit the form as a save …. hmmm …. not a terrible idea …. just need to scrape the page, find the elements and submit the form via some injected JS. Pretty simple …  I did some digging and found the HTML objects I needed by using Firebug when a lightbulb went off … instead of populating the form via a HTML page, why not rather check the HTTP methods it is calling when doing the actual save, since there must be some integration with HANA directly … which is when I came across the mother load … a small file called reposervice.xsjs It seemed that every time I was saving or modifying my objects through the IDE, it was calling this file. After checking out the parameters it was `, it was very clear that the methods and text were easy to simulate. I fired up REST Client and within a couple minutes the concept was POC’ed. Pass your file contents as your body with a path param and a POST and you were off to the races

Screen Shot 2013-06-09 at 4.39.png

Using Firefox Rest Client to monitor system calls showed each save, create, delete operation was using a small file called reposervice.xsjs, which references the libraries needed for the repository modifications.

Image2.png

The diagram above displays the HTTP call made when saving/creating a file, and how the IDE initially does a HEAD request for the XSRF token, followed by the HTTP PUT.

The initial HEAD request is to fetch the CSRF Token, secondly the token along with the parameter of mode, path and activate are passed to the URL. Pending you are successful, a JSON message is returned with the status. For those of you are not familiar with Cross-Site-Request-Forgery, you can read about it here: http://en.wikipedia.org/wiki/Cross-site_request_forgery

Once I had this done, I was wondering what the best integration option would be and weighed up a couple options of a simple check in type procedure, but wanted something faster, easier and “click free”. Being a bit of a highly iterative developer myself, I find it easier to develop “online”, which is why I decided it would be best to do a File System watch of a particular folder and save any changes automatically to my HANA instance. Similar to a dropbox type approach.

I had my POC working nicely, a integration goal defined and set out to start developing the UI/Application in Objective-C (Xcode). I had a template type of app from one of my little SAP Note Viewer applications which could act as a foundation. I threw some code out and pulled some very useful little open source packages in as helpers. Within a couple hours in my evenings each night the app was running nicely and doing what I had expected, modify a file or two in a predefined location and sync up to XS. easy.

Thats generally where development grinds to halt for me, as I envision feature after feature to build a Mac clone of HANA Studio Luckily my senses got the better of me, and I worked on doing a recursive package downloader, the ability to create, rename and delete files and folders and not a HANA Studio rewrite Once this was all done, ironing out the bugs was painful. The cocoa FSEvents stream (File System Events) Class on the mac is not easy to work with and a bear at best. Having to monitor a folder for any modifications, deletes and creates turned into a bit of a logic nightmare. One of the interesting challenges is that if you “delete” a file on the mac file system, it does not get a “delete” FS Event but rather a rename! (Since it goes to the trash/recycle bin!). This leads to having to do multiple … if exists then …. type statements around each file and folder event

UI is another interesting one, I like apps to look somewhat decent … and I spent a good amount of time working on each of the elements in Adobe Photoshop as usual … (Whenever I do a mobile app development talk I mention that I spend close to 40% of entire project time in apps like Photoshop with design work! Most are surprised!)

If you are interested in incorporating some these types of features into your own app, I will be posting a copy of the integration classes on GitHub shortly.

PLEASE KEEP IN MIND: This is exploratory type work with undocumented API’s, I would not recommend using this in production, or any important production work (or your important opensap homework!). The reason I shared this was to encourage people to look under the hood and understand the how’s and why’s of how some of these great new tools work.

I would be interested to hear if anyone has any interesting use-cases for being able to manipulate both HANA repository and DB artifacts from outside of the Studio? Does anyone have any challenges with the HANA Studio today they would like to see changed?


Screen Shot 2014-04-23 at 4.57.06 PM

Communicating with other iOS apps using URL Schemes

When I started developing iOS apps a couple of years ago, I was concerned that enterprise apps were going to quickly succumb to the world of development scope creep. Since the apps were fairly focused and provided a single function or process solution, what if the business wanted more? For example, you have a CRM app displaying your customer contact information and some high level details regarding order history and credit limits, but what if the user wanted some additional details from your BI solution drilling down into historical orders and profit analysis? Well, in that case, you had a few options:

1) Extend the scope of the app – by adding functionality and options, sometimes a reasonable option if the scope is not excessive or the functionality is relative to the app. In our  above example, probably not.

2) Build another app with replicated functionality – duplicate code to display the customers and overview details in both apps.

3) Build 2 separate apps – But this would require the user to re-enter information (such as searching for and entering the Customer ID) in the 2nd app.

The fourth option is to use option 3 but with a twist, by enabling communication between the 2 apps by using URL Schemes. As the majority of iOS developers know, you can open other standard iOS apps like Safari, Mail or the Phone using the UIApplication SharedApplication class, what many developers and mobility architects overlook is that, not only standard apps can be called from your code, but any app which has a URL scheme defined. This gives you the ability to separate processes by application, and in the event a user would like drill deeper into additional information or functions, you can pass information to another app where that can be handled. What if the app is not installed? Good question … You device will smartly open the Apple App Store giving you the ability to install the missing app. Configuring and consuming the communication between 2 apps is very simple and straight forward.

http://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/Art/app_open_url_2x.png

 

 

You can read about the URL Schemes within the apple documentation here:

http://developer.apple.com/library/ios/#featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html

and here: http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/AdvancedAppTricks/AdvancedAppTricks.html#//apple_ref/doc/uid/TP40007072-CH7-SW18

Within the SAP Suite of iOS Apps you can also find URL Schemes already configured and in use, some examples of these are:

CRM @ SAP

Find account by number: crmatsap://openAccount?accountid=<number>

Open opportunity by number: crmatsap://openOpportunity?opportunityid=<number>

Open account by number: crmatsap://openAccount?accountid=<number>

SAP Pricing

Open Product by ID: pricingapp://openProduct?productid=<productid>

If you are still not sure if iOS URL Schemes are right for you here are a couple of Pro’s and Con’s

Pros:

- Use other custom apps to augment yours

- Use other companies apps to augment yours

- No need to redevelop similar functionality

- Communicate between apps

- Launch your app from a hyperlink (online/in a web page?)

- No need to be online for base functionality

Cons:

- Shouldn’t pass sensitive data

- Clearly define the scope of each app and avoid over simplifying (e.g. an app for searching for customers by name, another for searching by ID.)

- Should not be a critical function, since there is no guarantee that the data is being received by the other application

I am curious to know how many app developers out there are using URL Schemes within their custom development?

 


mzl.qqijnvvh

zSCN – A iPad SCN Community Reader

After spending a couple of months traveling in 2012, I wrote a personal app for reading and following content created on the SAP Community Network. Although it was slightly buggy and ghetto, I found it way faster than firing up my MBA to check if a comment or something helpful had been posted in my local Netweaver Gateway hangout. I spent the last +- 6 months using the app and after reading a few comments on the new Idea place and in the forums, I decided a couple of others may find it helpful and set out to build a user friendly version.

Now on to the app …. firstly, starting up the app you are presented with a list of spaces (thanks to Jason Lax for creating a helpful list here!), secondly, the app uses the RSS feeds from each SCN “Space” to curate the content, each time you select a space the content is cached on the device (in a little SQLite DB) and refreshes each time the space is opened. You can also select the number of items which are downloaded in each category (blogs, documents and forums). After logging into SCN using your SCN username/password, it will download your spaces, people and saved content. Keep in mind that you cannot “save” or create content from the app, since the SCN API’s are not currently publicly available (I am hoping for some change on this!). However, each item can be selected and the original content will be displayed on the website where you can login and respond/comment. You can also easily email the item to a friend in need, or tweet it if you are a social butterfly

Currently the app has been written for iPad only but the iPhone version is in the works, but since blogs and more in depth content is more conducive to read on a tablet, I am still trying to decide if it would be warranted … any comment?

Personally, I am quite surprised the Community does not have a mobile app for iOS/Android since it would ultimately bring better adoption and collaboration amongst the users. Like all the other free apps I have developed, this is a work in progress and am hoping the direction and goals get driven by the community of users, so any and all constructive comments are welcome!

Here are a couple of screenshots and features of the app, you can download the iPad app here: https://itunes.apple.com/us/app/zscn-for-sap-scn/id587877577?ls=1&mt=8

Features:
- Access to your saved spaces, bookmarks and people
- View all available SCN spaces
- Blogs, messages and documents are all displayed on a single page for quick and easy browsing
- Each space includes relevant tweets
- Share articles via email or twitter easily

mzl.qqijnvvh.png

mzl.aanoimmq.png

mzl.vjmmiumh.png

mzl.mkweqdfx.png

mzl.wgoysymu.png