]> iEval git - gruntmaster-page.git/commitdiff
From Redis to Postgres - Part 2 (Better routes)
authorMarius Gavrilescu <marius@ieval.ro>
Wed, 19 Mar 2014 12:36:08 +0000 (14:36 +0200)
committerMarius Gavrilescu <marius@ieval.ro>
Wed, 19 Mar 2014 12:36:08 +0000 (14:36 +0200)
app.psgi
lib/Gruntmaster/Page/Generic.pm
lib/Plack/App/Gruntmaster.pm
tmpl/ct.en
tmpl/ct_entry.en
tmpl/log.en
tmpl/pb.en
tmpl/pb_entry.en
tmpl/st.en
tmpl/us.en
tmpl/us_entry.en

index b657a9abcd09cd6d0655faaf3be4ae93931d2504..2e3f6405c7b7848c5ee1e8ffde433430f92e2260 100644 (file)
--- a/app.psgi
+++ b/app.psgi
@@ -1,5 +1,6 @@
 #!/usr/bin/perl -w
 use v5.14;
 #!/usr/bin/perl -w
 use v5.14;
+no if $] >= 5.017011, warnings => 'experimental::smartmatch';
 
 use Apache2::Authen::Passphrase qw/pwcheck/;
 use Apache2::AuthzCaps qw/hascaps/;
 
 use Apache2::Authen::Passphrase qw/pwcheck/;
 use Apache2::AuthzCaps qw/hascaps/;
@@ -49,6 +50,22 @@ sub require_admin {
        }
 }
 
        }
 }
 
+sub mangle_request {
+       my $app = $_[0];
+       sub {
+               local *__ANON__ = 'mangle_request_middleware';
+               my $env = $_[0];
+               my ($number, $word) = (qr,(\d+),a, qr,(\w+),a);
+               for ($env->{PATH_INFO}) {
+                       $env->{'gruntmaster.page'}    = $1 if s,/page/$number$,/,;
+                       $env->{'gruntmaster.problem'} = $1 if s,^/pb/$word/,/,;
+                       $env->{'gruntmaster.contest'} = $1 if s,^/ct/$word/,/,;
+                       $env->{'gruntmaster.user'}    = $1 if s,^/us/$word/,/,;
+               }
+               $app->($env);
+       }
+}
+
 my %authen_cache;
 
 sub authenticate {
 my %authen_cache;
 
 sub authenticate {
@@ -84,6 +101,7 @@ builder {
        enable 'Log4perl', category => 'plack';
        enable \&require_admin;
        enable_if \&some_auth_required, 'Auth::Basic', authenticator => \&authenticate, realm => 'Gruntmaster 6000';
        enable 'Log4perl', category => 'plack';
        enable \&require_admin;
        enable_if \&some_auth_required, 'Auth::Basic', authenticator => \&authenticate, realm => 'Gruntmaster 6000';
+       enable \&mangle_request;
        enable sub { my $app = $_[0]; sub { $_[0]->{'gruntmaster.dbic'} = $db; $app->($_[0]) } };
        Plack::App::Gruntmaster->to_app
 }
        enable sub { my $app = $_[0]; sub { $_[0]->{'gruntmaster.dbic'} = $db; $app->($_[0]) } };
        Plack::App::Gruntmaster->to_app
 }
index 2cb82d91d86720a5e79e3ff3573a72e83f34e9d0..256add635bed2a31ed85d929cab7e710722ea57f 100644 (file)
@@ -29,13 +29,13 @@ sub makepkg {
 }
 
 sub list {
 }
 
 sub list {
-       my ($thing, $lang, $env, $ct) = @_;
+       my ($thing, $lang, $env) = @_;
        my %thing = %$thing;
        my %thing = %$thing;
-       my $req = Plack::Request->new($env);
-       debug $env => "Contest is $ct";
-       $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')) {
+       #debug $env => "Contest is $ct";
+       $thing{makers} //= sub { db(shift)->resultset($thing{rsname}) };
+       my $rs = $thing{makers}->($env);
+       $rs = $rs->search(undef, {order_by => 'me.id'}) unless $rs->is_ordered;
+       if (my $page = $env->{'gruntmaster.page'}) {
                my $pages = $rs->count / PAGE_SIZE;
                $rs = $rs->search(undef, {offset => ($page - 1) * PAGE_SIZE, ($page == $pages ? () : (rows => PAGE_SIZE))});
        }
                my $pages = $rs->count / PAGE_SIZE;
                $rs = $rs->search(undef, {offset => ($page - 1) * PAGE_SIZE, ($page == $pages ? () : (rows => PAGE_SIZE))});
        }
@@ -55,13 +55,11 @@ sub list {
 }
 
 sub entry {
 }
 
 sub entry {
-       my ($thing, $lang, $env, $id, $ct) = @_;
+       my ($thing, $lang, $env, $id) = @_;
        my %thing = %$thing;
        my %thing = %$thing;
-       ($id, $ct) = ($ct, $id) if $ct;
        debug $env => "Rsname is $thing{rsname} and id is $id";
        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{makers} //= sub { db(shift)->resultset($thing{rsname}) };
+       my %params = map {+ rs => $_, $_->get_columns } $thing{makers}->($env)->find($id);
        $thing{mangle}->(local $_ = \%params) if exists $thing{mangle};
        wantarray ? %params : \%params
 }
        $thing{mangle}->(local $_ = \%params) if exists $thing{mangle};
        wantarray ? %params : \%params
 }
@@ -114,7 +112,13 @@ thing {
 thing {
        params qw/pb Problem Problems/;
        prefetch 'owner';
 thing {
        params qw/pb Problem Problems/;
        prefetch 'owner';
-       makers { my ($db, $ct) = @_; $ct ? $db->contest($ct)->problems : $db->problems->search({private => 0}) };
+       makers {
+               my $env = $_[0];
+               my $db = db $env;
+               return $db->contest($env->{'gruntmaster.contest'})->problems->search(undef, {order_by => 'problem.id'}) if exists $env->{'gruntmaster.contest'};
+               return $db->problems->search({owner => $env->{'gruntmaster.user'}}) if exists $env->{'gruntmaster.user'};
+               $db->problems->search({private => 0});
+       };
        sortby { $a->{name} cmp $b->{name}};
        group { $_->{level} };
        mangle {
        sortby { $a->{name} cmp $b->{name}};
        group { $_->{level} };
        mangle {
@@ -142,7 +146,13 @@ thing {
 thing {
        params qw/log Job/, 'Job log';
        prefetch 'owner', 'problem';
 thing {
        params qw/log Job/, 'Job log';
        prefetch 'owner', 'problem';
-       makers { shift->jobs->search({contest => shift}) };
+       makers {
+               my $env = $_[0];
+               my $db = db $env;
+               return $db->jobs->search({'me.owner' => $env->{'gruntmaster.user'}}) if exists $env->{'gruntmaster.user'};
+               return $db->jobs->search({problem => $env->{'gruntmaster.problem'}}) if exists $env->{'gruntmaster.problem'};
+               $db->jobs->search({contest => $env->{'gruntmaster.contest'}})
+       };
        sortby { $b->{id} <=> $a->{id}};
        mangle {
                $_->{results} &&= decode_json $_->{results};
        sortby { $b->{id} <=> $a->{id}};
        mangle {
                $_->{results} &&= decode_json $_->{results};
index 99ce4987c9e24b387f95487464f8752b70d385a6..5b5f6606a25507f9f105faaff4586895ce48e335 100644 (file)
@@ -56,36 +56,30 @@ sub post {
 }
 
 BEGIN{
 }
 
 BEGIN{
-       my $word = qr,(\w+),a;
-       my $ct = qr,(?:\/ct/$word)?,a;
+       my $word   = qr,(\w+),a;
+       my $number = qr,(\d+),a;
 
        sub generic {
 
        sub generic {
-               my ($thing, $ct, $fs) = @_;
-               $ct //= '', $fs //= '';
-               my $pkg = ucfirst $thing;
-               get  qr,$ct/$thing/,             => $pkg;
-               get  qr,$ct/$thing/read,         => "${pkg}::Read";
-               get  qr,$ct/$thing/$word$fs,     => "${pkg}::Entry";
-#              post qr,$ct/$thing/$word/create, => "${pkg}::Entry::Create";
-               get  qr,$ct/$thing/$word/read,   => "${pkg}::Entry::Read";
-#              post qr,$ct/$thing/$word/update, => "${pkg}::Entry::Update";
-#              post qr,$ct/$thing/$word/delete, => "${pkg}::Entry::Delete";
+               for my $thing (@_) {
+                       my $pkg = ucfirst $thing;
+                       get  qr,/$thing/,             => $pkg;
+                       get  qr,/$thing/read,         => "${pkg}::Read";
+                       get  qr,/$thing/$word,        => "${pkg}::Entry";
+#                      post qr,/$thing/$word/create, => "${pkg}::Entry::Create";
+                       get  qr,/$thing/$word/read,   => "${pkg}::Entry::Read";
+#                      post qr,/$thing/$word/update, => "${pkg}::Entry::Update";
+#                      post qr,/$thing/$word/delete, => "${pkg}::Entry::Delete";
+               }
        }
 
        get qr,/css/$word\.css, => 'CSS';
        get qr,/js\.js, => 'JS';
 
        }
 
        get qr,/css/$word\.css, => 'CSS';
        get qr,/js\.js, => 'JS';
 
-       generic 'us';
-       generic ct => '', '/';
-       generic pb => $ct;
-       #generic log => $ct;
+       generic qw/us ct pb log/;
 
 
-       get qr,$ct/log/(\d+)?, => 'Log';
-       get qr,$ct/log/st, => 'St';
-       get qr,$ct/log/job/$word, => 'Log::Entry';
-       get qr,$ct/log/job/$word/read, => 'Log::Entry::Read';
-       get qr,$ct/log/src/$word\.$word, => 'Src';
-       post qr,$ct/pb/$word/submit, => 'Submit';
+       get qr,/log/st, => 'St';
+       get qr,/log/src/$number\.$word, => 'Src';
+       post qr,/pb/$word/submit, => 'Submit';
 
        post qr,/action/register, => 'Register';
        post qr,/action/passwd, => 'Passwd';
 
        post qr,/action/register, => 'Register';
        post qr,/action/passwd, => 'Passwd';
index 457f3d7e3a3e76536801e43711835d977ac7b11c..038fef0197c055f6fdcccd8e09f15c9c40aa4557 100644 (file)
@@ -4,7 +4,7 @@
 <thead>
 <tr><th>Name<th>Start date<th>Stop date<th>Owner
 <tbody>
 <thead>
 <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>
+<tmpl_loop running><tr><td><a href="/ct/<tmpl_var id>"><tmpl_var name></a>
 <td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
 <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>
 <td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
 <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>
@@ -18,7 +18,7 @@
 <thead>
 <tr><th>Name<th>Start date<th>Stop date<th>Owner
 <tbody>
 <thead>
 <tr><th>Name<th>Start date<th>Stop date<th>Owner
 <tbody>
-<tmpl_loop pstoping><tr><td><a href="<tmpl_var id>/"><tmpl_var name></a>
+<tmpl_loop pstoping><tr><td><a href="/ct/<tmpl_var id>"><tmpl_var name></a>
 <td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
 <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>
 <td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
 <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>
@@ -32,7 +32,7 @@
 <thead>
 <tr><th>Name<th>Start date<th>Stop date<th>Owner
 <tbody>
 <thead>
 <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>
+<tmpl_loop finished><tr><td><a href="/ct/<tmpl_var id>"><tmpl_var name></a>
 <td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
 <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>
 <td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{start}; %>
 <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>
index af533d618b21a071ebf7f84f2e20373135eb00ed..0ad32c9626ad76b3f142b948c1190b2f256c84f9 100644 (file)
@@ -3,6 +3,6 @@ Contest stop  time: <%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->
 
 <tmpl_var ESCAPE=0 description>
 
 
 <tmpl_var ESCAPE=0 description>
 
-<tmpl_if started><a href="pb/">Problems</a><br>
-<a href="log/">Job log</a><br>
-<a href="log/st">Standings</a></tmpl_if>
+<tmpl_if started><a href="/ct/<tmpl_var id>/pb/">Problems</a><br>
+<a href="/ct/<tmpl_var id>/log/">Job log</a><br>
+<a href="/ct/<tmpl_var id>/log/st">Standings</a></tmpl_if>
index 75bb3288975172aeb9a703dbadd6e824b363af86..1e5b4889ddbcd2673f555473dbd9487165bab81b 100644 (file)
@@ -2,10 +2,10 @@
 <thead>
 <tr><th>ID<th>Problem<th>Date<th>Size<th>User<th>Result
 <tbody>
 <thead>
 <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 problem_name></a>
+<tmpl_loop log><tr><td><a href="/log/<tmpl_var id>"><tmpl_var id></a>
+<td><a href="/pb/<tmpl_var problem>"><tmpl_var problem_name></a>
 <td><%perl __OUT__ POSIX::strftime '%c', localtime __CURRENT__->{date}; %>
 <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="/log/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>
 <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>
index 35cfbd418c9b9b9997c2846889496dca3caa5c06..fcb23b07547986f459a0ad2b11c71394dc69d2f2 100644 (file)
@@ -3,7 +3,7 @@
 <table border class="table table-bordered table-striped table-fixed">
 <thead><tr><th>Name<th>Author<th>Owner
 <tbody>
 <table border class="table table-bordered table-striped table-fixed">
 <thead><tr><th>Name<th>Author<th>Owner
 <tbody>
-<tmpl_loop beginner><tr><td><a href="<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><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 beginner><tr><td><a href="/pb/<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><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_loop>
 </table>
 </tmpl_if>
@@ -13,7 +13,7 @@
 <table border class="table table-bordered table-striped table-fixed">
 <thead><tr><th>Name<th>Author<th>Owner
 <tbody>
 <table border class="table table-bordered table-striped table-fixed">
 <thead><tr><th>Name<th>Author<th>Owner
 <tbody>
-<tmpl_loop easy><tr><td><a href="<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><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 easy><tr><td><a href="/pb/<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><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_loop>
 </table>
 </tmpl_if>
@@ -23,7 +23,7 @@
 <table border class="table table-bordered table-striped table-fixed">
 <thead><tr><th>Name<th>Author<th>Owner
 <tbody>
 <table border class="table table-bordered table-striped table-fixed">
 <thead><tr><th>Name<th>Author<th>Owner
 <tbody>
-<tmpl_loop medium><tr><td><a href="<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><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 medium><tr><td><a href="/pb/<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><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_loop>
 </table>
 </tmpl_if>
@@ -33,7 +33,7 @@
 <table border class="table table-bordered table-striped table-fixed">
 <thead><tr><th>Name<th>Author<th>Owner
 <tbody>
 <table border class="table table-bordered table-striped table-fixed">
 <thead><tr><th>Name<th>Author<th>Owner
 <tbody>
-<tmpl_loop hard><tr><td><a href="<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><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 hard><tr><td><a href="/pb/<tmpl_var id>"><tmpl_var name></a><td><tmpl_var author><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_loop>
 </table>
 </tmpl_if>
index 5656ad9ed5e992516d7273c3f948adc4337fe294..c70795262884ba0ec926405902dae1655177177b 100644 (file)
@@ -9,9 +9,11 @@
 <dt>Owner</dt> <dd><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a></dd>
 </dl>
 
 <dt>Owner</dt> <dd><a href="/us/<tmpl_var owner>"><tmpl_if owner_name><tmpl_var owner_name><tmpl_else><tmpl_var owner></tmpl_if></a></dd>
 </dl>
 
+<a href="/pb/<tmpl_var id>/log/">Job log</a>
+
 <tmpl_if cansubmit>
 <h1>Submit solution</h1>
 <tmpl_if cansubmit>
 <h1>Submit solution</h1>
-<form action="<tmpl_var id>/submit" method="POST" enctype="multipart/form-data" role="form">
+<form action="/pb/<tmpl_var id>/submit" method="POST" enctype="multipart/form-data" role="form">
 <input type="hidden" name="problem" value="<tmpl_var id>">
 <tmpl_if_defined contest><input type="hidden" name="contest" value="<tmpl_var contest>"></tmpl_if_defined>
 
 <input type="hidden" name="problem" value="<tmpl_var id>">
 <tmpl_if_defined contest><input type="hidden" name="contest" value="<tmpl_var contest>"></tmpl_if_defined>
 
@@ -30,4 +32,4 @@
 </form>
 </tmpl_if>
 </div>
 </form>
 </tmpl_if>
 </div>
-</div>
\ No newline at end of file
+</div>
index 1b18005ce3e41e8607c0bba7faa29045adfc1d94..6417f48bc1e894a3611707e717399d7737ed7944 100644 (file)
@@ -1,6 +1,6 @@
 <table border class="table table-border table-striped">
 <thead>
 <table border class="table table-border table-striped">
 <thead>
-<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_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>
 
 <tmpl_else><tr><th>Rank<th>User<th>Score
 </tmpl_if>
 
index 72c72c35876e08c33949a874f049d3765ebc2f4b..2b77d089752eee0472cbd58b9b1c2867152d7be3 100644 (file)
@@ -1,2 +1,2 @@
-<div class="list-group"><tmpl_loop us><a class="list-group-item" href="<tmpl_var id>"><tmpl_var name></a>
-</tmpl_loop></ol>
+<div class="list-group"><tmpl_loop us><a class="list-group-item" href="/us/<tmpl_var id>"><tmpl_var name></a>
+</tmpl_loop><div>
index 4588e2442ef1f8ffc1301ac4499393aeea25d215..69d95daf45d46eda1d1f7b1c7dc352f81682bc95 100644 (file)
@@ -3,3 +3,6 @@
 <dt>University</dt> <dd><tmpl_var university></dd>
 <dt>Level</dt> <dd><tmpl_var level></dd>
 </dl>
 <dt>University</dt> <dd><tmpl_var university></dd>
 <dt>Level</dt> <dd><tmpl_var level></dd>
 </dl>
+
+<a href="/us/<tmpl_var id>/log/">Job log</a><br>
+<a href="/us/<tmpl_var id>/pb/">Owned problems</a>
This page took 0.041874 seconds and 4 git commands to generate.