fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2010-07-29 05:56 am

[dw-free] Create a graphical front-end for the statistics system

[commit: http://hg.dwscoalition.org/dw-free/rev/a71463d5d873]

http://bugs.dwscoalition.org/show_bug.cgi?id=2699

Add module that creates graphs for the statistics system; add a script with
usage examples. Available graphs are: pie charts, bar charts, bar chart with
multiple bars, line graphs.

Patch by [personal profile] anarres.

Files modified:
  • bin/dev/graphs_usage.pl
  • cgi-bin/DW/Graphs.pm
  • etc/clrs.txt
--------------------------------------------------------------------------------
diff -r be9772bf3332 -r a71463d5d873 bin/dev/graphs_usage.pl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/dev/graphs_usage.pl	Wed Jul 28 22:56:28 2010 -0700
@@ -0,0 +1,110 @@
+#!/usr/bin/perl
+#
+# dw/bin/dev/graphs_usage.pl - Graphs usage examples
+#
+# Authors:
+#      Anarres <anarres@dreamwidth.org>
+#
+# Copyright (c) 2010 by Dreamwidth Studios, LLC.
+#
+# Gives examples of usage of the DW::Graphs module to make pie, bar, and line
+# graphs. This script is only for the purpose of showing how the Graphs module
+# works, normally the Graphs module would be used by graph image controller modules
+# such as DW::Controller::PaidAccountsGraph, not by a standalone script.
+#
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself. For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+
+use lib "$ENV{LJHOME}/cgi-bin";
+use DW::Graphs;
+use strict;
+use warnings;
+
+# Generate a pie chart pie.png ------------------------------------------------>
+my $pie_ref = {
+    "label 1" => 1,
+    "label 2" => 0.05,
+    "label 3" => 0.07,
+    "label 4" => 1.5,
+    "label 5" => 0.12,
+};
+
+# Make the graph
+my $gd = DW::Graphs::pie( $pie_ref );
+
+# Print the graph to a file
+open(IMG, '>pie.png') or die $!;
+binmode IMG;
+print IMG $gd->png;
+
+
+# Generate a bar chart bar.png ------------------------------------------------>
+my $bar_ref = [ 13.377, 15.9, 145.67788, 123.1111, 0.1, 44.03455, 33.3, 43, 123 ];
+
+# Labels - one label per bar. If labels are not wanted pass empty strings.
+# Optinally put "\n" in front of every 2nd label to stop them crowding each-other.
+my $bar_labels = [ ( "label 1", "\nlabel 2", "label 3", "\nlabel 4", "label 5",
+    "\nlabel 6", "label 7", "\nlabel 8", "label 9" ) ];
+
+# Make the graph
+my $bar_gd = DW::Graphs::bar( $bar_ref, $bar_labels, 'x-axis label', 'y-axis label' );
+
+# Print the graph to a file
+open(IMG, '>bar.png') or die $!;
+binmode IMG;
+print IMG $bar_gd->png;
+
+
+# Generate a bar chart bar2.png with two datasets ----------------------------->
+
+# You can have any number of datasets - here there are two
+my @values1 = ( 7243, 15901, 26188 );
+my @values2 = ( 4243, 12901, 11188 );
+
+# Dataset names to distinguish the datasets, used in the legend. The number
+# of dataset names must be the same as the number of datasets!
+my $names_ref = [ ( "Dataset 1",  "Dataset 2" ) ];
+
+# labels - one label per bar. The number of labels must be the same as the
+# number of values per dataset. If labels are not wanted pass empty strings.
+my @bar2_labels = ( "Bar 1", "Bar 2", "Bar 3" );
+
+# Package the input
+my $bar2_input = [ [@bar2_labels], [@values1], [@values2] ];
+
+# Make the graph
+my $bar2_gd = DW::Graphs::bar2( $bar2_input, 'x-axis label', 'y-axis label', $names_ref);
+
+# Print the graph to a file
+open(IMG, '>bar2.png') or die $!;
+binmode IMG;
+print IMG $bar2_gd->png;
+
+
+# Generate a line graph lines.png --------------------------------------------->
+
+# Define labels to go along the horizontal axis under the graph.
+# If you don't want labels, use empty strings
+my @line_labels = ( "1st","2nd","3rd","4th","5th","6th","7th", "8th" );
+
+# Define the datasets - each dataset will form one line on the graph
+# Each dataset should have the same length as the number of labels
+my @dataset1 = qw( 1900 2035 2078 2140 2141 2200 2460 2470 2576 );
+my @dataset2 = qw( 871 996 990 1058 1102 1162 1105 1150 );
+my @dataset3 = qw( 200 360 370 471 496 690 758 802 );
+
+# Names for the datasets, to go in the graph legend
+my $line_names = [ "1st dataset", "2nd dataset", "3rd dataset" ];
+
+# Put the data in a format that the Graphs module can deal with
+my $line_ref = [ [@line_labels], [@dataset1], [@dataset2], [@dataset3] ];
+
+# Make the graph
+my $line_gd = DW::Graphs::lines( $line_ref, 'x-axis label', 'y-axis label', 
+   $line_names );
+
+# Print the graph to a file
+open(IMG, '>lines.png') or die $!;
+binmode IMG;
+print IMG $line_gd->png;
diff -r be9772bf3332 -r a71463d5d873 cgi-bin/DW/Graphs.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/DW/Graphs.pm	Wed Jul 28 22:56:28 2010 -0700
@@ -0,0 +1,232 @@
+#!/usr/bin/perl
+#
+# DW::Graphs - creates graphs for the statistics system
+#
+# Authors:
+#      Anarres <anarres@dreamwidth.org>
+#
+# Copyright (c) 2010 by Dreamwidth Studios, LLC.
+#
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself. For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+
+package DW::Graphs;
+
+use strict;
+use warnings;
+use lib "$ENV{LJHOME}/cgi-bin";
+require 'ljlib.pl'; 
+use GD::Graph;
+use GD::Graph::bars;
+use GD::Graph::hbars;
+use GD::Graph::pie;
+use GD::Graph::lines;
+use GD::Text;
+use GD::Graph::colour;
+GD::Graph::colour::read_rgb("$LJ::HOME/etc/clrs.txt")
+    or die "cannot read colours";
+
+# Generates a pie chart. The argument is a hashref of labels and values.
+# Returns graph object $gd which can be printed with the command: print $gd->png;
+# See ~/dw/bin/dev/graphs_usage.pl for more detailed usage information.
+sub pie {
+    my ( $pie_ref ) = @_;
+
+    # Sort the key-value pairs of %$pie_ref by value:
+    # @pie_labels is the keys and @pie_values is the values
+    my @pie_labels = sort { $pie_ref->{$a} cmp $pie_ref->{$b} } keys %$pie_ref;
+    my @pie_values = map { $pie_ref->{$_} } @pie_labels;
+
+    # Package the data in a way that makes GD::Graph happy
+    my $pie = [ [@pie_labels], [@pie_values] ];
+
+    # Create graph object
+    my $graph = GD::Graph::pie->new( 300, 300 );
+    $graph->set(
+        transparent    => 0,      # Set this to 1 for transparent background
+        accentclr      => 'nearly_white', 
+        start_angle    => 90,     # Angle of first slice of pie, 0 = 6 o'clock
+        suppress_angle => 5,      # Smaller slices than this have no labels
+        bgclr          => 'background',
+        dclrs          => [ qw( pie_blue pie_orange pie_bluegreen pie_green
+                           pie_yellow pie_pink ) ], 
+        labelclr       => '#000000',
+        valuesclr      => '#000000',
+        textclr        => 'dw_red',
+        '3d'           => 0,
+    ) or die $graph->error;
+
+    # FIXME: make the pathnames and font sizes configurable
+    $graph->set_title_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 14);
+    $graph->set_value_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+    $graph->set_label_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+
+    my $gd = $graph->plot( $pie ) or die $graph->error;
+    return $gd;
+} 
+
+# Generates a bar chart. Arguments: a ref to an array of values, a ref to an
+# array of labels, title, x-axis label, y-axis label. # Returns graph object
+# $gd which can be printed with the command: print $gd->png; see 
+# graphs_usage.pl for more detailed usage information.
+sub bar {
+    my ( $values_ref, $labels_ref, $xlabel, $ylabel ) = @_;
+
+    # Package the input as required by GD Graph
+    my $input_ref = [ $labels_ref, $values_ref ];
+
+    # Create graph object
+    my $graph = GD::Graph::bars->new(500, 350);
+
+    # Graph settings
+      $graph->set( 
+            x_label         => "\n$xlabel",
+            y_label         => $ylabel,
+            show_values    => 1,
+            values_space   => 1, # Pixels between top of bar and value above
+            b_margin       => 20, # Bottom margin (makes space for labels)
+            bar_spacing    => 50, # Higher value = thinner bars
+
+            bgclr          => 'background',
+            fgclr          => 'white',
+            boxclr         => '#f4eedc', # Shaded-in background
+            long_ticks     => 1,         # Background grid lines
+            accentclr      => 'lgray',   # Colour of grid lines
+            accent_treshold => 500,      # Get rid of outline around bars
+
+            labelclr       => '#000000',
+            axislabelclr   => '#000000',
+            legendclr      => '#000000',
+            valuesclr      => '#000000',
+            textclr        => 'dw_red',
+
+            transparent    => 0,         # 1 for transparent background
+            dclrs          => [ qw( pie_blue pie_orange pie_bluegreen pie_green
+                               pie_yellow pie_pink ) ],
+      ) or die $graph->error;
+
+    # FIXME: make the pathnames and font sizes configurable
+    $graph->set_title_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 14);
+    $graph->set_x_label_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 12);
+    $graph->set_y_label_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 12);
+    $graph->set_x_axis_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+    $graph->set_y_axis_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+    $graph->set_values_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+
+    # Make the graph
+     my $gd = $graph->plot( $input_ref ) or die $graph->error;
+    return $gd;
+} 
+
+# Generates a bar chart with two or more sets of data represented by each bar.
+# Arguments: a reference containing labels and datasets, x-axis label,
+# y-axis label, ref to array of names for datasets. Returns graph object 
+# $gd which can be printed with the command: print $gd->png; see 
+# ~/dw/bin/dev/graphs_usage.pl for more detailed usage information.
+sub bar2 {
+    my ( $ref, $xlabel, $ylabel, $names_ref ) = @_;
+
+    # Create graph object
+    my $graph = GD::Graph::bars->new(500, 350);
+
+    # Graph settings
+    $graph->set( 
+        x_label         => "\n$xlabel",
+        y_label         => $ylabel,
+        show_values    => 1,
+        values_space   => 1, # Pixels between top of bar and value above
+        b_margin       => 20, # Bottom margin (makes space for labels)
+        bar_spacing    => 50, # Higher value = thinner bars
+        legend_placement => 'RC',      # Right centre
+        cumulate => 'true',  # Put the two datasets in one bar
+
+        shadowclr      => 'background',
+        bgclr          => 'background',
+        fgclr          => 'white',
+        boxclr         => '#f4eedc', # Shaded-in background 
+        long_ticks     => 1,         # Background grid lines
+        accentclr      => 'white',   # Colour of grid lines
+        transparent    => 0,         # 1 for transparent background
+        #accent_treshold => 500,      # Get rid of outline around bars
+
+        labelclr       => '#000000',
+        axislabelclr   => '#000000',
+        legendclr      => '#000000',
+        valuesclr      => '#000000',
+        textclr        => 'dw_red',
+
+        dclrs          => [ qw( pie_blue pie_orange pie_bluegreen pie_green
+                           pie_yellow pie_pink ) ],
+    ) or die $graph->error;
+
+    # FIXME: make the pathnames and font sizes configurable
+    $graph->set_title_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 14);
+    $graph->set_x_label_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 12);
+    $graph->set_y_label_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 12);
+    $graph->set_x_axis_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+    $graph->set_y_axis_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+    $graph->set_values_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+    $graph->set_legend_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+
+    # Set legend
+    $graph->set_legend( @$names_ref );
+
+    # Make the graph
+    my $gd = $graph->plot( $ref ) or die $graph->error;
+    return $gd;
+}
+
+# Generates a line graph. Arguments: a reference containing labels and datasets,
+# x-axis label, y-axis label, ref to array of names for datasets. Returns
+# graph object $gd which can be printed with the command: print $gd->png; see
+# ~/dw/bin/dev/graphs_usage.pl for more detailed usage information.
+sub lines {
+    my ( $data_ref, $xlabel, $ylabel, $data_names ) = @_;
+
+    # Create new Graph object $graph 750px by 320 px
+    my $graph = GD::Graph::lines->new(750, 320);
+
+    # Graph settings:
+    $graph->set( 
+        x_label       => $xlabel,
+        y_label       => $ylabel,
+        show_values   => 0,
+        transparent   => 0,            # Set this to 1 for transparent background
+        line_width    => 1,
+        long_ticks    => 1,            # Background grid lines
+        line_width    => 4,            # Line width in pixels
+        legend_placement => 'RC',      # Right centre
+
+        bgclr         => 'background',
+        fgclr         => 'white',
+        boxclr        => '#f4eedc',    # Shaded-in background colour
+        accentclr     => 'lgray',
+
+        labelclr       => '#000000',
+        axislabelclr   => '#000000',
+        legendclr      => '#000000',
+        valuesclr      => '#000000',
+        textclr        => 'dw_red',
+        dclrs                 => [ qw( solid0 solid1 solid2 solid3 solid4 ) ],
+        ) or die $graph->error;
+
+    $graph->set( line_types => [1, 2, 3, 4] ); # 1:solid 2:dash 3:dot 4:dot-dash
+
+    # FIXME: make the pathnames and font sizes configurable
+    $graph->set_title_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 14);
+    $graph->set_legend_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 12);
+    $graph->set_x_label_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 12);
+    $graph->set_y_label_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 12);
+    $graph->set_x_axis_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+    $graph->set_y_axis_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+    $graph->set_values_font("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", 10);
+
+    # Set legend
+    $graph->set_legend( @$data_names );
+
+    # Make the plot
+    my $gd = $graph->plot( $data_ref ) or die $graph->error;
+    return $gd;
+}
+1;
diff -r be9772bf3332 -r a71463d5d873 etc/clrs.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/etc/clrs.txt	Wed Jul 28 22:56:28 2010 -0700
@@ -0,0 +1,32 @@
+# dw/etc/clrs.txt
+#
+# Defines colours to be used by DW::Graphs
+#
+# Authors:
+#      Anarres <anarres@dreamwidth.org> 
+#
+# Copyright (c) 2010 by Dreamwidth Studios, LLC.
+#
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself. For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+
+# Solid colours 
+90  132 0         solid0
+146 13  27        solid1
+17  6   82        solid2
+229 187 27        solid3
+13  61  37        solid4
+
+# Colours for pie charts
+126 175 210      pie_blue
+243 151 62       pie_orange
+119 203 162      pie_bluegreen
+165 198 64       pie_green
+237 211 68       pie_yellow
+215 139 169      pie_pink
+
+# Other
+247 247 247      background
+197 53  60       dw_red
+248 248 248      nearly_white
--------------------------------------------------------------------------------