Skip to content

Instantly share code, notes, and snippets.

@itayw
Last active August 29, 2015 14:12
Show Gist options
  • Save itayw/8c21829767066e5d317a to your computer and use it in GitHub Desktop.
Save itayw/8c21829767066e5d317a to your computer and use it in GitHub Desktop.
Joola Demo

#Figure 2 For our demo, we'll use the theme of website visitor tracking, i.e. visitor arrive, browse, click and we collect data on their actions.

The event

Let's start with a simple recording of a user visiting our website.

{
    "user": "itay",
    "visits": 1
}

Note that the event contain a single dimension named user with the value itay and a single metric named visits with the value of 1.

Pushing into Joola

We will pushing the above event into the visits collection. Joola will create the collection if it doesn't exist.

$ curl -X POST -H 'Content-Type: application/json' -d '{
    "user": "itay",
    "visits": 1
}' http://localhost:8080/insert/visits?APIToken=apitoken-demo

#####Response Joola returns the document as it was saved into MongoDB. Note that Joola added a timestamp indicating the exact point in time it received the event for processing. Also, the saved document contains a timebucket to assist with future queries.

[
    {
        "user": "itay",
        "visits": 1,
        "timestamp": "2014-12-29T21:09:33.530Z",
        "timestamp_timebucket": {
            "dow": 1,
            "hod": 23,
            "second": "2014-12-29T21:09:33.000Z",
            "minute": "2014-12-29T21:09:00.000Z",
            "hour": "2014-12-29T21:00:00.000Z",
            "ddate": "2014-12-29T00:00:00.000Z",
            "month": "2014-12-01T00:00:00.000Z",
            "year": "2014-01-01T00:00:00.000Z"
        },
        "ourTimestamp": "2014-12-29T21:09:33.537Z",
        "saved": true
    }
]

#####Response Headers There are a bunch of response headers you might find useful.

HTTP/1.1 200 OK
Access-Control-Allow-Headers: content-type
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: ETag, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
X-joola-Request-Id: MSQELAXayqsDR5HwHG123rx1f1KUfLWN:1419887621341:mt5uLtJSD92HbPxXkluXRP1coxhla0Qd
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-RateLimit-Limit: 9999999999
X-RateLimit-Remaining: 9999999933
X-RateLimit-Reset: 1419890341
Retry-After: 2720
X-joola-Duration: 15
X-joola-Requested-By: MSQELAXayqsDR5HwHG123rx1f1KUfLWN
X-joola-Fulfilled-By: MSQELAXayqsDR5HwHG123rx1f1KUfLWN
X-joola-Duration-Fulfilled: 10
Content-Type: application/json; charset=utf-8
Content-Length: 374
Vary: Accept-Encoding
Date: Mon, 29 Dec 2014 21:13:41 GMT
Connection: keep-alive

#Figure 3 Now that we have an event pushed in, let's visualize it! For this we need a simple, static HTML page.

Create the HTML framework

$ mkdir ~/joola_demo
$ cd ~/joola_demo
$ touch figure-3.html

Using your favourite editor, paste this code.

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"/>
</head>
<body>
</body>
</html>

Open the file we created in your browser.

###Joola SDK We need to include Joola's Javascript SDK on the page and to keep things simple we're going to include the APIToken within the src tag.

<script src="http://localhost:8080/joola.js?APIToken=apitoken-demo"></script>

After loading the page again, let's make sure Joola is alive and kicking. From your Developer Tools -> Console:

> joola.VERSION
< "0.7.26"

If you see a version printout, everything is working fine.

###Drawing your first visualization In order to draw our first visualization, we need two things, a div container to draw the visualization in and to instruct Joola to do so. Let's start with the div, add this to your body tag:

<div id="timeline"></div>

Now add this to the end of the document, just before the </body> tag.

<script>
joola.on('ready', function() {
    new joola.viz.Timeline({
        container: '#timeline', 
        query: {
            timeframe: 'last_1_hour',
            interval: 'minute',
            metrics: ['visits'],
            collection: 'visits'
        }
    });
});
</script>

After reloading the page again you'll see a line chart visualization being rendered on your screen showing the event we pushed in Figure 2. Try pushing more events, refresh the page and see how they accumulate.

#Figure 4 So now we have pushed events and visualized them, but let's kick it up a notch.

More event data

Firstly, let's buff up our event. Include the following Javascript in your HTML before the </body> tag, but after the Joola SDK script.

<script src="https://rawgit.com/itayw/8c21829767066e5d317a/raw/d6dc67b6ab46e9d530cdf66617c7798553d7e6f5/zEventPusher.js"></script>

The above event pusher script tracks 3 different user actions:

  • When a user visit the page, the visit and the load_duration_ms of the page.
  • When a user click anywhere on the page, the click is recorded.
  • When a user moves his mouse over the page, the move is recorded.

Here's how a typical event looks like:

joola.insert('visits', {
    user_agent: navigator.userAgent,
    userid: userid, //generated randomally for each page load
    ip: ip, //generated randomally for each page load
    referrer: document.referrer,
    visits: 1,
    loadtime_ms: 12
});

Let's turn realtime on

Before adding realtime, let's change our metric to moves, so it will be more lively and then all we need to do is add realtime: true to our Timeline options:

new joola.viz.Timeline({
    container: '#timeline',
    query: {
        timeframe: 'last_1_hour',
        interval: 'minute',
        metrics: ['moves'],
        collection: 'moves',
        realtime: true
    }
});

After reloading the page, you'll notice everything moving in realtime.

Let's add another visualization

For the new visualization, we need another div container for it and instructing Joola a new visualization is needed. Add the following div:

<div id="table"></div>

Add the following Javascript to your bottom script tag right after the existing Timeline directive.

new joola.viz.Table({
    container: '#table',
    query: {
        timeframe: 'last_1_hour',
        interval: 'minute',
        dimensions: ['userid'],
        metrics: [
            'visits',
            {key: 'clicks', collection: 'clicks'},
            {key: 'moves', collection: 'moves'},
            {key: 'loadtime_ms', aggregation: 'avg', suffix: ' ms.'},
        ],
        collection: 'visits',
        realtime: true
    }   
});

Catching events

Let's catch an event of users selecting table elements. Change the recently pasted code above to this:

new joola.viz.Table({
    container: '#table',
    allowSelect: true,
    query: {
        timeframe: 'last_1_hour',
        interval: 'minute',
        dimensions: ['userid'],
        metrics: [
            'visits',
            {key: 'clicks', collection: 'clicks'},
            {key: 'loadtime_ms', aggregation: 'avg', suffix: ' ms.'},
        ],
        collection: 'visits',
        realtime: true
    }   
}).on('select', function(selected) {
    console.log('selected table item', selected);
});;

#Figure 5

Canvases allow us to combine several elements within a single scope and allow them to have an out-of-the-box functionality.

###Create a Canvas Create a new HTML document:

$ cd ~/joola_demo
$ touch figure-5.html

Using your favourtie editor, paste the following:

<!DOCTYPE html>
<html xmlns:joola="http://joo.la/ns/1.0">
<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"/>
  <style>
    body {
      padding-top: 30px;
    }

    table {
      width: 100%;
    }
  </style>
</head>
<body>
<div id="canvas" class="container">
  <div class="row">
    <div id="filters" class="col-md-12"></div>
  </div>
  <div class="row">
    <div class="col-md-12">
      <div id="datepicker"></div>
    </div>
  </div>
  <div class="row">
    <div class="col-md-3">
      <div id="metric1"></div>
    </div>
    <div class="col-md-3">
      <div id="metric2"></div>
    </div>
    <div class="col-md-3">
      <div id="metric3"></div>
    </div>
    <div class="col-md-3">
      <div id="metric4"></div>
    </div>
  </div>
  <div class="row">
    <div class="col-md-12">
      <div id="timeline"></div>
    </div>
  </div>
  <div class="row">
    <div class="col-md-4">
      <div id="bartable1"></div>
    </div>
    <div class="col-md-3">
      <div id="bartable2"></div>
    </div>
    <div class="col-md-3">
      <div id="bartable3"></div>
    </div>
    <div class="col-md-3">
      <div id="bartable4"></div>
    </div>
  </div>
  <div class="row">
    <div class="col-md-12">
      <div id="table"></div>
    </div>
  </div>
</div>

<script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="http://localhost:8080/js/ua-parser.min.js"></script>
<script src="http://localhost:8080/standalone-framework.src.js"></script>
<script src="http://localhost:8080/highcharts.src.js"></script>
<script src="http://localhost:8080/joola.js?APIToken=apitoken-demo"></script>

<script
    src="https://rawgit.com/itayw/8c21829767066e5d317a/raw/a34df047cbd65b74f8b4cdb10b61f5d85040eafd/zEventPusher2.js"></script>

<script>
  joola.on('ready', function () {
    new joola.viz.Canvas({
      container: '#canvas',
      realtime: false,
      datepicker: {
        container: '#datepicker'

      },
      filterbox: {
        container: '#filters'
      },
      visualizations: {
        bartable1: {
          type: 'bartable',
          caption: 'Mouse moves by Browser',
          container: '#bartable1',
          query: {
            dimensions: [
              {key: 'browser', name: 'Browser'}
            ],
            metrics: ['moves'],
            collection: 'moves',
            orderby: [
              ['moves', 'DESC']
            ]
          }
        },
        bartable2: {
          type: 'bartable',
          caption: 'Clicks by Engine',
          container: '#bartable2',
          query: {
            dimensions: ['engine'],
            metrics: ['clicks'],
            collection: 'clicks',
            orderby: [
              ['clicks', 'DESC']
            ]
          }
        },
        bartable3: {
          type: 'bartable',
          caption: 'Visits by IP',
          container: '#bartable3',
          query: {
            dimensions: ['ip'],
            metrics: ['visits'],
            collection: 'visits',
            orderby: [
              ['visits', 'DESC']
            ]
          }
        },
        metric1: {
          type: 'metric',
          caption: 'Mouse moves',
          container: '#metric1',
          query: {
            metrics: ['moves'],
            collection: 'moves'
          }
        },
        metric2: {
          type: 'metric',
          caption: 'Clicks',
          container: '#metric2',
          query: {
            metrics: ['clicks'],
            collection: 'clicks'
          }
        },
        metric3: {
          type: 'metric',
          caption: 'Visits',
          container: '#metric3',
          query: {
            metrics: ['visits'],
            collection: 'visits'
          }
        },
        table: {
          container: '#table',
          type: 'table',
          caption: '',
          pickers: {
            primary: {
              enabled: true,
              caption: 'Primary dimension...',
              allowRemove: false
            },
            add_dimension: {
              enabled: true,
              caption: 'Add dimension...',
              allowRemove: false,
              allowSelect: false
            },
            add_metric: {
              enabled: true,
              caption: 'Add metric...',
              allowRemove: false,
              allowSelect: false
            }
          },
          query: {
            dimensions: [
              {key: 'browser', description: 'The browser used'},
              {
                key: 'engine',
                description: 'The engine used'
              }
            ],
            metrics: [
              {key: 'moves', name: 'Mouse moves'},
              {key: 'clicks', collection: 'clicks'},
              {
                key: 'visits',
                collection: 'visits'
              }
            ],
            collection: 'moves',
            orderby: [
              ['moves', 'DESC']
            ]
          }
        },
        timeline: {
          container: '#timeline',
          type: 'timeline',
          chart: {
            chart: {
              height: 250,
              type: 'line'
            },
            yAxis: [
              {
                min: 0,
              },
              {
                min: 0,
              }
            ]
          },
          pickers: {
            main: {
              enabled: true
            },
            secondary: {
              enabled: true
            }
          },
          query: {
            dimensions: ['timestamp'],
            metrics: [
              {key: 'moves', name: 'Mouse moves'},
            ],
            collection: 'moves'
          }
        }
      }
    });
  });
</script>


</body>
</html>

Now you'll notice a full blown Canvas on your screen.

#Figure 1
### Before we get started
- We will be using our `localhost` with default configuration and settings.
- We will be running the `master` (stable) branch.
- We will be using MongoDB as our datastore with default configuration.
- We will be using the demo APIToken `apitoken-demo`.
###Installing Joola
```bash
$ mkdir /opt/joola
$ cd /opt/joola
$ npm install joola
```
###Running Joola
```bash
$ cd node_modules/joola
$ node joola.js
$ open http://localhost:8080
```
**All done.** At this point, Joola demo should be showing in your browser.
function randomIP() {
function getOctet() {
return Math.round(Math.random() * 255);
}
var ipaddress_string = getOctet()
+ '.' + getOctet()
+ '.' + getOctet()
+ '.' + getOctet();
return ipaddress_string;
}
var userid = joola.common.uuid();
var ip = randomIP();
var track = function () {
joola.insert('visits', {
user_agent: navigator.userAgent,
userid: userid,
ip: ip,
referrer: document.referrer,
visits: 1,
loadtime_ms: Math.floor(Math.random() * (120 - 10 + 1) + 10)
});
document.onclick = function (event) {
joola.insert('clicks', {
user_agent: navigator.userAgent,
userid: userid,
ip: ip,
referrer: document.referrer,
clicks: 1
});
};
var iTimeoutMoves = 0;
var moves = 0;
document.onmousemove = function (event) {
function pushMoves() {
if (moves > 0) {
var doc = {
user_agent: navigator.userAgent,
userid: userid,
ip: ip,
referrer: document.referrer,
moves: moves
};
joola.insert('moves', doc);
moves = 0;
}
}
clearTimeout(iTimeoutMoves);
moves++;
if (moves > 100)
pushMoves();
iTimeoutMoves = setTimeout(pushMoves, 100);
};
};
if (!joola.online) {
joola.on('ready', function () {
track();
});
}
else
track();
function randomIP() {
function getOctet() {
return Math.round(Math.random() * 255);
}
var ipaddress_string = getOctet()
+ '.' + getOctet()
+ '.' + getOctet()
+ '.' + getOctet();
return ipaddress_string;
}
var userid = joola.common.uuid();
var ip = randomIP();
joola.on('ready', function () {
try {
joola.insert('visits', {
timestamp: null,
browser: $.ua.browser.name,
device: $.ua.device.name,
engine: $.ua.engine.name,
os: $.ua.os.name,
userid: userid,
ip: ip,
referrer: document.referrer,
visits: 1,
loadtime_ms: Math.floor(Math.random() * (120 - 10 + 1) + 10)
}, {});
document.onclick = function (event) {
try {
joola.insert('clicks', {
timestamp: null,
browser: $.ua.browser.name,
device: $.ua.device.name,
engine: $.ua.engine.name,
os: $.ua.os.name,
userid: userid,
ip: ip,
referrer: document.referrer,
clicks: 1
}, {});
}
catch (ex) {
$('#log').append('<div>' + ex + '</div>');
console.log(ex);
}
};
var iTimeoutMoves = 0;
var moves = 0;
document.onmousemove = function (event) {
try {
function pushMoves() {
if (moves > 0) {
var doc = {
timestamp: null,
browser: $.ua.browser.name,
device: $.ua.device.name,
engine: $.ua.engine.name,
os: $.ua.os.name,
userid: userid,
ip: ip,
referrer: document.referrer,
moves: moves
};
joola.insert('moves', doc, {});
moves = 0;
}
}
clearTimeout(iTimeoutMoves);
moves++;
if (moves > 100)
pushMoves();
iTimeoutMoves = setTimeout(pushMoves, 100);
}
catch (ex) {
//$('#log').append('<div>' + ex + '</div>');
console.log(ex);
}
};
}
catch (ex) {
//$('#log').append('<div>' + ex + '</div>');
console.log(ex);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment