$Apache2::AuthzCaps::rootdir = $Apache2::Authen::Passphrase::rootdir;
my $word = qr,(\w+),a;
+my $db = Gruntmaster::Data->connect('dbi:Pg:');
sub debug {
local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1;
sub some_auth_required {
my $r = Plack::Request->new($_[0]);
return 1 if $_[0]->{'gruntmaster.reqadmin'} || $r->path eq '/action/passwd' || $r->path =~ m,/pb/$word/submit$,;
- return 1 if $r->path =~ m,^/ct/$word/pb/$word, && time < contest_end $1;
- 0
+ return 1 if $r->path =~ m,^/ct/$word/pb/$word, && time < $db->contest($1)->stop;
+ ''
}
sub admin_required {
local $_ = $_[0];
- return problem_owner $1 if m,^/pb/$word, && problem_private $1;
- return job_user $1 if m,^/log/(?:job|src)/$word, && job_private $1;
- return contest_owner $1 if m,^/ct/$word/(?:pb|log), && time < contest_start $1;
- if (m,^/ct/$word/log/(?:job|src)/$word, && time < contest_end $1){
- local $Gruntmaster::Data::contest = $1;
- return job_user $2;
- }
- 0
+ return $db->problem($1)->owner if m,^/pb/$word, && $db->problem($1)->private;
+ return $db->job ($1)->owner if m,^/log/(?:job|src)/$word, && $db->job($1)->private;
+ return $db->contest($1)->owner if m,^/ct/$word/(?:pb|log), && time < $db->contest($1)->start;
+ return $db->job ($2)->owner if m,^/ct/$word/log/(?:job|src)/$word, && time < $db->contest($1)->stop;
+ ''
}
sub require_admin {
enable 'Log4perl', category => 'plack';
enable \&require_admin;
enable_if \&some_auth_required, 'Auth::Basic', authenticator => \&authenticate, realm => 'Gruntmaster 6000';
+ enable sub { my $app = $_[0]; sub { $_[0]->{'gruntmaster.dbic'} = $db; $app->($_[0]) } };
Plack::App::Gruntmaster->to_app
}
##################################################
use POSIX ();
-use Gruntmaster::Data ();
use List::Util ();
use LWP::UserAgent;
use Plack::Request ();
feature->import(':5.14');
warnings->import;
File::Slurp->export_to_level(1, $caller, qw/read_file/);
- Gruntmaster::Data->export_to_level(1, $caller);
List::Util->export_to_level(1, $caller, qw/sum/);
no strict 'refs';
local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1;
$_[0]->{'psgix.logger'}->({qw/level debug message/ => $_[1]})
};
+ *{"${caller}::db"} = sub { $_[0]->{'gruntmaster.dbic'} };
*{"${caller}::reply"} = sub { [200, ['Content-Type' => 'text/plain', 'Cache-Control' => 'no-cache'], [ @_ ] ] };
*{"${caller}::purge"} = sub {
return unless $ENV{PURGE_HOST};
use warnings;
our $VERSION = '5999.000_001';
-use Gruntmaster::Data;
use Gruntmaster::Page::Base;
use JSON qw/encode_json decode_json/;
+use Plack::Request;
+use Sub::Name qw/subname/;
-sub hgetall {
- my $hash = shift;
- my $cp = $Gruntmaster::Data::contest ? "contest.$Gruntmaster::Data::contest." : '';
- map { { id => $_, HGETALL "$cp$hash.$_" } } SMEMBERS "$cp$hash"
-}
+use constant PAGE_SIZE => 10;
sub putsym {
my ($key, $value) = @_;
no strict 'refs';
+ subname $key => $value if ref $value eq 'CODE';
*{"$key"} = $value;
}
sub list {
my ($thing, $lang, $env, $ct) = @_;
my %thing = %$thing;
- undef $ct unless $thing{contest};
+ my $req = Plack::Request->new($env);
debug $env => "Contest is $ct";
- local $Gruntmaster::Data::contest = $ct if $ct;
- my @thing = hgetall $thing{hash};
+ $thing{makers} //= sub { shift->resultset($thing{rsname}) };
+ my $rs = $thing{makers}->(db $env)->search(undef, {order_by => 'me.id'});
+ if (my $page = $req->param('page')) {
+ my $pages = $rs->count / PAGE_SIZE;
+ $rs = $rs->search(undef, {offset => ($page - 1) * PAGE_SIZE, ($page == $pages ? () : (rows => PAGE_SIZE))});
+ }
+ $rs = $rs->search(undef, {prefetch => $thing{prefetch}}) if exists $thing{prefetch};
+ my @thing = map +{rs => $_, $_->get_columns}, $rs->all;
@thing = map { $thing{mangle}->(); $_ } @thing if exists $thing{mangle};
@thing = grep { $thing{choose}->() } @thing if exists $thing{choose};
@thing = sort { $thing{sortby}->() } @thing if exists $thing{sortby};
sub entry {
my ($thing, $lang, $env, $id, $ct) = @_;
my %thing = %$thing;
- ($id, $ct) = ($ct, $id) if $thing{contest};
- local $Gruntmaster::Data::contest = $ct if $ct;
- debug $env => "Hash is $thing{hash} and id is $id";
- my %params = HGETALL "$thing{hash}.$id";
+ ($id, $ct) = ($ct, $id) if $ct;
+ debug $env => "Rsname is $thing{rsname} and id is $id";
+ $thing{makers} //= sub { shift->resultset($thing{rsname}) };
+ my %params = map {+ rs => $_, $_->get_columns } $thing{makers}->(db $env)->find($id);
+ $params{contest} = $ct if $ct;
$thing{mangle}->(local $_ = \%params) if exists $thing{mangle};
wantarray ? %params : \%params
}
}
sub params;
-sub contest;
+sub makers (&);
sub choose (&);
sub sortby (&);
sub group (&);
sub mangle (&);
+sub prefetch;
sub thing (&){
my %thing;
no strict 'refs';
- local *{"params"} = sub { @thing{qw/id hash title/} = @_ };
+ local *{"params"} = sub { @thing{qw/id rsname title/} = @_ };
local *{"choose"} = sub { $thing{choose} = shift };
local *{"sortby"} = sub { $thing{sortby} = shift };
local *{"mangle"} = sub { $thing{mangle} = shift };
local *{"group"} = sub { $thing{group} = shift };
- local *{"contest"} = sub { $thing{contest} = 1 };
+ local *{"makers"} = sub { $thing{makers} = shift };
+ local *{"prefetch"} = sub { $thing{prefetch} = \@_ };
use strict 'refs';
shift->();
##################################################
thing {
- params qw/us user Users/;
+ params qw/us User Users/;
choose { $_->{name} =~ /\w/ };
sortby { lc $a->{name} cmp lc $b->{name} };
};
thing {
- params qw/pb problem Problems/;
- contest;
- sortby { $a->{name} cmp $b->{name} };
+ params qw/pb Problem Problems/;
+ prefetch 'owner';
+ makers { my ($db, $ct) = @_; $ct ? $db->contest($ct)->problems : $db->problems->search({private => 0}) };
+ sortby { $a->{name} cmp $b->{name}};
group { $_->{level} };
- mangle { $_->{owner_name} = do { local $Gruntmaster::Data::contest; user_name $_->{owner} } }
+ mangle {
+ my $env = shift;
+ $_->{owner_name} = $_->{rs}->owner->name;
+ $_->{cansubmit} = $_->{contest} ? time < $_->{rs}->contest->stop : 1;
+ eval {
+ db($env)->open->create({
+ contest => $_->{contest},
+ problem => $_->{id},
+ owner => $env->{REMOTE_USER},
+ })
+ } if $_->{contest} && time >= $_->{rs}->contest->start;
+ };
};
thing {
- params qw/ct contest Contests/;
+ params qw/ct Contest Contests/;
+ prefetch 'owner';
sortby { $b->{start} <=> $a->{start} };
- group { time < $_->{start} ? 'pending' : time > $_->{end} ? 'finished' : 'running' };
- mangle { $_->{started} = time >= $_->{start}; $_->{owner_name} = do { local $Gruntmaster::Data::contest; user_name $_->{owner} } };
+ group { time < $_->{start} ? 'pending' : time > $_->{stop} ? 'finished' : 'running' };
+ mangle { $_->{started} = time >= $_->{start}; $_->{owner_name} = $_->{rs}->owner->name };
};
thing {
- params qw/log job/, 'Job log';
- contest;
- mangle { $_->{results} &&= decode_json $_->{results}; $_->{user_name} = do { local $Gruntmaster::Data::contest; user_name $_->{user} } }
+ params qw/log Job/, 'Job log';
+ prefetch 'owner', 'problem';
+ makers { shift->jobs->search({contest => shift}) };
+ sortby { $b->{id} <=> $a->{id}};
+ mangle {
+ $_->{results} &&= decode_json $_->{results};
+ $_->{owner_name} = $_->{rs}->owner->name;
+ $_->{problem_name} = $_->{rs}->problem->name;
+ $_->{size} = length $_->{source};
+ delete $_->{source};
+ }
};
+putsym 'Gruntmaster::Page::Pb::Entry::vary', sub { 'Authorization' };
+putsym 'Gruntmaster::Page::Pb::Entry::max_age', sub { 600 };
+
1
+++ /dev/null
-package Gruntmaster::Page::Log;
-
-use Gruntmaster::Page::Base log => 'Job log';
-
-use constant PAGE_SIZE => 10;
-
-sub _generate{
- my ($self, $htc, $lang, $env, $ct, $page) = @_;
- debug $env => "language is '$lang', contest is '$ct' and page is '$page'";
- local $Gruntmaster::Data::contest = $ct if $ct;
-
- my $pages = POSIX::floor (jobcard / PAGE_SIZE);
- $pages ||= 1;
- $page ||= $pages;
-
- my @log = sort { $b->{id} <=> $a->{id} } map +{
- id => $_,
- (job_private() ? (private => job_private) : ()),
- date => (job_date() ? strftime ('%c' => localtime job_date) : '?'),
- extension => job_extension,
- name => problem_name job_problem,
- problem => job_problem,
- result => job_result,
- result_text => job_result_text,
- size => sprintf ("%.2f KiB", job_filesize() / 1024),
- user => job_user}, ($page - 1) * PAGE_SIZE + 1 .. ($page == $pages ? jobcard : $page * PAGE_SIZE);
- $_->{user_name} = do { local $Gruntmaster::Data::contest; user_name $_->{user} } for @log;
- $htc->param(log => \@log);
- $htc->param(next => $page + 1) unless $page == $pages;
- $htc->param(prev => $page - 1) unless $page == 1;
-}
-
-sub max_age { 5 }
-
-1
+++ /dev/null
-package Gruntmaster::Page::Pb::Entry;
-
-use Gruntmaster::Page::Base pb_entry => '<tmpl_var name>';
-
-use constant FORMATS => [qw/C CPP JAVA PERL PYTHON/];
-
-sub _generate{
- my ($self, $htc, $lang, $env, $contest, $id) = @_;
- debug $env => "language is '$lang', contest is '$contest', id is '$id'";
- my $user = $env->{REMOTE_USER};
- if ($contest && $user && time >= contest_start $contest) {
- local $Gruntmaster::Data::contest = $contest;
- mark_open $id, $user;
- debug $env => "Marking problem $id of contest $contest open by $user";
- }
-
- $htc->param(cansubmit => 1);
- if ($contest) {
- $htc->param(cansubmit => time <= contest_end $contest);
- $htc->param(contest => $contest);
- }
- $htc->param(formats => FORMATS);
- $htc->param(id => $id);
- local $Gruntmaster::Data::contest = $contest if $contest;
- $htc->param(name => problem_name $id);
- $htc->param(author => problem_author $id);
- $htc->param(owner => problem_owner $id);
- $htc->param(owner_name => do{ local $Gruntmaster::Data::contest; user_name $htc->param('owner')} );
- $htc->param(statement => problem_statement $id);
-}
-
-sub vary { 'Authorization' }
-sub max_age { 600 }
-
-1
return reply 'All fields are required' if grep { !length } $username, $password, $confirm_password, $name, $email, $phone, $town, $university, $level;
pwset $username, $password;
- insert_user $username, name => $name, email => $email, phone => $phone, town => $town, university => $university, level => $level;
+ db($env)->create({id => $username, name => $name, email => $email, phone => $phone, town => $town, university => $university, level => $level});
purge "/us/";
reply 'Registered successfully';
sub generate{
my ($self, $format, $env, $ct, $job, $ext) = @_;
debug $env => "Contest is $ct, job is $job and extension is $ext";
- local $Gruntmaster::Data::contest = $ct if $ct;
- [200, ['Content-Type' => CONTENT_TYPES->{$ext}, 'Cache-Control' => 'max-age=604800', 'X-Forever' => 1], [job_inmeta($job)->{files}{prog}{content}] ]
+ [200, ['Content-Type' => CONTENT_TYPES->{$ext}, 'Cache-Control' => 'max-age=604800', 'X-Forever' => 1], [db($env)->job($job)->source] ]
}
1
};
sub calc_score{
- my ($user, $problem, $date, $tries, $totaltime) = @_;
- my $mxscore = LEVEL_VALUES->{problem_level($problem)};
+ my ($mxscore, $time, $tries, $totaltime) = @_;
my $score = $mxscore;
- my $timetaken = $date - get_open($problem, $user);
- $timetaken = 0 if $timetaken < 0;
- $timetaken = 300 if $timetaken > $totaltime;
- $score = ($totaltime - $timetaken) / $totaltime * $score;
+ $time = 0 if $time < 0;
+ $time = 300 if $time > $totaltime;
+ $score = ($totaltime - $time) / $totaltime * $score;
$score -= $tries / 10 * $mxscore;
$score = $mxscore * 3 / 10 if $score < $mxscore * 3 / 10;
int $score + 0.5
sub _generate{
my ($self, $htc, $lang, $env, $ct) = @_;
debug $env => "language is '$lang' and contest is '$ct'";
- my ($totaltime, $start);
- local $Gruntmaster::Data::contest;
- if ($ct) {
- $start = contest_start ($ct);
- $totaltime = contest_end ($ct) - $start;
- $Gruntmaster::Data::contest = $ct;
- }
+ $ct &&= db($env)->contest($ct);
- my @problems = problems;
- @problems = sort @problems;
+ my @problems = map { $_->problem } db($env)->contest_problems->search({contest => $ct->id}, {qw/join problem order_by problem.level/});
my (%scores, %tries);
- for (1 .. jobcard) {
- next unless defined job_user && defined job_problem && defined job_result;
- next if $Gruntmaster::Data::contest && job_date() < $start;
-
- if ($Gruntmaster::Data::contest) {
- $scores{job_user()}{job_problem()} = job_result() ? 0 : calc_score (job_user(), job_problem(), job_date(), $tries{job_user()}{job_problem()}, $totaltime);
- $tries{job_user()}{job_problem()}++;
+ for my $job (db($env)->jobs->search({contest => $ct->id})) {
+
+ if ($ct) {
+ my $time = $job->date - $ct->start;
+ next if $time < 0;
+ my $value = $job->problem->value // LEVEL_VALUES->{$job->problem->level};
+ $scores{$job->owner->id}{$job->problem->id} = $job->result ? 0 : calc_score ($value, $time, $tries{$job->owner}{$job->problem}, $ct->stop - $ct->start);
+ $tries{$job->owner->id}{$job->problem->id}++;
} else {
no warnings 'numeric';
- $scores{job_user()}{job_problem()} = 0 + job_result_text() || (job_result() ? 0 : 100)
+ $scores{$job->owner->id}{$job->problem->id} = 0 + $job->result_text || ($job->result ? 0 : 100)
}
}
my @st = sort { $b->{score} <=> $a->{score} or $a->{user} cmp $b->{user}} map {
my $user = $_;
+{
- user => $user,
- name => do {local $Gruntmaster::Data::contest; user_name $user},
+ user => db($env)->user($user),
score => sum (values $scores{$user}),
- scores => [map { $scores{$user}{$_} // '-'} @problems],
- problems => $Gruntmaster::Data::contest,
+ scores => [map { $scores{$user}{$_->id} // '-'} @problems],
+ problems => $ct,
}
} keys %scores;
$st[0]->{rank} = 1;
$st[$_]->{rank} = $st[$_ - 1]->{rank} + ($st[$_]->{score} < $st[$_ - 1]->{score}) for 1 .. $#st;
- $htc->param(problems => [map { problem_name } @problems ]) if $Gruntmaster::Data::contest;
+ $htc->param(problems => \@problems) if $ct;
$htc->param(st => \@st);
}
$prog = $temp if $temp
}
die if defined $contest && $contest !~ /^\w+$/ ;
- die if defined $contest && (time > contest_end $contest);
+ die if defined $contest && (time > db($env)->contest($contest)->stop);
return reply 'A required parameter was not supplied' if grep { !defined } $problem, $format, $prog;
return reply 'Maximum source size is 10KB' if length $prog > 25 * 1024;
- return reply 'You must wait 30 seconds between jobs' unless time > user_lastjob ($r->user) + 30;
- set_user_lastjob $r->user, time;
+ return reply 'You must wait 30 seconds between jobs' unless time > db($env)->user($r->user)->lastjob + 30;
+ db($env)->user($r->user)->lastjob(time)->update;
- local $Gruntmaster::Data::contest = $contest if $contest;
-
- my $job = push_job (
+ db($env)->jobs->create({
+ defined $contest ? (contest => $contest) : (),
date => time,
- problem => $problem,
- user => $r->user,
- defined $private ? (private => $private) : (),
- defined $contest ? (contest => $contest, private => 1) : (),
- filesize => length $prog,
extension => FORMAT_EXTENSION->{$format},
- );
-
- set_job_inmeta $job, {
- files => {
- prog => {
- format => $format,
- name => 'prog.' . FORMAT_EXTENSION->{$format},
- content => $prog,
- }
- }
- };
+ format => $format,
+ defined $private ? (private => $private) : (),
+ probem => $problem,
+ source => $prog,
+ user => $r->user
+ });
$contest //= '';
- PUBLISH 'jobs', "$contest.$job";
+ #PUBLISH 'jobs', "$contest.$job";
[303, [Location => $r->path =~ s,/pb/\w+/submit$,/log/,r], ['']]
}
use File::Slurp qw/read_file/;
use HTTP::Negotiate qw/choose/;
use Plack::Request;
-use Gruntmaster::Page::Log;
-use Gruntmaster::Page::Pb::Entry;
use Gruntmaster::Page::Generic;
my %handlers;
<h1>Running contests</h1>
<table border class="table table-bordered table-striped">
<thead>
-<tr><th>Name<th>Start date<th>End date<th>Owner
+<tr><th>Name<th>Start date<th>Stop date<th>Owner
<tbody>
<tmpl_loop running><tr><td><a href="<tmpl_var id>/"><tmpl_var name></a>
<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
-<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{end}; %>
+<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{stop}; %>
<td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a>
</tmpl_loop>
</table>
</tmpl_if>
-<tmpl_if pending>
-<h1>Pending contests</h1>
+<tmpl_if pstoping>
+<h1>Pstoping contests</h1>
<table border class="table table-bordered table-striped">
<thead>
-<tr><th>Name<th>Start date<th>End date<th>Owner
+<tr><th>Name<th>Start date<th>Stop date<th>Owner
<tbody>
-<tmpl_loop pending><tr><td><a href="<tmpl_var id>/"><tmpl_var name></a>
+<tmpl_loop pstoping><tr><td><a href="<tmpl_var id>/"><tmpl_var name></a>
<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
-<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{end}; %>
+<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{stop}; %>
<td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a>
</tmpl_loop>
</table>
<h1>Finished contests</h1>
<table border class="table table-bordered table-striped">
<thead>
-<tr><th>Name<th>Start date<th>End date<th>Owner
+<tr><th>Name<th>Start date<th>Stop date<th>Owner
<tbody>
<tmpl_loop finished><tr><td><a href="<tmpl_var id>/"><tmpl_var name></a>
<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
-<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{end}; %>
+<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{stop}; %>
<td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a>
</tmpl_loop>
</table>
Contest start time: <%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %><br>
-Contest end time: <%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{end}; %><p>
+Contest stop time: <%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{stop}; %><p>
<tmpl_var ESCAPE=0 description>
<tr><th>ID<th>Problem<th>Date<th>Size<th>User<th>Result
<tbody>
<tmpl_loop log><tr><td><a href="job/<tmpl_var id>"><tmpl_var id></a>
-<td><a href="../pb/<tmpl_var problem>"><tmpl_var name></a>
-<td><tmpl_var date>
-<td><a href="src/<tmpl_var id>.<tmpl_var extension>"<tmpl_if private> data-private</tmpl_if>><tmpl_var size></a>
-<td><a href="/us/<tmpl_var user>"><tmpl_if user_name><tmpl_var user_name><tmpl_else><tmpl_var user></tmpl_if></a><td class="r<tmpl_var result>"><tmpl_var result_text>
+<td><a href="../pb/<tmpl_var problem>"><tmpl_var problem_name></a>
+<td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{date}; %>
+<td><a href="src/<tmpl_var id>.<tmpl_var extension>"<tmpl_if private> data-private</tmpl_if>><%perl __OUT__ sprintf '%.2fKB', __CURRENT__->{size}/1024; %></a>
+<td><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var user></tmpl_if></a>
+<td class="r<tmpl_var result>"><tmpl_var result_text>
</tmpl_loop>
</table>
<div class="form-group"><label for="prog">File:</label><input id="prog" name="prog" type="file"></div>
<div class="form-group"><label for="source_code">Source code:</label> <textarea class="form-control" id="source_code" name="source_code"></textarea></div>
<div class="form-group"><label for="prog_format">File format:</label><select id="prog_format" name="prog_format" class="form-control" required>
-<tmpl_loop formats><option value="<tmpl_var _>"><tmpl_var _></option>
-</tmpl_loop></select></div>
+<option value="C">C</option>
+<option value="CPP">C++</option>
+<option value="JAVA">Java</option>
+<option value="PASCAL">Pascal</option>
+<option value="PERL">Perl</option>
+<option value="PYTHON">Python</option>
+</select></div>
<input type="submit" value="Submit job" class="btn btn-primary">
</form>
<table border class="table table-border table-striped">
<thead>
-<tmpl_if problems><tr><th>Rank<th>User<tmpl_loop problems><th><tmpl_var _></tmpl_loop><th>Total
+<tmpl_if problems><tr><th>Rank<th>User<tmpl_loop problems><th><a href="../pb/<tmpl_var _.id>"><tmpl_var _.name></a></tmpl_loop><th>Total
<tmpl_else><tr><th>Rank<th>User<th>Score
</tmpl_if>
<tbody>
-<tmpl_loop st><tr><td><tmpl_var rank><td><a href="/us/<tmpl_var user>"><tmpl_if name><tmpl_var name><tmpl_else><tmpl_var user></tmpl_if></a>
+<tmpl_loop st><tr><td><tmpl_var rank><td><a href="/us/<tmpl_var user.id>"><tmpl_if user.name><tmpl_var user.name><tmpl_else><tmpl_var user.id></tmpl_if></a>
<tmpl_if problems><tmpl_loop scores><td><tmpl_var _>
</tmpl_loop></tmpl_if><td><tmpl_var score>
</tmpl_loop>