use parent qw/Exporter/;
our $VERSION = '5999.000_013';
-our @EXPORT = qw/purge db user_list user_entry problem_list problem_entry contest_list contest_entry contest_full contest_has_problem job_list job_entry job_full create_job standings update_status rerun_job/; ## no critic (ProhibitAutomaticExportation)
+our @EXPORT = qw/purge db user_list user_entry problem_list problem_entry problem_full contest_list contest_entry contest_full contest_has_problem job_list job_entry job_full create_job standings update_status rerun_job take_job finish_job/; ## no critic (ProhibitAutomaticExportation)
use JSON::MaybeXS qw/decode_json/;
use HTTP::Tiny;
opens_sth => 'SELECT problem,owner,time FROM opens WHERE contest = ?',
problem_entry_sth => 'SELECT ' . (join ',', @{PROBLEM_PUBLIC_COLUMNS()}, 'statement', 'solution') . ' FROM problems WHERE id = ?',
+ problem_full_sth => 'SELECT * FROM problems WHERE id = ?',
limits_sth => 'SELECT format,timeout FROM limits WHERE problem = ?',
problem_values_sth => 'SELECT id,value FROM problems',
job_full_sth => 'SELECT * FROM jobs WHERE id = ?',
rerun_job_sth => 'UPDATE jobs SET daemon=NULL,result=-2,result_text=NULL,results=NULL,errors=NULL WHERE id = ?',
+ take_job_sth => 'UPDATE jobs SET daemon=? WHERE id = (SELECT id FROM jobs WHERE daemon IS NULL LIMIT 1 FOR UPDATE) RETURNING id',
);
our $db;
sub add_names ($) { ## no critic (ProhibitSubroutinePrototypes)
my ($el) = @_;
+ return unless defined $el;
if (ref $el eq 'ARRAY') {
&add_names ($_) for @$el ## no critic (ProhibitAmpersandSigils)
} else {
$el
}
-sub user_list { +{us => scalar query('user_list_sth')->hashes} }
+sub user_list { scalar query('user_list_sth')->hashes }
sub user_entry {
my ($id) = @_;
$where{owner} = $args{owner} if $args{owner};
my $table = $args{contest} ? 'problems JOIN contest_problems cp ON cp.problem = id' : 'problems';
- my $ret = add_names $db->select(\$table, \@columns, \%where, 'name')->hashes;
-
- my %params;
- for (@$ret) {
- $params{$_->{level}} //= [];
- push @{$params{$_->{level}}}, $_
- }
- \%params
+ add_names $db->select(\$table, \@columns, \%where, 'name')->hashes
}
sub problem_entry {
$ret
}
-sub contest_list {
- my $ret = add_names query('contest_list_sth')->hashes;
+sub problem_full { scalar query(problem_full_sth => $_[0])->hash }
- my %ret;
- for (@$ret) {
- my $state = $_->{finished} ? 'finished' : $_->{started} ? 'running' : 'pending';
- $ret{$state} //= [];
- push @{$ret{$state}}, $_;
- }
-
- \%ret
-}
+sub contest_list { add_names query('contest_list_sth')->hashes }
-sub contest_entry {
- my ($id) = @_;
- add_names query(contest_entry_sth => $id)->hash;
-}
+sub contest_entry { add_names query(contest_entry_sth => $_[0])->hash }
-sub contest_full {
- my ($id) = @_;
- scalar query(contest_full_sth => $id)->hash;
-}
+sub contest_full { scalar query(contest_full_sth => $_[0])->hash }
-sub contest_has_problem {
- my ($contest, $problem) = @_;
- query('contest_has_problem_sth', $contest, $problem)->flat
-}
+sub contest_has_problem { query('contest_has_problem_sth', @_[0, 1])->flat }
sub job_list {
my (%args) = @_;
- $args{page} //= 1;
+ $args{page} = int ($args{page} // 1);
my %where = (
maybe contest => $args{contest},
maybe owner => $args{owner},
my $rows = $db->select('job_entry', 'COUNT(*)', \%where)->list;
my $pages = int (($rows + JOBS_PER_PAGE - 1) / JOBS_PER_PAGE);
my ($stmt, @bind) = $db->abstract->select('job_entry', '*', \%where, {-desc => 'id'});
- my $jobs = $db->query("$stmt LIMIT " . JOBS_PER_PAGE . ' OFFSET ' . ($args{page} - 1) * JOBS_PER_PAGE, @bind)->hashes;
- my %ret = (
- log => add_names $jobs,
+ my $jobs = add_names $db->query("$stmt LIMIT " . JOBS_PER_PAGE . ' OFFSET ' . ($args{page} - 1) * JOBS_PER_PAGE, @bind)->hashes;
+ my $pageinfo = {
current_page => $args{page},
last_page => $pages,
- );
- $ret{previous_page} = $args{page} - 1 if $args{page} - 1;
- $ret{next_page} = $args{page} + 1 if $args{page} < $pages;
-
- \%ret;
+ ($args{page} - 1) ? (previous_page => $args{page} - 1) : (),
+ ($args{page} < $pages) ? (next_page => $args{page} + 1) : (),
+ };
+ wantarray ? ($jobs, $pageinfo) : $jobs;
}
sub job_entry {
- my ($id) = @_;
- my $ret = add_names query(job_entry_sth => $id)->hash;
+ my $ret = add_names query(job_entry_sth => $_[0])->hash;
$ret->{results} = decode_json $ret->{results} if $ret->{results};
$ret
}
-sub job_full {
- my ($id) = @_;
- scalar query(job_full_sth => $id)->hash
-}
+sub job_full { scalar query(job_full_sth => $_[0])->hash }
sub create_job {
my (%args) = @_;
- $db->update('users', {lastjob => time});
+ $db->update('users', {lastjob => time}, {id => $args{owner}});
purge '/log/';
scalar $db->insert('jobs', \%args, {returning => 'id'})->list
}
sub rerun_job {
my ($id) = @_;
- $db->query(rerun_job_sth => $id);
+ query rerun_job_sth => $id;
purge '/log/';
purge "/log/$id";
}
+sub take_job {
+ my ($daemon) = @_;
+ my $id = query(take_job_sth => $daemon)->list;
+ return $id ? job_full $id : undef;
+}
+
+sub finish_job {
+ my ($job, $private, %args) = @_;
+ db->update(jobs => \%args, {id => $job->{id}});
+ return if $private;
+ my $status = {
+ problem => $job->{problem},
+ owner => $job->{owner},
+ job => $job->{id},
+ solved => ($args{result} ? 0 : 1),
+ };
+ eval {
+ db->insert(problem_status => $status)
+ } or db->update(problem_status => $status, {owner => $job->{owner}, problem => $job->{problem}});
+}
+
my @PURGE_HOSTS = exists $ENV{PURGE_HOSTS} ? split ' ', $ENV{PURGE_HOSTS} : ();
my $ht = HTTP::Tiny->new;