Bug 54750

Bugzilla graph generation redesign


git-svn-id: svn://svn.office.custis.ru/3rdparty/bugzilla.org/trunk@572 6955db30-a419-402b-8a0d-67ecbb4d7f56
master
vfilippov 2009-12-09 16:53:13 +00:00
parent 27d3df470c
commit bf5b6646b1
2 changed files with 143 additions and 58 deletions

View File

@ -49,36 +49,16 @@ if (!defined $cgi->param('id') && $display ne 'doall') {
# Connect to the shadow database if this installation is using one to improve
# performance.
my $webdotbase = Bugzilla->params->{webdotbase};
my $webtwopibase = Bugzilla->params->{webtwopibase};
my ($seen, $edges, $baselist, $deps) = GetEdges($display, $cgi->param('id'));
my ($nodes, $bugtitles) = GetNodes($seen, $baselist, $deps, $vars);
my $filename = MakeDot($edges, $nodes);
# Remote dot server. We don't hardcode 'urlbase' here in case
# 'sslbase' is in use.
if ($webdotbase =~ /^https?:/)
my ($clusters, $independent) = GetClusters($seen, $edges);
my $graphs = [];
for (@$clusters, $independent)
{
$webdotbase =~ s/%([a-z]*)%/Bugzilla->params->{$1}/eg;
my $url = $webdotbase . $filename;
$vars->{image_url} = $url . ".gif";
$vars->{map_url} = $url . ".map";
}
# Local dot installation
else
{
# First, generate the png image file from the .dot source
my ($pngfh, $pngfilename) = DotTemp('.png');
DotInto($pngfh, $webdotbase, '-Tpng', $filename) or ($vars->{timeout} = 1);
$vars->{image_url} = DotRel($pngfilename);
# Then, generate a imagemap datafile that contains the corner data
# for drawn bug objects. Pass it on to CreateImagemap that
# turns this monster into html.
my ($mapfh, $mapfilename) = DotTemp('.map');
DotInto($mapfh, $webdotbase, '-Tismap', $filename) or ($vars->{timeout} = 1);
$vars->{image_map} = CreateImagemap($mapfilename, $bugtitles);
my $filename = MakeDot($edges, $nodes, $_);
my $gr = { cluster => $_, filename => $filename, alt => $_ eq $independent };
GetDotUrls($gr, $bugtitles);
push @$graphs, $gr;
}
# Cleanup any old .dot files created from previous runs.
@ -88,12 +68,104 @@ my @bugs = keys %$baselist;
$vars->{bug_id} = join ', ', @bugs;
$vars->{multiple_bugs} = scalar(keys %$baselist) > 1;
$vars->{display} = $display;
$vars->{graphs} = $graphs;
# Generate and return the UI (HTML page) from the appropriate template.
print $cgi->header();
$template->process("bug/dependency-graph.html.tmpl", $vars)
|| ThrowTemplateError($template->error());
# Divide the overall graph into closed clusters
sub GetClusters
{
my ($seen, $edges) = @_;
my $twoway = {};
for my $b (keys %$edges)
{
for my $d (keys %{$edges->{$b}})
{
$twoway->{$b}->{$d} = 1;
$twoway->{$d}->{$b} = 1;
}
}
$seen = { %$seen };
my ($clusters, $independent) = ([], {});
my $cluster;
while (keys %$seen)
{
unless ($cluster && %$cluster)
{
my ($b) = keys %$seen;
$cluster = { $b => 1 };
delete $seen->{$b};
}
my $added = 0;
for (keys %$cluster)
{
for (keys %{$twoway->{$_}})
{
unless ($cluster->{$_})
{
$cluster->{$_} = 1;
delete $seen->{$_};
$added++;
}
}
}
# кластер замкнут
unless ($added)
{
if (scalar(keys %$cluster) == 1)
{
my ($b) = keys %$cluster;
$independent->{$b} = 1;
}
else
{
push @$clusters, $cluster;
}
$cluster = undef;
}
}
return ($clusters, $independent);
}
# Get URLs for HTML usage
sub GetDotUrls
{
my ($inout, $bugtitles) = @_;
my $base = Bugzilla->params->{$inout->{alt} ? 'webtwopibase' : 'webdotbase'};
# Remote dot server. We don't hardcode 'urlbase' here in case
# 'sslbase' is in use.
if ($base =~ /^https?:/)
{
$inout->{image_url} = $base . $inout->{filename} . ".gif";
$inout->{map_url} = $base . $inout->{filename} . ".map";
}
# Local dot installation
else
{
# First, generate the svg image file from the .dot source
my ($pngfh, $pngfilename) = DotTemp('.svg');
DotInto($pngfh, $base, '-Tsvg', $inout->{filename}) or ($inout->{timeout} = 1);
$inout->{image_svg_url} = DotRel($pngfilename);
# Next, generate the png image file for those who don't support SVG
my ($pngfh, $pngfilename) = DotTemp('.png');
DotInto($pngfh, $base, '-Tpng', $inout->{filename}) or ($inout->{timeout} = 1);
$inout->{image_url} = DotRel($pngfilename);
# Then, generate a imagemap datafile that contains the corner data
# for drawn bug objects. Pass it on to CreateImagemap that
# turns this monster into html.
my ($mapfh, $mapfilename) = DotTemp('.map');
DotInto($mapfh, $base, '-Tismap', $inout->{filename}) or ($inout->{timeout} = 1);
$inout->{image_map_id} = 'imap' . $mapfilename;
$inout->{image_map_id} =~ s/\W+/_/gso;
$inout->{image_map} = CreateImagemap($mapfilename, $inout->{image_map_id}, $bugtitles);
}
}
# DotInto: Run local dot with timeout support and write output into filehandle
sub DotInto
{
@ -137,8 +209,9 @@ sub DotInto
sub CreateImagemap
{
my ($mapfilename, $bugtitles) = @_;
my $map = "<map name=\"imagemap\">\n";
my ($mapfilename, $mapid, $bugtitles) = @_;
$mapid ||= 'imagemap';
my $map = "<map name=\"$mapid\">\n";
my $default;
open MAP, "<$mapfilename";
@ -171,13 +244,9 @@ sub CreateImagemap
sub AddLink
{
my ($blocked, $dependson, $seen, $edges) = (@_);
my $key = "$blocked,$dependson";
if (!exists $edges->{$key})
{
$edges->{$key} = 1;
$seen->{$blocked} = 1;
$seen->{$dependson} = 1;
}
$edges->{$blocked}->{$dependson} = 1;
$seen->{$blocked} = 1;
$seen->{$dependson} = 1;
}
sub DotTemp
@ -283,8 +352,8 @@ sub GetEdges
sub GetNodes
{
my ($seen, $baselist, $deps, $vars) = @_;
return [] unless keys %$seen;
my $nodes = [];
return {} unless keys %$seen;
my $nodes = {};
my $bugtitles = {};
# Retrieve bug information from the database
my $rows = Bugzilla->dbh->selectall_arrayref(
@ -335,7 +404,7 @@ GROUP BY t1.bug_id", {Slice=>{}}, keys %$seen) || {};
($deps->{$row->{bug_id}} || 0) > 4;
if ($important)
{
push @params, "width=3", "height=1.5", "fontsize=13";
push @params, "height=1.5", "fontsize=13";
}
if ($row->{short_desc})
@ -360,8 +429,6 @@ $row->{short_desc}
EOF
}
push @$nodes, $row->{bug_id} . (@params ? " [" . join(',', @params) . "]" : "");
# Push the bug tooltip texts into a global hash so that
# CreateImagemap sub (used with local dot installations) can
# use them later on.
@ -372,13 +439,20 @@ EOF
if ($row->{short_desc}) {
$bugtitles->{$row->{bug_id}} .= " $row->{product}/$row->{component} - $row->{short_desc}";
}
my $t = $bugtitles->{$row->{bug_id}};
$t =~ s/([\\\"])/\\$1/gso;
push @params, "tooltip=\"$t\"";
$nodes->{$row->{bug_id}} = $row->{bug_id} . (@params ? " [" . join(',', @params) . "]" : "");
}
return ($nodes, $bugtitles);
}
sub MakeDot
{
my ($edges, $nodes) = @_;
my ($edges, $nodes, $cluster) = @_;
$cluster ||= $edges;
my ($fh, $filename) = DotTemp('.dot');
my $urlbase = Bugzilla->params->{urlbase};
no warnings 'utf8';
@ -387,10 +461,10 @@ sub MakeDot
ranksep=0.5;
graph [URL="${urlbase}query.cgi" rankdir=LR overlap=false splines=true]
node [URL="${urlbase}show_bug.cgi?id=\\N" shape=note fontsize=10 fontname="Consolas" style=filled fillcolor=white]
edge [color=blue arrowtail=none arrowhead=none len=0.5]
edge [color=blue len=0.5]
EOF
OutLinks($edges, $fh);
print $fh "$_\n" foreach @$nodes;
OutLinks($edges, $cluster, $fh);
print $fh $nodes->{$_}, "\n" foreach keys %$cluster;
print $fh "}\n";
close $fh;
chmod 0777, $filename;
@ -399,12 +473,16 @@ EOF
sub OutLinks
{
my ($edges, $fh) = @_;
my ($blocked, $dependson);
for (keys %$edges)
my ($edges, $cluster, $fh) = @_;
for my $blocked (keys %$edges)
{
($blocked, $dependson) = split /,/, $_, 2;
print $fh "$blocked -> $dependson\n";
if ($cluster->{$blocked})
{
for my $dependson (keys %{$edges->{$blocked}})
{
print $fh "$blocked -> $dependson\n";
}
}
}
}

View File

@ -46,8 +46,6 @@
[% PROCESS global/header.html.tmpl %]
[% image_map %]
<table cellpadding="2" style="margin-bottom: 16px; border: 1px solid gray; background-color: white">
<tr>
<td>Bug states:</td>
@ -60,15 +58,24 @@
</tr>
</table>
[% IF timeout %]
[% FOR g = graphs %]
[% IF g.timeout %]
<p style="color:red">Timed out.</p>
[% ELSIF g.image_map %]
<p>
[% g.image_map %]
[% IF g.image_svg_url %]<object type="image/svg+xml" data="[% g.image_svg_url %]" style="border: 1px solid gray">[% END %]
<img src="[% g.image_url %]" alt="Dependency graph" usemap="#[% g.image_map_id | html %]" [% IF !g.image_svg_url %]style="border: 1px solid gray"[% END %] />
[% IF g.image_svg_url %]</object>[% END %]
</p>
[% ELSE %]
[% IF image_map %]
<img src="[% image_url %]" alt="Dependency graph" usemap="#imagemap" style="border: 1px solid gray">
[% ELSE %]
<a href="[% map_url %]">
<img src="[% image_url %]" alt="Dependency graph" ismap="ismap" style="border: 1px solid gray">
<p>
<a href="[% g.map_url %]">
[% IF g.image_svg_url %]<object type="image/svg+xml" data="[% g.image_svg_url %]" style="border: 1px solid gray">[% END %]
<img src="[% g.image_url %]" alt="Dependency graph" ismap="ismap" [% IF !g.image_svg_url %]style="border: 1px solid gray"[% END %] />
[% IF g.image_svg_url %]</object>[% END %]
</a>
</p>
[% END %]
[% END %]