Recently I was looking for a way to make some snazzy bar charts from a Catalyst application. I have successfully used GD and GD::Graph before, but wanted to try something different this time around. After exploring several possibilities I decided to have a crack at using Open Flash Chart version 2 (OFC2). In this article, I discuss how I integrate Open Flash Chart into a Catalyst application.
Chart Data Source - The Model
A chart is only useful when it displays some useful data. Most of the time, this data will be coming from a database somehow, perhaps with some adjustment on the way out. In my case, the data source is a set of student test scores stored in a database. I use a DBIx::Class custom ResultSet classes to get at the data, and then munge it a bit to suit my needs. As an example, here is how I get at the averages for each of the five sections of the test for an arbitrary school and grade1.
sub school_section_averages {
my $self = shift;
my $admin_id = shift;
my $school_id = shift;
my $school_section_scores = $self->search_rs(
{
'me.admin_id' => $admin_id,
'student2class.school_id' => $school_id,
},
{
'select' => [
{ avg => 'me.percentage_score' }, 'me.section_id',
'section.name', 'section.position',
],
'as' => [
'average_score', 'section_id',
'section_name', 'section_position',
],
'join' => [ 'student2class', 'section' ],
'group_by' =>
[ 'me.section_id',
'section.name',
'section.position', ],
'order_by' => ['section.position'],
}
);
my @school_section_averages;
while ( my $section_average = $school_section_scores->next ) {
push @school_section_averages,
sprintf( "%.0f",
$section_average->get_column('average_score') );
}
return \@school_section_averages;
}
I do something similar to get at section averages by classroom teacher which we'll chart later.
Mediating the Data - The Controller
In a standard Catalyst controller I acquire the underlying data and ship it to a Private action for further processing2. This processing is where I create a Perl data structure that defines all the attributes of the chart. This data structure is then transformed into JSON which is the data format required by Open Flash Chart 23. Let's see what the structure looks like:
my $chart_data = {
'bg_colour' => '#ffffff',
'y_axis' => {
'tick_length' => 3,
'colour' => '#d000d0',
'stroke' => 1,
'max' => 100,
'grid_colour' => '#00ff00',
'offset' => 0,
'steps' => 10,
},
'x_axis' => {
'colour' => '#d000d0',
'stroke' => 1,
'labels' => {
'labels' => $labels,
'size' => 12,
},
'grid_colour' => '#00ff00',
'tick_height' => 10
},
'elements' => $elements,
'title' => {
'text' => $title,
'style' => '{
font-size: 20px;
color:black;
font-family: Verdana sans;
text-align: center;
}'
},
'y_legend' => {
'text' => 'Percent Correct',
'style' => '{color: #736AFF; font-size: 14px;}'
},
'tooltip' => {
'mouse' => 2,
'stroke' => 1,
'colour' => '#000000',
'background' => '#ffffff'
},
};
The three variables $labels, $elements and $title have to be constructed as well, but I've left them out for the sake of clarity. Suffice it to say that $elements is the most interesting as it defines the actuall chart values and chart styles. The $elements variable is actually an ArrayRef[HashRef] where the HashRef values could be a scalar or another ArrayRef. To help ease your head from spinning here is example of one element4:
{"alpha":"0.5","colour":"#9933CC","text":"Fergie Liscious",
"values":[93,84,92,89,88],"type":"bar","font-size":12}
Once I've packaged up the data into a Perl data structure, I can turn it into JSON with the help of the JSON module like so:
use JSON; my $json = new JSON; my $json_chart_data = $json->encode($chart_data);
Turning Data into a Chart - The View
At this point we have put the data into the format needed by OFC2. Now let's focus on building the view. The approach I take is to construct a web page via Template Toolkit (TT) that has the necessary Flash and Javascript library links along with the JSON data embedded in the page. Here's what the TT template for the page head looks like:
<script type="text/javascript" src="c.uri_for('/static/chart/swfobject.js') "></script>
<script type="text/javascript">
swfobject.embedSWF("c.uri_for('/static/chart/open-flash-chart-custom.swf') ", "ofc_averages_chart", "800", "400",
"9.0.0", "c.uri_for('/static/chart/expressInstall.swf') ");
</script>
<script type="text/javascript">
function open_flash_chart_data()
{
// read data
return JSON.stringify(data);
}
function findSWF(movieName) {
if (navigator.appName.indexOf("Microsoft")!= -1) {
return window[movieName];
} else {
return document[movieName];
}
}
var data = json_chart_data ;
Let's go through this template in parts:
json2.js
This is the Javascript JSON library used to stringify the JSON for OFC2. It uses var data which comes from the JSON data we encoded earlier in the Controller.
swfobject.js
Optional helper Javascript library used to reduce the amount of Javascript code needed in this template.
open-flash-chart-custom.swf
This Flash application is the center-piece of the charting process. It is a custom version of OFC25 It uses the open_flash_chart_data() function to consume the chart data and render the chart. The ofc_averages_chart parameter is the id of an HTML div where the chart will be placed in the web page.
Final View
If you'd like to see an example of the final output, you can go HERE.
As you can see, after this relatively small amount of work, we have a dynamic, flexible and shiny chart tracking the data we are interested in. Open Flash Chart is a nice and simple way to add a bit of 'wow' factor to a set of statistical data, and including it in your Catalyst app is a breeze.
Footnotes
1 The $admin_id represent a test given to a certain grade level at a certain time. e.g First Grade Spring 20009.
2 Yes, I would be wise to put my controller on a diet and move more of the data processing into the model.
3 There are some Perl modules that can assist this process but the mature one is for the older (non-JSON) format, while the newer one is not stable.
4 I'm actually showing the element in it's JSON form, but the Perl form is very close, just substitute ':' with ' => '.
5 I'm using a slightly modified version of OFC2. It includes a patch that provides keys to shapes. The patch came from DZ. This custom library was compiled with Flex SDK 3.3 on Linux. Bug me for another article if that interests you.




Would be great to see a complete code tree, or perhaps have it downloadable...
Apologies if it is... I just haven't seen it.