Parsing Your Data from the Habitica API

I've been using the Habitica Habit Tracker recently and it's been a really good app to handle all sorts of history tracking. How much water do I drink? How many cigarettes do I smoke per day? Hint: recently very few.

Fortunately Habitica also has an API that lets you work with your entire history programmatically and to display it in every chart you can imagine.

The chart I wanted was to look at the history of how many cigarettes I had smoked per day. If you're a smoker you probably know that it's more of a day by day thing how much energy you have to fight the urge of simply going for another smoking break or lighting another cigarette.

Downloading History Data from Habitica

To get your data from Habitica, you'll have to visit the Habitica API docs or just enter https://habitica.com/export/history.csv and download the CSV file.

You can open it straight away with Libre Office, Excel, a text editor, etc:

You'll be able to see your habit history data and the columns: Task Name, Task ID, Task Type, Date and Value.

Parsing your History data into JSON

I needed the Data in another shape, so I wrote a short Node.js script that will transform the CSV file to JSON like this:

[
  {"date":"2018-02-01","amount":0},
  {"date":"2018-02-02","amount":0},
  {"date":"2018-02-03","amount":2},
  {"date":"2018-02-04","amount":2}
]

This is the format that I need to build a timeseries chart out of it. This means we have to figure out the amounts that I've pressed the - button for every date, but not the specific time stamp.

To parse the csv I use a node module called csv-parse and to parse the date I use moment. To sort the dataset by date, I use lodash.

The code that does all of this, can be found below:

parse.js
const fs = require('fs');
const _ = require('lodash');
const parse = require('csv-parse');
const moment = require('moment');

const fileName = './data/habitica-tasks-history.csv';

var parsed = {};

var input = fs.readFileSync(fileName, 'utf-8');

parse(input, {
  columns: true
}, processData);

function processData(err, output) {
  var previousValue = 0;

  output = _.sortBy(output, ['Date']);

  for (var i = 0; i < output.length; i++) {
    if (output[i]['Task Name'] === 'Chew / Smoke') {
      var date = moment(output[i]['Date']);
      var formattedDate = date.format('YYYY-MM-DD');
      // if the value is less, smoking has occurred
      var current = Math.abs(output[i]['Value'])
      var previous = Math.abs(previousValue)

      if (typeof parsed[formattedDate] === 'undefined') {
        parsed[formattedDate] = 0;
      }

      if (current > previous) {
        parsed[formattedDate]++;
      }

      previousValue = output[i]['Value'];
    }
  }

  var dataEx = [];
  for (const [k, v] of Object.entries(parsed)) {
    dataEx.push({
      date: k,
      amount: v
    })
  }

  var json = JSON.stringify(dataEx);
  fs.writeFileSync('./public/smoking.json', json, 'utf8');
}

and can be run with:

node parse.js

assuming your downloaded csv file is in data/habitica-task-history.csv.

Caveat: +/- Habit Parsing into Amounts / Counts

Sadly, the Habitica API does not tell us if we pressed the + or -, but gives us an overall value, so we have to compare it to the previous value, after we have sorted the dataset by Date:

var current = Math.abs(output[i]['Value'])
var previous = Math.abs(previousValue)

if (typeof parsed[formattedDate] === 'undefined') {
  parsed[formattedDate] = 0;
}

if (current > previous) {
  parsed[formattedDate]++;
}

previousValue = output[i]['Value'];
}

If your task does not have a positive and a negative value, you can simplify this part by just iterating the value for that date.

Displaying Charts with Taucharts

I've been exploring a couple of different charting libaries and Taucharts has been a worthy competitor. As many others it's based on D3.js and will yield a wide variety of chart types.

This below is the HTML file that loads the charts library and the index.js file:

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/taucharts@1/build/production/tauCharts.min.css">
  <script src="//cdn.jsdelivr.net/d3js/3.5.17/d3.min.js" charset="utf-8"></script>
  <script src="//cdn.jsdelivr.net/npm/taucharts@1/build/production/tauCharts.min.js" type="text/javascript"></script>
  <script src="index.js"></script>
  <title>cigarettes</title>
  <style>
  #smoking {
    height: 70vh;
  }</style>
</head>
<body>
  <div id="smoking"></div>
</body>
</html>

This is our JavaScript file that will make a request to our generated JSON that we have transformed out of the CSV data and feed the resulting JSON to the charting library:

index.js
document.addEventListener("DOMContentLoaded", init);

function init(){
  var url = '/smoking.json';

  fetch(url, {
    method: 'GET',
  })
    .then(res => res.json())
    .catch(error => console.error('Error:', error))
    .then(function(response){
      console.log(response);
      renderChart(response)
    });
}

function renderChart(data) {
  var chart = new tauCharts.Chart({
    type: 'bar',
    x: 'date',
    y: 'amount',
    data: data
  });
  chart.renderTo('#smoking');
}
[{"date":"2018-02-01","amount":0},{"date":"2018-02-02","amount":0},{"date":"2018-02-03","amount":2},{"date":"2018-02-04","amount":2}]
Tagged with: #csv #Habitica #JSON #Taucharts

Thank you for reading! If you have any comments, additions or questions, please tweet or toot them at me!