Wednesday, March 18, 2009

Display Google Visualization charts in a Grails application

I use Google Analytics to monitor several websites to see how many visitors come to the site, which goals are attained and more. But each time I have to log in to Google Analytics to see something. In a previous post we learned how to get data out of Google Analytics. In this post we will see how can use this data and display our own graphs on a web page. We are going to use Grails and Google Visualization API to achieve this. The Grails web application will have a web page to show an areachart with the number of visitors we get from the XML report from Google Analytics.

First things first. We start by creating a new Grails application and create an AnalyticsService and AnalyticsController. The service will be reading the XML report and return a map with number of visitors. The controller is responsible for displaying the web page and transforming the data from the service to valid input for the area chart.

$ grails create-app dashboard-web
$ cd dashboard-web
$ grails create-service analytics
$ grails create-controller analytics

Our service class is responsible for reading the XML report from Google Analytics. These reports are stored on disk in a directory. We only want the latests report file, so our service first needs to find the newest file in the directory. After that we use the XmlSlurper to read in the XML report. We can then easily extract the information we want from the report. Our service class looks like this:

import grails.util.Environment
import java.text.SimpleDateFormat

class AnalyticsService {
    // Automatically injected by Grails.
    def grailsApplication
    // Default parser to read date formats from Google Analytics XML.
    def dateParser = new SimpleDateFormat("EEEE, MMMM ddd, yyyy", Locale.US)
    def visitors() {
        // Directory with XML reports defined in Config.groovy.
        def dir = new File(grailsApplication.config.input.dir)
        // List all files, reverse sort them on date, 
        // and the top one is the newest file.        
        def xmlFile = dir.listFiles().sort{ file -> file.lastModified() }.reverse()[0]
        // Read XML data.
        def report = new XmlSlurper().parse(xmlFile)

        // Fill list with number of visitor per day.
        // Data is found in XML:
        // AnalyticsReport / Report / Graph / Serie / Point / Value|Label
        def visitors = []
        report.Report.Graph[0].Serie[0].Point.each { 
            visitors << [visits: new Integer(it.Value.text()), 
                               date: dateParser.parse(it.Label.text())] 
        return visitors

In our Config.groovy we must assign the property input.dir, which is used in line 12, the value for the directory name with all XML reports:

environments {
    development {
        input.dir = '/reports/analytics'
    production {
        input.dir = '/applications/dashboard/reports/repository'

Next we define the AnalyticsController class. The controller must display a web page and on the web page we want to include the area chart from Google Visualization API. And the controller must return the input for the area chart in the correct format for the Google Visualization API. We start by defining a new view for the controller. This view will be shown by the index method of the controller, so we create the file in grails-app/views/analytics/index.gsp. The contents of the file is:

        <title>Google Analytics</title>
        <script type="text/javascript" src="http://www.google.com/jsapi"></script>
        <script type="text/javascript">
          // Load the Visualization API and the areachart package.
          google.load('visualization', '1', {'packages':['areachart']});
          // Set a callback to run when the API is loaded.
          function initialize() {
            var query = new google.visualization.Query("${createLink(action: 'visitors')}");
          // Callback that creates and populates a data table, 
          // instantiates the pie chart, passes in the data and
          // draws it.
          function drawChart(response) {
            var data = response.getDataTable();    
            var chart = new google.visualization.AreaChart(document.getElementById('chart_div'));
            chart.draw(data, {width: 860, height: 240, legend: 'none', title: 'Visits'});
         <div id="chart_div"></div>        

Notice how we use createLink to define the action which will return the necessary data from our AnalyticsController. We return the data in JSON format. The rest of the code is mostly JavaScript to setup Google Visualizations. Now it is time to see the controller class:

import grails.converters.JSON;

class AnalyticsController {
    // Automatically injected by Grails.
    def analyticsService
    def index = { // Return default view index.gsp
    def visitors = {
        def map = [:]
        map.version = 0.5
        map.reqId = '0'
        map.status = 'ok'
        def columns = []
        columns << [label: 'Date', type: 'date']
        columns << [label: 'Visitors', type: 'number']

        def data = analyticsService.visitors()
        def rows = []
        def cells
        data.each {
            cells = []
            cells << [v: it.date] << [v: it.visits]
            rows << ['c': cells]
        def table = [cols: columns, rows: rows]
        map.table = table
        render "google.visualization.Query.setResponse(" + (map as JSON) + ")"

To support the correct date format we must change the default date format used by the JSON converter. We do this by changing the Config.groovy class and adding the following line:

grails.converters.json.date = "javascript"

Well that is all the code we need. We can start our Grails application, grails run-app, browse to http://localhost:8080/dashboard-web/analytics and get a nice chart in our web browser: