]> git.sur5r.net Git - bacula/bacula/commitdiff
ebl Add a new Balloon view that display nb files, bytes and job time
authorEric Bollengier <eric@eb.homelinux.org>
Wed, 19 Dec 2007 17:18:59 +0000 (17:18 +0000)
committerEric Bollengier <eric@eb.homelinux.org>
Wed, 19 Dec 2007 17:18:59 +0000 (17:18 +0000)
     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
gui/bweb/lib/Bweb.pm
gui/bweb/lib/GBalloon.pm [new file with mode: 0644]
gui/bweb/technotes-2.3
gui/bweb/tpl/graph.tpl
gui/debian/changelog
gui/debian/rules

index 4d3eb7b798eb732abba9160239f40eedc33d782e..bbf61e33fc39b3c1d22babe9f67f99a6759c97fe 100755 (executable)
@@ -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();
index 9881eb8e1878a4542ecfb7ae55a657452344d997..247f37485dda026ff4a53fd526677009e4a2fda8 100644 (file)
@@ -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 (file)
index 0000000..cf6a4a0
--- /dev/null
@@ -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 "
+<map name='$self->{img_id}'>
+" . join ('', reverse @{$self->{image_map}}) . "
+</map>
+<script type='text/javascript' language='JavaScript'>
+ document.getElementById('$self->{img_id}').src='$img';
+</script>
+" ;
+
+}
+
+sub push_image_map
+{
+    my ($self, $label, $tips, $x, $y, $z) = @_ ;
+
+    my $ret = sprintf("%.2f,%.2f,%.2f",
+                      $x,$y,$z);
+
+    push @{$self->{image_map}}, 
+        "<area shape='circle' coords='$ret' ".
+       "title='$tips' href='$self->{base_url}${label}'>\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 "<html><body>";
+print STDERR $top->get_imagemap("example", "a.png");
+print STDERR "</body></html>";
index 868618718c45e3cf974b25e3d555e7879cd99fe6..e4012d99774ba0a70c5640f12518bbcee474f126 100644 (file)
@@ -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
 
index c80a58f645a8b904ac22e9586774377a6798b5fa..c6801efb06679845adb141d9f40cdfa852a0ebaf 100644 (file)
@@ -98,6 +98,7 @@
     <option id='gtype_bars' value='bars'>Bars</option>
 <!--  <option id='gtype_bars3d' value='bars3d'>Bars3d</option> -->
     <option id='gtype_lines' value='lines'>Lines</option>
+    <option id='gtype_balloon' value='balloon'>Balloon</option>
     <option id='gtype_linespoints' value='linespoints'>Lines points</option>
 </td>
 <td>
  Current &nbsp;
  </div>
  <div class="otherbox">
- <img src='bgraph.pl?<TMPL_VAR NAME=url>' alt='Nothing to display, Try a bigger date range'>
+
+ <img usemap='imggraph' id='imggraph' 
+      alt='Nothing to display, Try a bigger date range'>
  </div>
 
 </td>
      document.getElementById('gtype_<TMPL_VAR gtype>').selected=true;
   </TMPL_IF>
 
+  <TMPL_IF url>
+   document.getElementById('imggraph').src='bgraph.pl?<TMPL_VAR NAME=url>'
+  </TMPL_IF>
+
 </script>
index b4e2ef9ca79083bac3b95a077b213bda61348b25..1fd1b6091e8d7f391625f6f1e0686be128543904 100644 (file)
@@ -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
        
index 26970908cb0a3ff2df3c88a77b7cc7eea9931407..fc7afc372fe5b9e6b893bb8b54a450670de8fa30 100755 (executable)
@@ -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