I've blogged a few times now about Google's Analytics Embed library. I really dig it as it makes working with their Analytics API pretty darn simple. It really opens up the ability to create interesting mashups and custom dashboards on your sites with just plain JavaScript. Recently, a reader asked me a question about how he could have more control over the data displayed by the library.
His question involved formatting of the "TABLE" display supported by the library. This is a format I had not used myself yet so I built up a quick demo (and as the reader supplied some code it made it a bit easier). Let's look at the code and then I'll show the result.
<!DOCTYPE html>
<html>
<head>
<title>Embed API Demo</title>
</head>
<body>
<!-- Step 1: Create the containing elements. -->
<section id="auth-button"></section>
<h2>RaymondCamden.com</h2>
<section id="timeline"></section>
<!-- Step 2: Load the library. -->
<script>
(function(w,d,s,g,js,fjs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(cb){this.q.push(cb)}};
js=d.createElement(s);fjs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';
fjs.parentNode.insertBefore(js,fjs);js.onload=function(){g.load('analytics')};
}(window,document,'script'));
</script>
<script>
gapi.analytics.ready(function() {
// Step 3: Authorize the user.
var CLIENT_ID = '818125206534-g1r0datdtu9serq2pf9cp5vkuih3h8pv.apps.googleusercontent.com';
gapi.analytics.auth.authorize({
container: 'auth-button',
clientid: CLIENT_ID,
userInfoLabel:""
});
// Step 5: Create the timeline chart.
var timeline = new gapi.analytics.googleCharts.DataChart({
reportType: 'ga',
query: {
'dimensions': 'ga:date',
'metrics': 'ga:sessions,ga:avgSessionDuration',
'start-date': '30daysAgo',
'end-date': 'yesterday',
'ids': "ga:31405"
},
chart: {
type: 'TABLE',
container: 'timeline'
}
});
gapi.analytics.auth.on('success', function(response) {
//hide the auth-button
document.querySelector("#auth-button").style.display='none';
timeline.execute();
});
});
</script>
</body>
</html>
I'm assuming you are somewhat familiar with my older posts, but if not, the basic idea here is that the Embed library will handle authentication and it provides rendering capabilities. You can see the DataChart call there handling both a query (what data to fetch) and how to render it (a table). Here is the result:
Nice, but what if you wanted more control over the rendering? Specifically, the user wanted to change the seconds column into a report that showed the minutes instead. Unfortunately, you don't get the ability to modify the format of the table. Fortunately, Google makes it easy to get the data manually and do whatever the heck you want. Let's look at an updated version of the script.
<!DOCTYPE html>
<html>
<head>
<title>Embed API Demo</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="moment.min.js"></script>
</head>
<body>
<div class="container">
<section id="auth-button"></section>
<h2>RaymondCamden.com</h2>
<div id="status"></div>
<table id="dataTable" class="table">
<thead><tr><th>Date</th><th>Sessions</th><th>Avg. Session Duration</th></tr></thread>
<tbody></tbody>
</table>
</div>
<script>
(function(w,d,s,g,js,fjs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(cb){this.q.push(cb)}};
js=d.createElement(s);fjs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';
fjs.parentNode.insertBefore(js,fjs);js.onload=function(){g.load('analytics')};
}(window,document,'script'));
</script>
<script>
var $tbody;
var $status;
var intl = false;
$(document).ready(function() {
if(window.Intl) intl = true;
$tbody = $("#dataTable tbody");
$status = $("#status");
$status.html("<i>Please stand by - loading data...</i>");
gapi.analytics.ready(function() {
var CLIENT_ID = '818125206534-g1r0datdtu9serq2pf9cp5vkuih3h8pv.apps.googleusercontent.com';
gapi.analytics.auth.authorize({
container: 'auth-button',
clientid: CLIENT_ID,
userInfoLabel:""
});
gapi.analytics.auth.on('success', function(response) {
//hide the auth-button
document.querySelector("#auth-button").style.display='none';
gapi.client.analytics.data.ga.get({
'ids': 'ga:31405',
'metrics': 'ga:sessions,ga:avgSessionDuration',
'start-date': '30daysAgo',
'end-date': 'yesterday',
'dimensions':'ga:date'
}).execute(function(results) {
$status.html("");
renderData(results.rows);
});
});
});
});
function formatDate(str) {
var year = str.substring(0,4);
var month = str.substring(4,6);
var day = str.substring(6,8);
if(intl) {
var d = new Date(year,month-1,day);
return new Intl.DateTimeFormat().format(d);
}
return month + "/" + day + "/" + year;
}
function formatNumber(str) {
if(intl) return new Intl.NumberFormat().format(str);
return str;
}
function formatTime(str) {
var dur = moment.duration(parseInt(str,10), 'seconds');
var minutes = dur.minutes();
var seconds = dur.seconds();
return minutes + " minutes and " + seconds + " seconds";
}
function renderData(data) {
var s = "";
for(var i=0;i<data.length;i++) {
s += "<tr><td>" + formatDate(data[i][0]) + "</td>";
s += "<td>" + formatNumber(data[i][1]) + "</td>";
s += "<td>" + formatTime(data[i][2]) + "</td></tr>";
}
$tbody.html(s);
}
</script>
</body>
</html>
The biggest change here is that now I ask for the data so that I can do whatever I want with it. You can see this in the gapi.client.analytics.data.ga.get
call. Once I have the data, I'm free to format it as I want. I decided to get a bit fancy.
First, I made use of Intl to format the dates and numbers nicely. This is a cool web standard (see my article here - Working with Intl) that is incredibly easy to use. It doesn't work on iOS yet, but luckily you can do 'snap to' with CSS in iOS which is far more important than internationalization.
To handle the minute display, I made use of the awesome MomentJS library. It has a duration
API that makes showing the parts of a span of time very easy. I'm not terribly happy with how I showed it - but obviously you could modify this to your needs. Here is the result.
There ya go. Hopefully this helps. I'd share an online version but it is currently tied to my web property so it wouldn't work for you. To be clear, you can easily change the property, or add support for letting the user select their property.