From 7725038821337dfd2557dda85b18b3fc3b0fd9d2 Mon Sep 17 00:00:00 2001
From: Eelco Dolstra <eelco.dolstra@logicblox.com>
Date: Mon, 26 Aug 2013 18:58:04 +0200
Subject: [PATCH] On aggregate job pages, show a matrix showing all the
 constituent builds

---
 src/lib/Hydra/Controller/Job.pm | 26 ++++++++++++-
 src/root/job.tt                 | 68 +++++++++++++++++++++++++++++++++
 2 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/src/lib/Hydra/Controller/Job.pm b/src/lib/Hydra/Controller/Job.pm
index 50b6b875..2e272395 100644
--- a/src/lib/Hydra/Controller/Job.pm
+++ b/src/lib/Hydra/Controller/Job.pm
@@ -20,15 +20,16 @@ sub job : Chained('/') PathPart('job') CaptureArgs(3) {
 
 sub overview : Chained('job') PathPart('') Args(0) {
     my ($self, $c) = @_;
+    my $job = $c->stash->{job};
 
     $c->stash->{template} = 'job.tt';
 
     $c->stash->{lastBuilds} =
-        [ $c->stash->{job}->builds->search({ finished => 1 },
+        [ $job->builds->search({ finished => 1 },
             { order_by => 'id DESC', rows => 10, columns => [@buildListColumns] }) ];
 
     $c->stash->{queuedBuilds} = [
-        $c->stash->{job}->builds->search(
+        $job->builds->search(
             { finished => 0 },
             { join => ['project']
             , order_by => ["priority DESC", "id"]
@@ -36,6 +37,27 @@ sub overview : Chained('job') PathPart('') Args(0) {
             , '+as' => ['enabled']
             }
         ) ];
+
+    # If this is an aggregate job, then get its constituents.
+    my @constituents = $c->model('DB::Builds')->search(
+	{ aggregate => { -in => $job->builds->search({}, { columns => ["id"], order_by => "id desc", rows => 10 })->as_query } },
+	{ join => 'aggregateconstituents_constituents', 
+	  columns => ['id', 'job', 'finished', 'buildstatus'],
+	  +select => ['aggregateconstituents_constituents.aggregate'],
+	  +as => ['aggregate']
+	});
+
+    my $aggregates = {};
+    my %constituentJobs;
+    foreach my $b (@constituents) {
+	my $jobName = $b->get_column('job');
+	$aggregates->{$b->get_column('aggregate')}->{$jobName} =
+	    { id => $b->id, finished => $b->finished, buildstatus => $b->buildstatus};
+	$constituentJobs{$jobName} = 1;
+    }
+
+    $c->stash->{aggregates} = $aggregates;
+    $c->stash->{constituentJobs} = [sort (keys %constituentJobs)];
 }
 
 
diff --git a/src/root/job.tt b/src/root/job.tt
index 8722d3df..c9c4b6cb 100644
--- a/src/root/job.tt
+++ b/src/root/job.tt
@@ -4,6 +4,9 @@
 
 <ul class="nav nav-tabs">
   <li class="active"><a href="#tabs-status" data-toggle="tab">Status</a></li>
+  [% IF constituentJobs.size > 0 %]
+    <li><a href="#tabs-constituents" data-toggle="tab">Constituents</a></li>
+  [% END %]
   <li><a href="#tabs-links" data-toggle="tab">Links</a></li>
 </ul>
 
@@ -21,6 +24,71 @@
     [% END %]
   </div>
 
+  [% IF constituentJobs.size > 0 %]
+
+    <div id="tabs-constituents" class="tab-pane">
+
+      <table class="table table-striped table-condensed">
+	<thead>
+	  <tr>
+	    <th>#</th>
+	    [% FOREACH j IN constituentJobs %]
+	      <th>[% HTML.escape(j) %]</th>
+	    [% END %]
+	  </tr>
+	</thead>
+	<tbody>
+	  [% FOREACH agg IN aggregates.keys.nsort.reverse %]
+	    <tr>
+	      <td><a class="row-link" href="[% c.uri_for('/build' agg) %]">[% agg %]</a></td>
+	      [% FOREACH j IN constituentJobs %]
+		<td>
+		  [% r = aggregates.$agg.$j; IF r.id %]
+		    <a href="[% c.uri_for('/build' r.id) %]">
+		      [% INCLUDE renderBuildStatusIcon size=16 build=r %]
+		    </a>
+		  [% END %]
+		</td>
+	      [% END %]
+	    </tr>
+	  [% END %]
+	</tbody>
+      </table>
+      
+      <hr/>
+  
+      <table class="table table-striped table-condensed">
+	<thead>
+	  <tr>
+	    <th>#</th>
+	    [% FOREACH j IN constituentJobs %]
+	      <th>[% HTML.escape(j) %]</th>
+	    [% END %]
+	  </tr>
+	</thead>
+	<tbody>
+	  [% FOREACH agg IN aggregates.keys.nsort.reverse %]
+	    <tr>
+	      <td><a class="row-link" href="[% c.uri_for('/build' agg) %]">[% agg %]</a></td>
+	      [% FOREACH j IN constituentJobs %]
+		<td>
+		  [% r = aggregates.$agg.$j; IF r.id %]
+		    <a href="[% c.uri_for('/build' r.id) %]">
+		      [% INCLUDE renderBuildStatusIcon size=16 build=r %]
+		    </a>
+		  [% END %]
+		</td>
+	      [% END %]
+	    </tr>
+	  [% END %]
+	</tbody>
+      </table>
+
+
+    </div>
+  
+  [% END %]
+
   <div id="tabs-links" class="tab-pane">
     <ul>
       <li><a href="[% c.uri_for('/job' project.name jobset.name job.name 'latest') %]">Latest successful build</a></li>