The Curious Dev

Various programming sidetracks and shiny-object detours

Kimono and Geckoboard

A little rundown on how I’ve used Kimono and a little Json transformation to automatically retrieve and display various metrics from across the web on a Geckoboard dashboard.

Geckoboard

Geckoboard provide a nice way to display data on a dashboard and have a significant number of widgets that tie into various APIs but they also have a simple ability to display a number, a simple string of text or a chart of some kind when provided with a suitably structured JSON or XML payload. See more about the custom widgets here.

I’ve been a Geckoboard user for a number of years now, mostly just as a place to put up various metrics of things I’m somewhat vaguely keeping track of. Examples include my FitBit steps, share prices and the current temperature.

Geckoboard

Kimono

Kimono have only been around for a short while but they’ve certainly exploded onto the scene with a rather impressive offering. Check out their intro videos and their interactive demo and see how easy it is to create an API from a website, you may never need to write a custom, hacky web-scraper ever again :)

I’ve created several APIs with Kimono to extract data on recurring basis, in this case it’s the recent atmostpheric Carbon Dioxide as measured at Cape Grim by CSIRO. Every week Kimono will scrape the site and update it’s cached local copy of the value. The Cape Grimm data is monthly data and is updated monthly (in theory, but this seems to vary), so checking weekly with Kimono sounds best and it costs nothing either way.

There are various transports available to retrieve the data, including JSON, CSV and RSS/XML. I’ve done a lot of XML and CSV over the years but not so much JSON so figured it was time to have a play with that. Rather than hacky string chomping I’ve found a simple library to do the work for me in much the same way I would use XPATH if I was consuming XML.

Kimono to Geckoboard

JsonPath

JsonPath is a little library from Ben Hale that has implemented the concepts discussed in an article JsonPath - XPATH for JSON by Stefan Goessner. Invoking the library is incredibly easy and has followed along the lines of using the Java regex Pattern class.

Both Kimono and Geckoboard have simple data structures for their services, but they’re not exactly compatible, so I’ve used JsonPath to ease my translation between them.

The output from my Kimono Cape Grim CO2 service is something like this:

{
  "name": "metric_Cape_Grim_CO2_Monthly",
  "count": 1,
  "frequency": "weekly",
  "version": 2,
  "newdata": false,
  "lastrunstatus": "success",
  "lastsuccess": "Mon May 19 2014 18:21:44 GMT+0000 (UTC)",
  "nextrun": "Mon May 26 2014 18:21:44 GMT+0000 (UTC)",
  "results": {
    "collection1": [
      {
        "property1": "CO2: 393.7 (ppm) - February 2014"
      }
    ]
  }
}

The required input for a Geckoboard ”custom text widget” is like this:

{
  "item": [
    {
      "text": "CO2: 393.7 (ppm) - February 2014",
      "type": 0
    }
  ]
}

So my desired and rather simple mapping is something like this:

Kimono to Geckoboard

With JsonPath, the following simple expression is used to extract the required value from the Kimono JSON:

$.results.collection1[0].property1

Breaking the above expression down into steps:

  • the $ is the equivalent of the “root node” or // in XSL, so it selects the top level JSON
  • then we select the results property
  • then the first element in the collection1 collection
  • and finally the property1 property which contains our desired value

Note how the . breaks up the elements in the expression, essentially the same as the / in XSL. This expression above will give us the relevant value and we can then pass that into our next process of building the Geckoboard JSON.

This feature is implemented like this:

public String extractValueFromKimonoUsingJsonPath(String input) {
    JsonPath namePath = JsonPath.compile("$.results.collection1[0].property1");
    String geckoValue = namePath.read(input, String.class);
    System.out.println("Gecko:\n" + geckoValue);

    return geckoValue;
}

Building the Geckoboard JSON

Taking the value we’ve extract above, we then produce a Geckoboard compliant JSON payload using the Jackson ObjectMapper.

To produce JSON, all we need to do is populate a couple objects with the relevant data and then run the object tree through ObjectMapper. This creates a small overhead in forcing one to create a few basic object classes, but certainly seems the easiest way to produce compliant JSON. Alternatively, one could perhaps use a Groovy String as a template but that’s not exactly useful for anything other than likely this particular scenario with a new template needed in most situations.

Here’s the class that holds the actual “text” value:

public class GeckoboardItem {
    private String text;
    private int type;
    
    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }
}

Here’s the method to actually map the object tree to a JSON string:

public String buildGeckoboardJsonWithJackson(String input) {
    List<GeckoboardItem> items = new ArrayList<GeckoboardItem>();
    GeckoboardItem itemValue = new GeckoboardItem();
    itemValue.setText(input);
    itemValue.setType(0); //0 for no corner icon, 1 for error/red alert and 2 for info/grey alert
    items.add(itemValue);
    GeckoboardPayload<GeckoboardItem> payload = new GeckoboardPayload<GeckoboardItem>(items);

    String result = null;
    try {
        ObjectMapper mapper = new ObjectMapper();
        result = mapper.writeValueAsString(payload);
    } catch (JsonMappingException e) {
        System.out.println("Error: There was an issue when creating JSON string. " + e);
    } catch (JsonGenerationException e) {
        System.out.println("Error: There was an issue when creating JSON string. " + e);
    } catch (IOException e) {
        System.out.println("Error: There was an issue when creating JSON string. " + e);
    }

    System.out.println("Jackson:\n" + result);
    return result;
}

Deployment

For this particular little project I’ve simply added the new class into my existing Grails webapp that provides the Thrasher’s Wheat podcast feed, in time I may build out quite a few of these “glue” artefacts and roll a separate project.

For now, I’ve created a basic Grails controller that, in a synchronous manner, simply:

  • receives the Geckoboard request
  • calls out to the Kimono service
  • receives the Kimono service response
  • transforms the JSON into the relevant Geckoboard format
  • responds back to Geckoboard with the new data

The method in my controller really is as simple as this:

def co2_cape_grim() {
    String kimonoValue = kimonoToGeckoService.callKimono(Kimono.CO2_CAPE_GRIM, false)
    String value = kimonoToGeckoService.extractValueFromKimonoUsingJsonPath(kimonoValue)
    String result = kimonoToGeckoService.buildGeckoboardJsonWithJackson(value)
    render result
}

Hope this provides something useful to get the imagination going.

Comments

Included file 'facebook_like.html' not found in _includes directory