From 2127d133cda796f1dea4c1b5016fbc99ea58aa47 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 14 Oct 2013 20:07:26 +0200 Subject: [PATCH] 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. --- src/lib/Hydra/Controller/Job.pm | 24 +++++ src/lib/Hydra/Controller/User.pm | 15 +++ src/lib/Hydra/Schema/Jobs.pm | 23 +++- src/lib/Hydra/Schema/Jobsets.pm | 22 +++- src/lib/Hydra/Schema/Projects.pm | 19 +++- src/lib/Hydra/Schema/StarredJobs.pm | 161 ++++++++++++++++++++++++++++ src/lib/Hydra/Schema/Users.pm | 19 +++- src/root/common.tt | 5 + src/root/dashboard.tt | 42 ++++++++ src/root/job.tt | 5 +- src/root/layout.tt | 2 +- src/root/static/css/hydra.css | 11 +- src/root/static/js/common.js | 17 +++ src/root/topbar.tt | 4 + src/sql/hydra.sql | 13 +++ src/sql/upgrade-23.sql | 11 ++ 16 files changed, 382 insertions(+), 11 deletions(-) create mode 100644 src/lib/Hydra/Schema/StarredJobs.pm create mode 100644 src/root/dashboard.tt create mode 100644 src/sql/upgrade-23.sql diff --git a/src/lib/Hydra/Controller/Job.pm b/src/lib/Hydra/Controller/Job.pm index 66a12e5f..2758345c 100644 --- a/src/lib/Hydra/Controller/Job.pm +++ b/src/lib/Hydra/Controller/Job.pm @@ -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; diff --git a/src/lib/Hydra/Controller/User.pm b/src/lib/Hydra/Controller/User.pm index c43f7d16..f0930e93 100644 --- a/src/lib/Hydra/Controller/User.pm +++ b/src/lib/Hydra/Controller/User.pm @@ -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; diff --git a/src/lib/Hydra/Schema/Jobs.pm b/src/lib/Hydra/Schema/Jobs.pm index 6a703588..dcfa557e 100644 --- a/src/lib/Hydra/Schema/Jobs.pm +++ b/src/lib/Hydra/Schema/Jobs.pm @@ -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 + +=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; diff --git a/src/lib/Hydra/Schema/Jobsets.pm b/src/lib/Hydra/Schema/Jobsets.pm index f31a75f7..658e48ee 100644 --- a/src/lib/Hydra/Schema/Jobsets.pm +++ b/src/lib/Hydra/Schema/Jobsets.pm @@ -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 + +=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; diff --git a/src/lib/Hydra/Schema/Projects.pm b/src/lib/Hydra/Schema/Projects.pm index fcc87a30..b0e4fe1e 100644 --- a/src/lib/Hydra/Schema/Projects.pm +++ b/src/lib/Hydra/Schema/Projects.pm @@ -226,6 +226,21 @@ __PACKAGE__->has_many( undef, ); +=head2 starredjobs + +Type: has_many + +Related object: L + +=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 -> 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 diff --git a/src/lib/Hydra/Schema/StarredJobs.pm b/src/lib/Hydra/Schema/StarredJobs.pm new file mode 100644 index 00000000..51bde91f --- /dev/null +++ b/src/lib/Hydra/Schema/StarredJobs.pm @@ -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 + +=back + +=cut + +__PACKAGE__->load_components("+Hydra::Component::ToJSON"); + +=head1 TABLE: C + +=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 + +=item * L + +=item * L + +=item * L + +=back + +=cut + +__PACKAGE__->set_primary_key("username", "project", "jobset", "job"); + +=head1 RELATIONS + +=head2 job + +Type: belongs_to + +Related object: L + +=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 + +=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 + +=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 + +=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; diff --git a/src/lib/Hydra/Schema/Users.pm b/src/lib/Hydra/Schema/Users.pm index 2fac38ed..032023a4 100644 --- a/src/lib/Hydra/Schema/Users.pm +++ b/src/lib/Hydra/Schema/Users.pm @@ -135,6 +135,21 @@ __PACKAGE__->has_many( undef, ); +=head2 starredjobs + +Type: has_many + +Related object: L + +=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 -> 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 diff --git a/src/root/common.tt b/src/root/common.tt index 9b2b9dcf..48366567 100644 --- a/src/root/common.tt +++ b/src/root/common.tt @@ -459,4 +459,9 @@ BLOCK menuItem %] [% END; + +BLOCK makeStar %] +[% IF starred; "★"; ELSE; "☆"; END %] +[% END; + %] diff --git a/src/root/dashboard.tt b/src/root/dashboard.tt new file mode 100644 index 00000000..d33eb887 --- /dev/null +++ b/src/root/dashboard.tt @@ -0,0 +1,42 @@ +[% WRAPPER layout.tt title="Dashboard" %] +[% PROCESS common.tt %] + + + +
+ +
+ + [% IF starredJobs.size > 0 %] + +

Below are the 20 most recent builds of your starred jobs.

+ + + + + + + [% FOREACH j IN starredJobs %] + + + [% FOREACH b IN j.builds %] + + [% END %] + + [% END %] + +
Job
[% INCLUDE renderFullJobName project=j.job.get_column('project') jobset=j.job.get_column('jobset') job=j.job.name %][% INCLUDE renderBuildStatusIcon size=16 build=b %]
+ + [% ELSE %] + +
You have no starred jobs. You can add them by visiting a job page and clicking on the ☆ icon.
+ + [% END %] + +
+ +
+ +[% END %] diff --git a/src/root/job.tt b/src/root/job.tt index b7eb3f97..8369efc6 100644 --- a/src/root/job.tt +++ b/src/root/job.tt @@ -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 %] diff --git a/src/root/layout.tt b/src/root/layout.tt index b2b76910..0fd1397f 100644 --- a/src/root/layout.tt +++ b/src/root/layout.tt @@ -82,7 +82,7 @@ [% IF !hideHeader %] [% ELSE %]
diff --git a/src/root/static/css/hydra.css b/src/root/static/css/hydra.css index c407e439..4fac2c3c 100644 --- a/src/root/static/css/hydra.css +++ b/src/root/static/css/hydra.css @@ -97,5 +97,14 @@ td.nowrap { } .actions { - font-weight:bold; + font-weight: bold; } + +.star { + color: black; + font-size: 110%; +} + +.star:hover { + cursor: pointer; +} \ No newline at end of file diff --git a/src/root/static/js/common.js b/src/root/static/js/common.js index c304d658..891fd4e6 100644 --- a/src/root/static/js/common.js +++ b/src/root/static/js/common.js @@ -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 = {}; diff --git a/src/root/topbar.tt b/src/root/topbar.tt index f3a8a89d..28e3885c 100644 --- a/src/root/topbar.tt +++ b/src/root/topbar.tt @@ -9,6 +9,10 @@