1 # Blosxom Plugin: postgraph -*- perl -*-
2 # Author: Todd Larason jtl@molehill.org and Nilesh Chaudhari http://nilesh.org/
4 # Blosxom Home/Docs/Licensing: http://www.raelity.org/blosxom
5 # Categories plugin Home/Docs/Licensing:
6 # http://molelog.molehill.org/blox/Computers/Internet/Web/Blosxom/Graph/
7 # parts Copyright (c) 2002 Nilesh Chaudhari http://nilesh.org/
11 # --- Configuration Variables ---
12 $destination_dir ||= ''; # must be configured
13 $graph_start_day ||= "19000101";
14 $graph_num_bars ||= 24;
16 $graph_height ||= 100;
17 $barcolor ||= "#f5deb3"; # the bars themselves
18 $bordercolor ||= "#83660f"; # the borders of the bars
19 $outlinecolor ||= "#83660f"; # the outline of the box around the graph
20 $boxcolor ||= "#fffbf0"; # the inside of the box
21 $textcolor ||= "#4f0000"; # the various text bits
25 $bt_linecolor ||= '#ffffff';
26 $bt_textcolor ||= '#757575';
27 $bt_fillcolor ||= '#757575';
28 $bt_bordercolor ||= '#757575';
29 $bt_padding = 5 unless defined $bt_padding;
30 $bt_show_text = 1 unless defined $bt_show_text;
32 $debug_level = 2 unless defined $debug_level;
33 # ------------------------------------------------------------
38 use GD::Graph::colour qw/:convert :colours/;
40 use vars qw/$destination_dir $graph_start_day $graph_num_bars $graph_width
41 $graph_height $barcolor $bordercolor $outlinecolor $boxcolor $textcolor
42 $bt_width $bt_height $bt_linecolor $bt_textcolor $bt_fillcolor
43 $bt_bordercolor $bt_padding $bt_show_text $debug_level/;
45 my $package = "postgraph";
46 my $timestamp_file = "$blosxom::plugin_state_dir/.$package.timestamp";
50 my ($level, @msg) = @_;
52 if ($debug_level >= $level) {
53 print STDERR "$package debug $level: @msg\n";
61 $max = $_ if $_ > $max;
66 return int($_[0] + .5);
71 my ($r, $g, $b) = ($hex =~ m!\#(..)(..)(..)!);
72 return (hex($r),hex($g),hex($b));
76 my $secs_per_bucket = 86400 / $graph_num_bars;
77 my $mins_per_bucket = 1440 / $graph_num_bars;
78 my $buckets_per_hour = $graph_num_bars / 24;
81 my ($hour, $min, $sec) = @_;
82 my $time = $hour * 3600 + $min * 60 + $sec;
83 my $bucket = $time / $secs_per_bucket;
84 $bucketcount[$bucket]++;
89 for (my $hour = 0; $hour < 24; $hour++) {
90 for (my $min; $min < 60; $min += $mins_per_bucket) {
91 push @labels, $hour; # sprintf("%d:%02d", $hour, $min);
94 my $graph = GD::Graph::bars->new($graph_width, $graph_height);
96 # title => 'Posts per hour of day',
97 y_max_value => max(@bucketcount) + 2,
98 x_label_skip => $buckets_per_hour * 4,
99 # bgclr => add_colour($bgcolor), # does nothing?
100 fgclr => add_colour($outlinecolor),
101 boxclr => add_colour($boxcolor),
102 labelclr => add_colour($textcolor),
103 axislabelclr => add_colour($textcolor),
104 legendclr => add_colour($textcolor),
105 valuesclr => add_colour($textcolor),
106 textclr => add_colour($textcolor),
107 dclrs => [add_colour($barcolor)],
108 borderclrs => [add_colour($bordercolor)],
110 $#bucketcount = $graph_num_bars;
111 my $gd = $graph->plot([\@labels, \@bucketcount]);
112 open IMG, "> $destination_dir/graph.png";
116 debug(2, "build graph");
119 # blogtimes directly derived from BLOGTIMES by Nilesh Chaudhari
120 # -- http://nilesh.org/mt/blogtimes/
122 my @monthname=('null', 'JANUARY','FEBRUARY','MARCH','APRIL','MAY','JUNE',
123 'JULY','AUGUST','SEPTEMBER','OCTOBER','NOVEMBER','DECEMBER');
127 my ($hour, $min) = @_;
128 push @bt_entry_times, $hour*60 + $min;
130 sub build_blogtimes {
131 my ($year, $month) = @_;
132 my $txtpad = $bt_show_text ? gdTinyFont->height : 0;
133 my $scale_width = $bt_width + ($bt_padding*2);
134 my $scale_height = $bt_height + ($bt_padding*2) + ($txtpad*2);
135 my $img = GD::Image->new($scale_width,$scale_height);
136 my $white = $img->colorAllocate(255,255,255);
137 my $linecolor = $img->colorAllocate(hex2rgb($bt_linecolor));
138 my $textcolor = $img->colorAllocate(hex2rgb($bt_textcolor));
139 my $fillcolor = $img->colorAllocate(hex2rgb($bt_fillcolor));
140 my $bordercolor = $img->colorAllocate(hex2rgb($bt_bordercolor));
141 my $line_y1 = $bt_padding + $txtpad;
142 my $line_y2 = $bt_padding + $txtpad + $bt_height;
143 $img->transparent($white);
144 $img->rectangle(0, 0, $scale_width-1, $scale_height-1, $bordercolor);
145 $img->filledRectangle($bt_padding, $line_y1, $bt_padding + $bt_width,
146 $line_y2, $fillcolor);
148 foreach $i (@bt_entry_times) {
149 $line_x = $bt_padding + (round(($i/1440) * $bt_width));
150 $img->line($line_x, $line_y1, $line_x, $line_y2, $linecolor);
152 # Shut off text if width is too less.
154 if ($bt_width >= 100) {
155 my $ruler_y = $bt_padding + $txtpad + $bt_height + 2;
157 for ($i = 0; $i <= 23; $i += 2) {
158 $ruler_x = $bt_padding + round($i * $bt_width/24);
159 $img->string(gdTinyFont,$ruler_x,$ruler_y,"$i",$textcolor);
160 debug(5, 'tinyfont',$ruler_x,$ruler_y,"$i",$textcolor);
162 $img->string(gdTinyFont, $bt_padding + $bt_width-2,
163 $ruler_y, "0", $textcolor);
164 my $caption_x = $bt_padding;
165 my $caption_y = $bt_padding-1;
166 my $caption = "B L O G T I M E S $monthname[$month] $year";
167 $img->string(gdTinyFont, $caption_x, $caption_y,
168 $caption, $textcolor);
169 debug(5, 'tinyfont', $caption_x, $caption_y, $caption, $textcolor);
171 my $ruler_y = $bt_padding + $txtpad + $bt_height + 2;
173 for ($i = 0; $i <= 23; $i += 6) {
174 $ruler_x = $bt_padding + round($i * $bt_width/24);
175 $img->string(gdTinyFont,$ruler_x,$ruler_y,"$i",$textcolor);
177 $img->string(gdTinyFont, $bt_padding + $bt_width - 2,
178 $ruler_y, "0", $textcolor);
179 my $caption_x = $bt_padding;
180 my $caption_y = $bt_padding-1;
181 my $caption = "$month $year";
182 $img->string(gdTinyFont, $caption_x, $caption_y,
183 $caption, $textcolor);
186 open IMG, "> $destination_dir/blogtimes.png" or die "$!";
188 print IMG $img->png or die "$!";
190 debug(2, "build blogtimes");
194 my ($pkg, $files) = @_;
195 my $latest_story = (sort {$b <=> $a} values %$files)[0];
196 return 1 if $latest_story <= $last_timestamp;
198 my $now_month = $now[4] + 1;
199 my $now_year = $now[5] + 1900;
201 debug(1, "updating graph");
203 foreach (keys %{$files}) {
204 my @date = localtime($files->{$_});
206 my $month = $date[4] + 1;
207 my $year = $date[5] + 1900;
208 graph_add($date[2], $date[1], $date[0])
209 if (sprintf("%04d%02d%02d", $year, $month, $mday) ge
211 bt_add($date[2], $date[1])
212 if ($year == $now_year and $month == $now_month);
215 build_blogtimes($now_year, $now_month);
219 return 0 unless $destination_dir;
220 $last_timestamp = -e $timestamp_file ? stat($timestamp_file)->mtime : 0;
221 my $fh = new FileHandle;
222 if (!$fh->open(">$timestamp_file")) {
223 debug(0, "Couldn't touch timestamp file $timestamp_file");
227 debug(1, "$package enabled");
234 Blosxom Plug-in: graph
238 Purpose: creates graphs showing the time of day stories are posted
241 * $destination_dir/graph.png -- bar graph showing number of posts per
242 period of time (default: hour) since $graph_start_day (default: 19000101)
243 * $destination_dir/blogtimes.png -- a vertical-line form of a scatterplot,
244 showing the posting time of all stories posted this month
254 Todd Larason <jtl@molehill.org>, http://molelog.molehill.org/
256 portions (the "BLOGTIMES" chart style) based on code by Nilesh Chaudhari;
257 see http://nilesh.org/mt/blogtimes/. Nilesh gets credit, but direct bugs
262 None known; address bug reports and comments to me or to the Blosxom
263 mailing list [http://www.yahoogroups.com/groups.blosxom].
267 =head2 Configuration variables
269 There are many configuration variables controlling height, width and colors
270 of the two graphs; see the configuration variable section for those.
274 C<$destination_dir>, the directory where the output files will be created;
275 this must be configured.
277 C<$graph_start_day>, the earlist date to consider stories from for the bar
278 graph. If you've converted from another weblogging package and lost time of
279 day information on converted stories, you probably want to set this to when
280 you started using Blosxom.
282 C<$graph_num_bars> is the number of bars to create in the bargraph form;
283 if it isn't evenly divisible by 24, things probably act weird -- that isn't
286 The C<bt_> variables control the "BLOGTIMES" style chart; the others the
287 bar graph. For the bargraph, the width and height is the size of the overall
288 image; for the blogtimes, it's for the graph portion only -- padding is added
289 for a border and optionally for text. One or the other of these is likely to
290 change in a future version to provide consistency.
294 The images are only recreated when they appear to be out of date; a timestamp
295 file is maintained for this. To force them to be regenerated, remove
296 $plugin_state_dir/.postgraph.timestamp.
301 Copyright 2003, Todd Larason
303 Copyright (c) 2002 Nilesh Chaudhari http://nilesh.org/
305 (This license is the same as Blosxom's and the original BLOGTIME's)
307 Permission is hereby granted, free of charge, to any person obtaining a
308 copy of this software and associated documentation files (the "Software"),
309 to deal in the Software without restriction, including without limitation
310 the rights to use, copy, modify, merge, publish, distribute, sublicense,
311 and/or sell copies of the Software, and to permit persons to whom the
312 Software is furnished to do so, subject to the following conditions:
314 The above copyright notice and this permission notice shall be included
315 in all copies or substantial portions of the Software.
317 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
318 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
319 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
320 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
321 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
322 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
323 OTHER DEALINGS IN THE SOFTWARE.