forked from lix-project/hydra
Add a dashboard
Currently the dashboard allows users to get a quick overview of the status of jobs they're interested in, but more will be added, e.g. viewing all your jobsets or all jobs of which you're a maintainer.
This commit is contained in:
parent
09b5679ee7
commit
2127d133cd
16 changed files with 382 additions and 11 deletions
|
@ -60,6 +60,12 @@ sub overview : Chained('job') PathPart('') Args(0) {
|
|||
|
||||
$c->stash->{aggregates} = $aggregates;
|
||||
$c->stash->{constituentJobs} = [sort (keys %constituentJobs)];
|
||||
|
||||
$c->stash->{starred} = $c->user->starredjobs(
|
||||
{ project => $c->stash->{project}->name
|
||||
, jobset => $c->stash->{jobset}->name
|
||||
, job => $c->stash->{job}->name
|
||||
})->count == 1 if $c->user_exists;
|
||||
}
|
||||
|
||||
|
||||
|
@ -74,4 +80,22 @@ sub get_builds : Chained('job') PathPart('') CaptureArgs(0) {
|
|||
}
|
||||
|
||||
|
||||
sub star : Chained('job') PathPart('star') Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
requirePost($c);
|
||||
requireUser($c);
|
||||
my $args =
|
||||
{ project => $c->stash->{project}->name
|
||||
, jobset => $c->stash->{jobset}->name
|
||||
, job => $c->stash->{job}->name
|
||||
};
|
||||
if ($c->request->params->{star} eq "1") {
|
||||
$c->user->starredjobs->update_or_create($args);
|
||||
} else {
|
||||
$c->user->starredjobs->find($args)->delete;
|
||||
}
|
||||
$c->stash->{resource}->{success} = 1;
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
|
|
@ -269,4 +269,19 @@ sub edit_POST {
|
|||
}
|
||||
|
||||
|
||||
sub dashboard :Chained('user') :Args(0) {
|
||||
my ($self, $c) = @_;
|
||||
$c->stash->{template} = 'dashboard.tt';
|
||||
|
||||
# Get the N most recent builds for each starred job.
|
||||
$c->stash->{starredJobs} = [];
|
||||
foreach my $j ($c->stash->{user}->starredjobs->search({}, { order_by => ['project', 'jobset', 'job'] })) {
|
||||
my @builds = $j->job->builds->search(
|
||||
{ },
|
||||
{ rows => 20, order_by => "id desc" });
|
||||
push $c->stash->{starredJobs}, { job => $j->job, builds => [@builds] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
|
|
@ -137,8 +137,27 @@ __PACKAGE__->belongs_to(
|
|||
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "CASCADE" },
|
||||
);
|
||||
|
||||
=head2 starredjobs
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:t2CCfUjFEz/lO4szROz1AQ
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::StarredJobs>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"starredjobs",
|
||||
"Hydra::Schema::StarredJobs",
|
||||
{
|
||||
"foreign.job" => "self.name",
|
||||
"foreign.jobset" => "self.jobset",
|
||||
"foreign.project" => "self.project",
|
||||
},
|
||||
undef,
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-10-14 15:46:29
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:uYKWjewvKBEAuK53u7vKuw
|
||||
|
||||
1;
|
||||
|
|
|
@ -286,8 +286,26 @@ __PACKAGE__->belongs_to(
|
|||
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "CASCADE" },
|
||||
);
|
||||
|
||||
=head2 starredjobs
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-09-25 14:10:28
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:cAZ4+c7OhqGW8ATru8Foiw
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::StarredJobs>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"starredjobs",
|
||||
"Hydra::Schema::StarredJobs",
|
||||
{
|
||||
"foreign.jobset" => "self.name",
|
||||
"foreign.project" => "self.project",
|
||||
},
|
||||
undef,
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-10-14 15:46:29
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:DTAGxP5RFvcNxP/ciJGo4Q
|
||||
|
||||
1;
|
||||
|
|
|
@ -226,6 +226,21 @@ __PACKAGE__->has_many(
|
|||
undef,
|
||||
);
|
||||
|
||||
=head2 starredjobs
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::StarredJobs>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"starredjobs",
|
||||
"Hydra::Schema::StarredJobs",
|
||||
{ "foreign.project" => "self.name" },
|
||||
undef,
|
||||
);
|
||||
|
||||
=head2 viewjobs
|
||||
|
||||
Type: has_many
|
||||
|
@ -267,8 +282,8 @@ Composing rels: L</projectmembers> -> username
|
|||
__PACKAGE__->many_to_many("usernames", "projectmembers", "username");
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:RffghAo9jAaqYk41y1Sdqw
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-10-14 15:46:29
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:PdNQ2mf5azBB6nI+iAm8fQ
|
||||
# These lines were loaded from '/home/rbvermaa/src/hydra/src/lib/Hydra/Schema/Projects.pm' found in @INC.
|
||||
# They are now part of the custom portion of this file
|
||||
# for you to hand-edit. If you do not either delete
|
||||
|
|
161
src/lib/Hydra/Schema/StarredJobs.pm
Normal file
161
src/lib/Hydra/Schema/StarredJobs.pm
Normal file
|
@ -0,0 +1,161 @@
|
|||
use utf8;
|
||||
package Hydra::Schema::StarredJobs;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Hydra::Schema::StarredJobs
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
=head1 COMPONENTS LOADED
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L<Hydra::Component::ToJSON>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->load_components("+Hydra::Component::ToJSON");
|
||||
|
||||
=head1 TABLE: C<StarredJobs>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("StarredJobs");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 username
|
||||
|
||||
data_type: 'text'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 project
|
||||
|
||||
data_type: 'text'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 jobset
|
||||
|
||||
data_type: 'text'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 job
|
||||
|
||||
data_type: 'text'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"username",
|
||||
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
|
||||
"project",
|
||||
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
|
||||
"jobset",
|
||||
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
|
||||
"job",
|
||||
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</username>
|
||||
|
||||
=item * L</project>
|
||||
|
||||
=item * L</jobset>
|
||||
|
||||
=item * L</job>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("username", "project", "jobset", "job");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 job
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Hydra::Schema::Jobs>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"job",
|
||||
"Hydra::Schema::Jobs",
|
||||
{ jobset => "jobset", name => "job", project => "project" },
|
||||
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "CASCADE" },
|
||||
);
|
||||
|
||||
=head2 jobset
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Hydra::Schema::Jobsets>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"jobset",
|
||||
"Hydra::Schema::Jobsets",
|
||||
{ name => "jobset", project => "project" },
|
||||
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "CASCADE" },
|
||||
);
|
||||
|
||||
=head2 project
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Hydra::Schema::Projects>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"project",
|
||||
"Hydra::Schema::Projects",
|
||||
{ name => "project" },
|
||||
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "CASCADE" },
|
||||
);
|
||||
|
||||
=head2 username
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Hydra::Schema::Users>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"username",
|
||||
"Hydra::Schema::Users",
|
||||
{ username => "username" },
|
||||
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "CASCADE" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-10-14 15:46:29
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:naj5aKWuw8hLE6klmvW9Eg
|
||||
|
||||
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
1;
|
|
@ -135,6 +135,21 @@ __PACKAGE__->has_many(
|
|||
undef,
|
||||
);
|
||||
|
||||
=head2 starredjobs
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::StarredJobs>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"starredjobs",
|
||||
"Hydra::Schema::StarredJobs",
|
||||
{ "foreign.username" => "self.username" },
|
||||
undef,
|
||||
);
|
||||
|
||||
=head2 userroles
|
||||
|
||||
Type: has_many
|
||||
|
@ -161,8 +176,8 @@ Composing rels: L</projectmembers> -> project
|
|||
__PACKAGE__->many_to_many("projects", "projectmembers", "project");
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hy3MKvFxfL+1bTc7Hcb1zA
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-10-14 15:46:29
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Hv9Ukqud0d3uIUot0ErKeg
|
||||
# These lines were loaded from '/home/rbvermaa/src/hydra/src/lib/Hydra/Schema/Users.pm' found in @INC.
|
||||
# They are now part of the custom portion of this file
|
||||
# for you to hand-edit. If you do not either delete
|
||||
|
|
|
@ -459,4 +459,9 @@ BLOCK menuItem %]
|
|||
</li>
|
||||
[% END;
|
||||
|
||||
|
||||
BLOCK makeStar %]
|
||||
<span class="star" data-post="[% starUri %]">[% IF starred; "★"; ELSE; "☆"; END %]</span>
|
||||
[% END;
|
||||
|
||||
%]
|
||||
|
|
42
src/root/dashboard.tt
Normal file
42
src/root/dashboard.tt
Normal file
|
@ -0,0 +1,42 @@
|
|||
[% WRAPPER layout.tt title="Dashboard" %]
|
||||
[% PROCESS common.tt %]
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#tabs-starred-jobs" data-toggle="tab">Starred jobs</a></li>
|
||||
</ul>
|
||||
|
||||
<div id="generic-tabs" class="tab-content">
|
||||
|
||||
<div id="tabs-starred-jobs" class="tab-pane active">
|
||||
|
||||
[% IF starredJobs.size > 0 %]
|
||||
|
||||
<p>Below are the 20 most recent builds of your starred jobs.</p>
|
||||
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr><th>Job</th></tr>
|
||||
</thead>
|
||||
<tdata>
|
||||
[% FOREACH j IN starredJobs %]
|
||||
<tr>
|
||||
<td>[% INCLUDE renderFullJobName project=j.job.get_column('project') jobset=j.job.get_column('jobset') job=j.job.name %]</td>
|
||||
[% FOREACH b IN j.builds %]
|
||||
<td><a href="[% c.uri_for('/build' b.id) %]">[% INCLUDE renderBuildStatusIcon size=16 build=b %]</a></td>
|
||||
[% END %]
|
||||
</tr>
|
||||
[% END %]
|
||||
</tdata>
|
||||
</table>
|
||||
|
||||
[% ELSE %]
|
||||
|
||||
<div class="alert alert-warning">You have no starred jobs. You can add them by visiting a job page and clicking on the ☆ icon.</div>
|
||||
|
||||
[% END %]
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
[% END %]
|
|
@ -1,4 +1,7 @@
|
|||
[% WRAPPER layout.tt title="Job $project.name:$jobset.name:$job.name" %]
|
||||
[% WRAPPER layout.tt
|
||||
title="Job $project.name:$jobset.name:$job.name"
|
||||
starUri=c.uri_for(c.controller('Job').action_for('star'), c.req.captures)
|
||||
%]
|
||||
[% PROCESS common.tt %]
|
||||
[% hideProjectName=1 hideJobsetName=1 hideJobName=1 %]
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
|
||||
[% IF !hideHeader %]
|
||||
<div class="page-header">
|
||||
<h1><small>[% HTML.escape(title) %]</small></h1>
|
||||
<h1><small>[% IF c.user_exists && starUri; INCLUDE makeStar; " "; END; HTML.escape(title) %]</small></h1>
|
||||
</div>
|
||||
[% ELSE %]
|
||||
<br />
|
||||
|
|
|
@ -99,3 +99,12 @@ td.nowrap {
|
|||
.actions {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.star {
|
||||
color: black;
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
.star:hover {
|
||||
cursor: pointer;
|
||||
}
|
|
@ -80,6 +80,23 @@ $(document).ready(function() {
|
|||
$('div[data-toggle="buttons-radio"] .btn').click(function(){
|
||||
$('input', $(this).parent()).val($(this).val());
|
||||
});
|
||||
|
||||
$(".star").click(function(event) {
|
||||
var star = $(this);
|
||||
var active = star.text() != '★';
|
||||
requestJSON({
|
||||
url: star.attr("data-post"),
|
||||
data: active ? "star=1" : "star=0",
|
||||
type: 'POST',
|
||||
success: function(res) {
|
||||
if (active) {
|
||||
star.text('★');
|
||||
} else {
|
||||
star.text('☆');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var tabsLoaded = {};
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
|
||||
<ul class="nav pull-left">
|
||||
|
||||
[% IF c.user_exists %]
|
||||
[% INCLUDE menuItem uri = c.uri_for(c.controller('User').action_for('dashboard'), [c.user.username]) title = "Dashboard" %]
|
||||
[% END %]
|
||||
|
||||
[% WRAPPER makeSubMenu title="Status" %]
|
||||
[% INCLUDE menuItem
|
||||
uri = c.uri_for(c.controller('Root').action_for('queue'))
|
||||
|
|
|
@ -532,6 +532,19 @@ create table AggregateConstituents (
|
|||
);
|
||||
|
||||
|
||||
create table StarredJobs (
|
||||
userName text not null,
|
||||
project text not null,
|
||||
jobset text not null,
|
||||
job text not null,
|
||||
primary key (userName, project, jobset, job),
|
||||
foreign key (userName) references Users(userName) on update cascade on delete cascade,
|
||||
foreign key (project) references Projects(name) on update cascade on delete cascade,
|
||||
foreign key (project, jobset) references Jobsets(project, name) on update cascade on delete cascade,
|
||||
foreign key (project, jobset, job) references Jobs(project, jobset, name) on update cascade on delete cascade
|
||||
);
|
||||
|
||||
|
||||
-- Cache of the number of finished builds.
|
||||
create table NrBuilds (
|
||||
what text primary key not null,
|
||||
|
|
11
src/sql/upgrade-23.sql
Normal file
11
src/sql/upgrade-23.sql
Normal file
|
@ -0,0 +1,11 @@
|
|||
create table StarredJobs (
|
||||
userName text not null,
|
||||
project text not null,
|
||||
jobset text not null,
|
||||
job text not null,
|
||||
primary key (userName, project, jobset, job),
|
||||
foreign key (userName) references Users(userName) on update cascade on delete cascade,
|
||||
foreign key (project) references Projects(name) on update cascade on delete cascade,
|
||||
foreign key (project, jobset) references Jobsets(project, name) on update cascade on delete cascade,
|
||||
foreign key (project, jobset, job) references Jobs(project, jobset, name) on update cascade on delete cascade
|
||||
);
|
Loading…
Reference in a new issue