This weekend, in my spare time, I've finally achieved one thing I wanted since some time: Fully automating the Site Stats of Kartones.Net.
Previously, I had to manually update the page with the current data each month, plus exporting some Google Analytics reports in PDF. As computers are supposed to exist to make our lives easier (although for developers is more of the opposite actualy :), I decided to use the Google Analytics Data Export API (which as usual uses GData for retrieval of the information) and ASP.NET Chart Controls to create an automated and live stats page.
As I had in mind publishing the class under my KartonesNet CS2007 Addon Pack, I've made some things in the code specifically to make it more customizable by anyone. But let's get into how I've made it and how it works...
Without entering into details of how GData works, as usual working with it when being authenticated we will make http request to specific urls and retrieve XML contents (in which we will have a list of <entry> elements). The requests have this format:
https://www.google.com/analytics/feeds/data?ids=ga:_ProfileID_&dimensions=_Dimensions_&metrics=_Metrics_&sort=_OrderBy_&start-date=_StartDate_&end-date=_EndDate_&prettyprint=true&max-results=_MaxResults_
As we can see, the first parameter is a Profile ID, obtained after authentication. My API supports only ClientLogin
(user & password, as usual *), but there are other methods. After authentication, we will receive an authorization token, which we will need to always specify in the headers of our Http Requests:
request.Headers.Add("Authorization: GoogleLogin auth=" + sessionToken);
The profile ID represents the "site" we want to get data about. If you don't know the profile ID of your site, or only have one, don't bother with it, if you call the API Login() method without ProfileID, it will get the first available one.
Login with the API can't be easier:
GoogleAnalyticsAPI googleAnalytics = new GoogleAnalyticsAPI();
bool loginSuccess = googleAnalytics.Login(user, password);
if (loginSuccess)
{
...
After authentication, we can make GData requests to gather data, by specifying dimensions and metrics. As the documentation is pretty self-explaining I won't enter into details, just mentioning that it is important to check the valid combinations of them because you can have errors with given combinations.
I have recreated for Kartones.Net some basic reports, like visits/pageviews, visitor OS, browser and language config, or a ‘Top 20 content pages'. There is a lot of room for imagination here, specially if you perform operations with the returned data.
The API calls are very simple:
public string GADataCall(string[] Dimensions, string[] Metrics, string OrderBy);
I have not made enumerations for dimensions/metrics just in case Google extends it (and because they are a lot and I only needed a few for my reports), and the OrderBy parameter can be set to null/string.Empty if you don't want ordering.
Note: To specify a descending order, prepend a minus to the field, e.g.: -ga:pageviews
A few important things about the API calls to gather data:
- The date range of data to request is defined with the StartingDate and EndingDate properties of the GoogleAnalyticsAPI class. If not specified, they will set to retrieve from the past month up to yesterday (same day).
- The number of results is specified too with the property MaxResults. By default is 50.
- If running under ASP.NET, call results are cached automatically for the number of seconds specified in CacheExpirationTime (default: 300 secs).
- And most important, returned data from this call is not XML, but a formatted string. ¿How do we format that string?...
One thing I wanted is to allow customization of the results while keeping them small and light. My scenario was the web, but even in there I needed more than only "table formatting" with <tr> and <td>, so I created this small interface:
namespace KartonesNet.APIs
{
/// <summary>
/// Interface for implementing a Google Analytics data formatter
/// </summary>
public interface IGoogleAnalyticsDataFormatter
{
/// <summary>
/// Tag to inject before each element
/// </summary>
/// <returns>Tag</returns>
string PreElementTag();
/// <summary>
/// Tag to inject before each field of an element
/// </summary>
/// <returns>Tag</returns>
string PreFieldTag();
/// <summary>
/// Tag to inject after each field of an element
/// </summary>
/// <returns>Tag</returns>
string PostFieldTag();
/// <summary>
/// Tag to inject after each element
/// </summary>
/// <returns>Tag</returns>
string PostElementTag();
}
}
Very simple and customizable (you can simply return string.Empty on those values you don't want to use). I provide three DataFormatters with the API:
- GoogleAnalyticsCommaDataFormatter: A simple concatenation of fields via commas. Very useful for manipulating later data (with a simple string.split(‘,'))
- GoogleAnalyticsTableDataFormatter: Simple table formatter, adds <tr><td></td></tr> structure. Note: It adds Community Server css classes too, so I recommend you to create another one without custom css classes.
- GoogleAnalyticsTableWithLinkDataFormatter: The same as the previous one, but converting the first field of each data element/entry into a HREF tag (for the Top 20 content pages) and prepending the host url (‘http://Kartones.net') to it.
This is an example call that uses all stuff (not much anyway):
googleAnalytics.DataFormatter = new GoogleAnalyticsTableDataFormatter();
googleAnalytics.MaxResults = 12;
osGAData = googleAnalytics.GADataCall(
new string[] { "ga:operatingSystem" },
new string[] { "ga:pageviews" },
"-ga:pageviews");
And not much else to add... Plugging the gathered data into the ASP.NET Charting Control as usual took more time for visual setup than for logic itself but was really easy, and as it has caching too, the resulting stats page loads pretty fast on subsequent calls (and not slow on the first one).
Here is a screenshot of how all this looks once finished:
All that remains is grabbing the component and making your own reports!
I hope the API proves useful, feel free to drop a comment with suggestions, problems or just thoughts about it :)
Tags: Kartones.Net