What's It Going to be then, Eh?
Thu 27 Mar 2008
Google Graphs - Detail

So here is a little more detail on how I did the weight graph for the footer of the blog.

At Google Code They describe the API for generating a .png image of a graph. This is done by sending a request with the proper parameters in the url. the url I'm using is:

"http://chart.apis.google.com/chart?cht=lc&chxt=x,y&chxl=0:" { A bunch of dates here } "1:|185|200|250|275& chd=t:{ A bunch of weights here }& chds=185,275& chs=600x100& chco=224499& chm=B,dcdcdc,0,0,0& chtt=Weight+Graph"

Easy as pie right? The url is a bit cumbersome but in short this is what it brakes down to:

http://char.apis.google.com/chart
This is the address where the chart application lives
cht
This is the Chart type. lc = line chart
chxt is the axis types
I have given the chart an X & Y axis (weight over time)
chxl is the access lables
for the x axis I'm going to use dates that are pulled from the blog and for the y axis I'm giving a static weight range from 185 to 275. Which I think are reasonable extremes in weight for me.
chd is the chart data. the t: means I'm passing the data as text
The data I'm passing is the weights pulled from my blog
chds is the data scale
The data scale is the range for the data points. By default the api expects all points to be between 0 and 100. I change that to 185 and 275.
chs is chart size
This allows you to set the dimensions of the .png image returned
chco is chart color
This sets the color of the line
chm is fill area
This sets area for the data plotted the capital B means fill everything under the line with the color specified
chtt is the chart title
This allows you to specify the Title to appear on the chart using '+' for spaces

Now where to get the data from? Well I use the blosxom blog engine for my blog. It uses a simple text file for each entry. For each daily entry I add my weight to the top. In fact I have written an emacs mode just for blogging that asks my weight and auto populates that info for me when I start the entry. So that makes grep an excellent tool for pulling out the data in those files. Originally I considered perl but that seemed a bit heavy handed. So to pull the data form the text files I used the following grep command

grep -o -P "[w|W]eight:?.?[0-9]{1,3}" *.txt
That says print out the file name and any text matching the pattern specified. The pattern says to Look for the word "weight"(case insensitive) with an optional colon and space afterward, and at least 1 digit but up to 3. So the out put looks like this: UPDATE: had to remove ths as it skewed the graph Now that is starting to look like something we can work with.

So now I want to strip away just the file name and date. awk is just the thing there.

awk -F: '{print $1 "," $3}'
This tells awk that the field separator is a colon and to print out fields 1 and 3. easy poesy!

Next I want to sort the data coming out to give it a bit of order in the graph. To accomplish this I pipe through sort. Next I pipe that output to a perl script I wrote to collect the data and craft the url.

so the final cron job looks like this:

#!/bin/bash
cd /home/abturet/blog/;
grep -o -P "[w|W]eight:?.?[0-9]{1,3}" *.txt |awk -F: '{print $1 "," $3}' \
| sort | ./weightgraph.pl;

So what is it that the perl script does???? Well, I'll tell you. But first I'll show you

#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw(strftime);
my ($url,$img_location,$dates,$weights,$weight_counter,$date_counter,@data,$date,$weight);
$weight_counter = 40;
$date_counter = 100;
$dates .= "|";
$img_location = "/home/abturet/public_html/images/weightgraph.png";
while(<>) {
($date,$weight) = m/(.*), ?(\d+)\n/;
$date= strftime "%Y-%m-%d", (localtime((stat($date))[9]));
s/^\s+/$2/;
if($weight_counter == 40){
$weights .= "$weight,";
$weight_counter = 0
}
if($date_counter == 100){
$dates .= "$date|";
$date_counter = 0
}
$weight_counter++;
$date_counter++;
}
$dates.= "$date|";
$weights .= $weight;
$weights =~ s/,$//;
$url = "\"http://chart.apis.google.com/chart?cht=lc&chxt=x,y&chxl=0:".$dates.
"1:|185|200|250|275&chd=t:$weights
&chds=185,275&chs=600x100&chco=224499&chm=B,dcdcdc,0,0,0&chtt=Weight+Graph\""
;
`curl -o $img_location -s $url `;

That's quite a mouthfull. What I do is as each line comes through I capture the two parts. The date and the weight. The date is actually the file name. So I call stat on the file and use the ninth element(the create date). I format that with strftime. Then I strip any white space from the weight.

The next to blocks make sure I don't send too much data. The api documentation says there is a url size limit of 3600 characters so I only grab every 40th weight and every 100th date. I have initialized them to the max value so that I have the first values from 2004 in the data set. After I've handled every date in the blog I tack on the last date and width so that the full range is represented.

Then I use those big strings of dates and weights to fill in the url and use curl to send the http request and save my nifty png file.

Sometimes it's fun to put your documentation out there in the public view. And now that it's here I can always find it if I want to make a change.


Home
Weight graph, 793 weigh-ins