From 6918b99f65750e47f91bd44d725b863fc14d0251 Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Wed, 19 Dec 2007 17:18:59 +0000 Subject: [PATCH] ebl Add a new Balloon view that display nb files, bytes and job time on the same graphic git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@6076 91ce42f0-d328-0410-95d8-f526ca767f89 --- gui/bweb/cgi/bgraph.pl | 55 ++++++- gui/bweb/lib/Bweb.pm | 10 +- gui/bweb/lib/GBalloon.pm | 334 +++++++++++++++++++++++++++++++++++++++ gui/bweb/technotes-2.3 | 4 + gui/bweb/tpl/graph.tpl | 9 +- gui/debian/changelog | 1 + gui/debian/rules | 1 + 7 files changed, 408 insertions(+), 6 deletions(-) create mode 100644 gui/bweb/lib/GBalloon.pm diff --git a/gui/bweb/cgi/bgraph.pl b/gui/bweb/cgi/bgraph.pl index 4d3eb7b798..bbf61e33fc 100755 --- a/gui/bweb/cgi/bgraph.pl +++ b/gui/bweb/cgi/bgraph.pl @@ -115,6 +115,58 @@ my $filter = $bweb->get_client_filter(); my $gtype = CGI::param('gtype') || 'bars'; + +# in this mode, we generate an image and an imagemap +if ($gtype eq 'balloon') { + use Digest::MD5 qw(md5_hex); + use GBalloon; + + my $all = $dbh->selectall_arrayref(" +SELECT $bweb->{sql}->{SEC_TO_INT}( $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime) + - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) + AS duration, JobBytes, JobFiles, JobId, $jobt.Name + + FROM $jobt, Client $filter $groupf +WHERE $jobt.ClientId = Client.ClientId + AND $jobt.Type = 'B' + $clientq + $statusq + $levelq + $jobnameq + $groupq +$limitq +"); + + my $b = new GBalloon(width=>$arg->{width}, + height =>$arg->{height}); + $b->set_legend_axis(x_title => 'Time', + x_func => sub { + POSIX::strftime('%H:%M', gmtime($_[0])) + }, + y_title => 'Size', y_func => \&Bweb::human_size, + z_title => 'Nb files'); + + foreach my $a (@$all) { + $b->add_point($a->[0], $a->[1], $a->[2], + "?action=job_zoom;jobid=$a->[3]", + "$a->[4] $a->[2] files"); + } + + $b->init_gd(); + $b->finalize(); + + my $md5_rep = md5_hex(join(":", map { $arg->{$_} } sort keys %$arg)); + + # need to cleanup this path + open(FP, ">$conf->{fv_write_path}/$md5_rep.png"); + print FP $GBalloon::gd->png; + close(FP); + + print $b->get_imagemap("Job overview", "/bweb/fv/$md5_rep.png"); + + exit 0; +} + print CGI::header('image/png'); sub get_graph @@ -514,6 +566,7 @@ $limit my ($ret) = make_tab_sum($all); print $obj->plot([$ret->{date}, $ret->{nb}])->png; -} + +} $dbh->disconnect(); diff --git a/gui/bweb/lib/Bweb.pm b/gui/bweb/lib/Bweb.pm index 9881eb8e18..247f37485d 100644 --- a/gui/bweb/lib/Bweb.pm +++ b/gui/bweb/lib/Bweb.pm @@ -1891,11 +1891,10 @@ sub display_graph graph gtype type filter db_clients limit db_filesets width height qclients qfilesets qjobnames db_jobnames/); - - + my $url = CGI::url(-full => 0, - -base => 0, - -query => 1); + -base => 0, + -query => 1); $url =~ s/^.+?\?//; # http://path/to/bweb.pl?arg => arg # this organisation is to keep user choice between 2 click @@ -1906,6 +1905,9 @@ sub display_graph %$fields, }, "graph.tpl"); + if ($fields->{gtype} eq 'balloon') { + system("./bgraph.pl"); + } } sub get_selected_media_location diff --git a/gui/bweb/lib/GBalloon.pm b/gui/bweb/lib/GBalloon.pm new file mode 100644 index 0000000000..cf6a4a0208 --- /dev/null +++ b/gui/bweb/lib/GBalloon.pm @@ -0,0 +1,334 @@ + +=head1 LICENSE + + Bweb - A Bacula web interface + Bacula® - The Network Backup Solution + + Copyright (C) 2000-2006 Free Software Foundation Europe e.V. + + The main author of Bweb is Eric Bollengier. + The main author of Bacula is Kern Sibbald, with contributions from + many others, a complete list can be found in the file AUTHORS. + + This program is Free Software; you can redistribute it and/or + modify it under the terms of version two of the GNU General Public + License as published by the Free Software Foundation plus additions + that are listed in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Bacula® is a registered trademark of John Walker. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zurich, + Switzerland, email:ftf@fsfeurope.org. + +=cut + +use strict ; +use GD qw/gdSmallFont/; + +package GBalloon ; + +our $gd ; +our @color_tab ; + +our $black ; +our $white ; + +sub new +{ + my ($class, %arg) = @_ ; + + my $self = { + height => 800, # element height in px + width => 600, # element width in px + xmarge => 75, + ymarge => 100, + z_max_size => 50, # max circle size + z_min_size => 8, # min circle size + + x_min => 0, + y_min => 0, + x_max => 0, + y_max => 0, + + data => [], + type => {}, + + img_id => 'imggraph', # used to display graph + + base_url => '', + } ; + + map { $self->{$_} = $arg{$_} } keys %arg ; + + bless $self ; + + return $self ; +} + +sub get_imagemap +{ + my ($self, $title, $img) = @_ ; + + return " + +" . join ('', reverse @{$self->{image_map}}) . " + + +" ; + +} + +sub push_image_map +{ + my ($self, $label, $tips, $x, $y, $z) = @_ ; + + my $ret = sprintf("%.2f,%.2f,%.2f", + $x,$y,$z); + + push @{$self->{image_map}}, + "\n" ; +} + +sub init_gd +{ + my ($self) = @_; + + unless (defined $gd) { + my $height = $self->{height} ; + my $width = $self->{width} ; + + $gd = new GD::Image($width+100,$height+100); + $white = $gd->colorAllocate(255,255,255); + + push @color_tab, ($gd->colorAllocate(135,236,88), + $gd->colorAllocate( 255, 236, 139), + $gd->colorAllocate(255,95,95), + $gd->colorAllocate(255,149,0), + $gd->colorAllocate( 255, 174, 185), + $gd->colorAllocate( 179, 255, 58), + $gd->colorAllocate( 205, 133, 0), + $gd->colorAllocate(205, 133, 0 ), + $gd->colorAllocate(238, 238, 209), + ) ; + + $black = $gd->colorAllocate(0,0,0); + $gd->transparent($white); +# $gd->interlaced('true'); + # x y x y + } +} +use POSIX qw/strftime/; + +sub compute +{ + my ($self, $d) = @_; + +# print STDERR "compute: x=$d->[0] y=$d->[1] z=$d->[2]\n"; + + # offset percent max + my $x = $self->{xmarge} + $d->[0] / $self->{x_max} * ($self->{width} - $self->{xmarge}); + my $y = $self->{height} - $d->[1] / $self->{y_max} * ($self->{height} - $self->{ymarge}); + my $z = sqrt($d->[2]) / $self->{z_max} * $self->{z_max_size}; + + if ($z < $self->{z_min_size}) { + $z += $self->{z_min_size}; # min size + } + + return ($x, $y, $z); +} + +sub finalize +{ + my ($self) = @_; + + # we need to display big z before min z + my @data = sort { $b->[2] <=> $a->[2] } @{$self->{data}}; + return unless (scalar(@data)); + + # the max z will take something like 10% of the total size + $self->{z_max} = sqrt($data[0]->[2]); + my $c=0; + +# print STDERR "max: x=$self->{x_max} y=$self->{y_max} z=$self->{z_max}\n"; + + foreach my $d (@data) + { + my ($x, $y, $z) = $self->compute($d); +# print STDERR "current: x=$x y=$y z=$z\n"; + $c = ($c+1) % scalar(@color_tab); + $gd->filledArc($x, $y, 2*$z, 2*$z, 0, 360, $color_tab[$c]); + $self->push_image_map($d->[3], $d->[4],$x,$y,$z); + } + + $self->draw_axis(); +} + +sub set_legend_axis +{ + my ($self, %arg) = @_; + $self->{axis} = \%arg; +} + +sub draw_axis +{ + my ($self) = @_; + # draw axis + $gd->line($self->{xmarge}, 5, + $self->{xmarge}, $self->{height}, + $black); + $gd->line($self->{xmarge}, $self->{height}, + $self->{width} - 5, $self->{height}, $black); + + $gd->string(GD::Font->Small, + $self->{width} - 5, + $self->{height} + 10, + $self->{axis}->{x_title}, + $black); + + $gd->string(GD::Font->Small, + 0, + 10, + $self->{axis}->{y_title}, + $black); + + my $h = $self->{height} ; + my $w = $self->{width} - $self->{xmarge}; + + my $i=0; + for (my $p = $self->{xmarge}; $p <= $w; $p = $p + $w/7) { + $gd->string(GD::Font->Small, + $p, + $self->{height} + 10, + $self->{axis}->{x_func}($i), + $black); + + $gd->line($p, + $self->{height} - 2, + $p, + $self->{height} + 2, + $black); + + $i = $i + $self->{x_max}/7; + } + + $i=0; + for (my $p = $h; $p >= 0; $p = $p - $h/7) { + $gd->string(GD::Font->Small, + 10, $p, + $self->{axis}->{y_func}($i), + $black); + + $gd->line($self->{xmarge} - 2, + $p, + $self->{xmarge} + 2, + $p, + $black); + + $i = $i + $self->{y_max}/7; + } +} + +sub add_point +{ + my ($self, $x_val, $y_val, $z_val, $label, $link) = @_; + return unless ($x_val or $y_val or $z_val or $label); + + if ($self->{x_max} < $x_val) { + $self->{x_max} = $x_val; + } + if ($self->{y_max} < $y_val) { + $self->{y_max} = $y_val; + } + + if ($self->{x_min} > $x_val) { + $self->{x_min} = $x_val; + } + if ($self->{y_min} > $y_val) { + $self->{y_min} = $y_val; + } + + # z will be compute after + push @{$self->{data}}, [$x_val, $y_val, $z_val, $label, $link]; +} + +1; +__END__ + +package main ; + +# display Mb/Gb/Kb +sub human_size +{ + my @unit = qw(B KB MB GB TB); + my $val = shift || 0; + my $i=0; + my $format = '%i %s'; + while ($val / 1024 > 1) { + $i++; + $val /= 1024; + } + $format = ($i>0)?'%0.1f %s':'%i %s'; + return sprintf($format, $val, $unit[$i]); +} + +# display Day, Hour, Year +sub human_sec +{ + use integer; + + my $val = shift; + $val /= 60; # sec -> min + + if ($val / 60 <= 1) { + return "$val mins"; + } + + $val /= 60; # min -> hour + if ($val / 24 <= 1) { + return "$val hours"; + } + + $val /= 24; # hour -> day + return "$val days"; +} + +my $top = new GBalloon(debug => 1) ; +$top->set_legend_axis(x_title => 'Time', x_func => \&human_sec, + y_title => 'Size', y_func => \&human_size, + z_title => 'Nb files'); + +$top->add_point( 13*60+56 , 3518454692 , 6 , 'PRLISVCS_RMAN'); +$top->add_point( 23*60+31 , 2419151398 , 4 , 'RHREC_BACKUP' ); +$top->add_point( 12*60+07 , 1373969860 , 1044 , '3_DATA'); +$top->add_point( 13*60+40 , 2284109956 , 1121 , '4_DATA'); +$top->add_point( 13*60+40 , 2284109956 , 1 , '1_DATA'); +$top->add_point( 13*60+10 , 2284109956 , 1 , '2_DATA'); +$top->add_point( 13*60+10 , 2284109956 , 2000000 , '7_DATA'); + + + +$top->init_gd(); +$top->finalize() ; +#$top->draw_labels() ; +# make sure we are writing to a binary stream +binmode STDOUT; + +# Convert the image to PNG and print it on standard output +print $gd->png; + +print STDERR ""; +print STDERR $top->get_imagemap("example", "a.png"); +print STDERR ""; diff --git a/gui/bweb/technotes-2.3 b/gui/bweb/technotes-2.3 index 868618718c..e4012d9977 100644 --- a/gui/bweb/technotes-2.3 +++ b/gui/bweb/technotes-2.3 @@ -1,3 +1,7 @@ +19Dec07 +ebl Add a new balloon graphic mode which display + time, bytes and number of files on the same graph + 13Dec07 ebl Try to extract when= from get_form() more smoothly diff --git a/gui/bweb/tpl/graph.tpl b/gui/bweb/tpl/graph.tpl index c80a58f645..c6801efb06 100644 --- a/gui/bweb/tpl/graph.tpl +++ b/gui/bweb/tpl/graph.tpl @@ -98,6 +98,7 @@ + @@ -115,7 +116,9 @@ Current  
- Nothing to display, Try a bigger date range + + Nothing to display, Try a bigger date range
@@ -168,4 +171,8 @@ document.getElementById('gtype_').selected=true; + + document.getElementById('imggraph').src='bgraph.pl?' + + diff --git a/gui/debian/changelog b/gui/debian/changelog index b4e2ef9ca7..1fd1b6091e 100644 --- a/gui/debian/changelog +++ b/gui/debian/changelog @@ -1,4 +1,5 @@ bweb (2.2.7-1) stable; urgency=low + * Add new balloon view * Add new btime module * Add a new scheduled view, you MUST have Date::Calc module diff --git a/gui/debian/rules b/gui/debian/rules index 26970908cb..fc7afc372f 100755 --- a/gui/debian/rules +++ b/gui/debian/rules @@ -52,6 +52,7 @@ install-indep: install -m 644 bweb/lib/CCircle.pm debian/bweb/usr/share/perl5 install -m 644 bweb/lib/Bweb.pm debian/bweb/usr/share/perl5 install -m 644 bweb/lib/GTime.pm debian/bweb/usr/share/perl5 + install -m 644 bweb/lib/GBalloon.pm debian/bweb/usr/share/perl5 install -m 644 bweb/script/bweb.conf debian/bweb/etc/apache/conf.d install -m 755 bweb/cgi/bgraph.pl debian/bweb/usr/lib/cgi-bin/bweb install -m 755 bweb/cgi/bweb.pl debian/bweb/usr/lib/cgi-bin/bweb -- 2.39.5