My favorites | English | Sign in

Google Desktop APIs (Labs)

Gadget Usage Stats

This article was written and submitted by an external developer. The Google Desktop Team thanks Teodor Filimon for his time and expertise.


Teodor "Teo" Filimon, Gadget Developer
October 2008
"It's the question that drives us" - Trinity, The Matrix

Introduction

One of the most rewarding parts of making a gadget is seeing people use it and send over feedback about their experiences. Detailed statistics about the gadget's usage combined with user feedback helps you make improvements in the right direction. This article shows you how to fetch usage stats, such as number of users and session lengths, and discusses what data is appropriate to collect, keeping in mind the privacy of the user.

Information flow

The easiest way to understand the stat fetching process is to break it down into steps. Take a look at the diagram below:


Path of information
Figure 1: Information flow

Let's start the discussion from the end point: the best way to store our stats is through a database. A free and widely used database system is MySQL, and it is supported by most web hosting providers. Elements can be inserted in this database by a server-side script file (PHP for instance), which is activated by your gadget through the XMLHttpRequest object. Whenever a monitored event occurs (gadget installation, gadget closing, etc.), the gadget can report its usage through this process.

In the following sections, we use the Web TV & Radio gadget as a case study to examine every step in detail. The JavaScript code which comprises this methodology is also available in an open-source project.

Database

If you choose a MySQL database, a tool that can help you with its administration is phpMyAdmin and is also offered by most providers. Every element in this database will ultimately represent an action within the gadget, so it makes sense to have a field called 'Actions'.

So what actions can we monitor? For now, lets consider the ones that interest us the most:

  • the user installs your gadget: in this case, let's call the action 'install'. It helps us determine how many people installed your gadget.
  • the user runs the previously installed gadget: the action can be called 'confirm', because it confirms that the user is still keeping your gadget.

Now that we think about it, more fields are necessary to store extra information. Why? Well, we could have a 'Parameters' field which may contain the version of your gadget for the 'install' action or a user ID for the 'confirm' action. User IDs should be generated in an anonymous way, and we see how we can do this in the sections below. Another helpful field is called 'Moment', and it holds the time when an action occurred. Here is the necessary code to create the table structure we defined (you can run this code with phpMyAdmin on your desired database):

CREATE TABLE `Actions` (
   `Action` varchar(20) NOT NULL,
   `Parameters` text NOT NULL,
   `Moment` datetime NOT NULL,
   KEY `Action` (`Action`)
 ) ENGINE=MyISAM DEFAULT CHARSET=latin1;

At the end of the day when the system is up and running and people install your gadgets, you'll start seeing entries in the table:


Table
Figure 2: Stats sample

Server-side script

As I've mentioned earlier, we need a server-side script that is executed with certain parameters, connects to the database, and inserts an entry in the table. In this example we use PHP (check out the comments for details):

<?php

//----VARIABLE PART OF THE PHP
//location of the database host (given by providers):
$dbhost = '_THE_HOST_';
//username with which to log in the database:
$dbuser = '_USERNAME_';
//password:
$dbpass = '_PASSWORD_';
//database name:
$dbname = 'web_tv_radio_stats';
//insertion query into the right table, using the parameters which the PHP receives
$dbquery = "INSERT INTO `Actions` (Action,Parameters,Moment) VALUES ".$_POST['Action']."','".$_POST['Parameters']."','NOW()')";

//----CONSTANT PART OF THE PHP
//connecting to the database:
$conn = mysql_connect($dbhost, $dbuser, $dbpass);
mysql_select_db($dbname);
//running the query:
mysql_query($dbquery);
//closing the database:
mysql_close($conn);
//optional return message:
echo 'finished';
 ?>

Basically you connect to the database, run the query, and close the database:


Connection to the database
Figure 3: What actually happens within the PHP script

Initiating the process within the gadget

We acknowledged two aspects that can be of interest to us: how many people have install the gadget and how long they keep it running. We can automatically send these messages to the PHP page when the gadget is opened:

function _onOpen(){

	//setting default options
	//...
	options.putDefaultValue("firstInstall",true);
	options.putDefaultValue("UID",new Date().getTime());

	//getting a confirmation, and, if it's the case, an install notification
	var f=new Fetcher();
	if (options.getValue("firstInstall")){
		options.putValue("firstInstall",false);
		f.post(parameters.WebTvCentral,"install",parameters.version);
	}
	f.post(parameters.WebTvCentral,"confirm",options.getValue("UID"));
}

Notice how we generate a unique ID through the Date's object getTime() function. It returns the number of milliseconds passed since 1970. :-) Keep in mind that this is not actually an ID, but rather a key that allows us to group the confirmation messages and measure time lengths; everything is still perfectly anonymous. Extra actions which are triggered by users (options, settings, feature use, etc.) should be fetched only with explicit permission. This can easily be done using a checkbox in the options dialog.

You might be wondering what exactly the Fetcher object is. I called it that because it allowed me to fetch usage stats into the database (the object calls the PHP page). Its implementation is below:

function Fetcher(){
}

//...

Fetcher.prototype.post=function(URL,Action,Parameters){

	try{ 
		var xmlObject = new XMLHttpRequest(); 
	} 
	catch (error){
		var xmlObject = new ActiveXObject("Microsoft.XMLHTTP");
	}

	//setting up the parameters:
	params="Action="+Action+"&Parameters="+Parameters;
	xmlObject.open("POST",URL,true);
	xmlObject.setRequestHeader('Content-type','application/x-www-form-urlencoded');
	//xmlObject.setRequestHeader("Content-length", params.length);
	//xmlObject.setRequestHeader("Connection", "close");
	xmlObject.send(params);
}

Conclusion

There we have it! A perfectly flexible and expandable usage stats system to help us measure the evolution of a gadget.

Resources

Author Bio

Teodor Filimon

I'm a natural born programmer. My first contact with a techno-gadget was Star Trek (remember those cool sensors?). I used to fill up a whole room with drawings of them when I was only 3 years old. Generally, I find a lot of inspiration for intuitive interfaces in things with "star" in their names (like Star Wars, Stargate,... :-) . I like the border between interface and functionality the essence of a program, I think. Anyway, I'm a software engineering student now, and you can learn more about me and what I'm thinking and doing at my website or blog. My best gadgets are Web TV and Radio and DigiWatch.


Creative Commons License
This work is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.