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
|
@ -60,6 +60,12 @@ sub overview : Chained('job') PathPart('') Args(0) {
|
||||||
|
|
||||||
$c->stash->{aggregates} = $aggregates;
|
$c->stash->{aggregates} = $aggregates;
|
||||||
$c->stash->{constituentJobs} = [sort (keys %constituentJobs)];
|
$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;
|
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;
|
1;
|
||||||
|
|
|
@ -137,8 +137,27 @@ __PACKAGE__->belongs_to(
|
||||||
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "CASCADE" },
|
{ 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
|
Type: has_many
|
||||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:t2CCfUjFEz/lO4szROz1AQ
|
|
||||||
|
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;
|
1;
|
||||||
|
|
|
@ -286,8 +286,26 @@ __PACKAGE__->belongs_to(
|
||||||
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "CASCADE" },
|
{ 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
|
Type: has_many
|
||||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:cAZ4+c7OhqGW8ATru8Foiw
|
|
||||||
|
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;
|
1;
|
||||||
|
|
|
@ -226,6 +226,21 @@ __PACKAGE__->has_many(
|
||||||
undef,
|
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
|
=head2 viewjobs
|
||||||
|
|
||||||
Type: has_many
|
Type: has_many
|
||||||
|
@ -267,8 +282,8 @@ Composing rels: L</projectmembers> -> username
|
||||||
__PACKAGE__->many_to_many("usernames", "projectmembers", "username");
|
__PACKAGE__->many_to_many("usernames", "projectmembers", "username");
|
||||||
|
|
||||||
|
|
||||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
|
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-10-14 15:46:29
|
||||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:RffghAo9jAaqYk41y1Sdqw
|
# 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.
|
# 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
|
# They are now part of the custom portion of this file
|
||||||
# for you to hand-edit. If you do not either delete
|
# 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,
|
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
|
=head2 userroles
|
||||||
|
|
||||||
Type: has_many
|
Type: has_many
|
||||||
|
@ -161,8 +176,8 @@ Composing rels: L</projectmembers> -> project
|
||||||
__PACKAGE__->many_to_many("projects", "projectmembers", "project");
|
__PACKAGE__->many_to_many("projects", "projectmembers", "project");
|
||||||
|
|
||||||
|
|
||||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-06-13 01:54:50
|
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2013-10-14 15:46:29
|
||||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hy3MKvFxfL+1bTc7Hcb1zA
|
# 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.
|
# 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
|
# They are now part of the custom portion of this file
|
||||||
# for you to hand-edit. If you do not either delete
|
# for you to hand-edit. If you do not either delete
|
||||||
|
|
|
@ -459,4 +459,9 @@ BLOCK menuItem %]
|
||||||
</li>
|
</li>
|
||||||
[% END;
|
[% 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 %]
|
[% PROCESS common.tt %]
|
||||||
[% hideProjectName=1 hideJobsetName=1 hideJobName=1 %]
|
[% hideProjectName=1 hideJobsetName=1 hideJobName=1 %]
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@
|
||||||
|
|
||||||
[% IF !hideHeader %]
|
[% IF !hideHeader %]
|
||||||
<div class="page-header">
|
<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>
|
</div>
|
||||||
[% ELSE %]
|
[% ELSE %]
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -97,5 +97,14 @@ td.nowrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
font-weight:bold;
|
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(){
|
$('div[data-toggle="buttons-radio"] .btn').click(function(){
|
||||||
$('input', $(this).parent()).val($(this).val());
|
$('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 = {};
|
var tabsLoaded = {};
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
|
|
||||||
<ul class="nav pull-left">
|
<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" %]
|
[% WRAPPER makeSubMenu title="Status" %]
|
||||||
[% INCLUDE menuItem
|
[% INCLUDE menuItem
|
||||||
uri = c.uri_for(c.controller('Root').action_for('queue'))
|
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.
|
-- Cache of the number of finished builds.
|
||||||
create table NrBuilds (
|
create table NrBuilds (
|
||||||
what text primary key not null,
|
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