Skip to content

Commit 51db540

Browse files
Show list of submissions on public and team scoreboards when clicking on a cell
Fixes #2427
1 parent ec8b33b commit 51db540

File tree

9 files changed

+181
-2
lines changed

9 files changed

+181
-2
lines changed

webapp/public/style_domjudge.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,14 @@ tr.ignore td, td.ignore, span.ignore {
647647
min-width: 2em;
648648
}
649649

650+
h5 .problem-badge {
651+
font-size: 1rem;
652+
}
653+
654+
h1 .problem-badge {
655+
font-size: 2rem;
656+
}
657+
650658
.tooltip .tooltip-inner {
651659
max-width: 500px;
652660
}

webapp/src/Controller/PublicController.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Controller;
44

5+
use App\DataTransferObject\SubmissionRestriction;
56
use App\Entity\Contest;
67
use App\Entity\ContestProblem;
78
use App\Entity\Team;
@@ -11,6 +12,7 @@
1112
use App\Service\EventLogService;
1213
use App\Service\ScoreboardService;
1314
use App\Service\StatisticsService;
15+
use App\Service\SubmissionService;
1416
use Doctrine\ORM\EntityManagerInterface;
1517
use Doctrine\ORM\NonUniqueResultException;
1618
use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -33,6 +35,7 @@ public function __construct(
3335
protected readonly ConfigurationService $config,
3436
protected readonly ScoreboardService $scoreboardService,
3537
protected readonly StatisticsService $stats,
38+
protected readonly SubmissionService $submissionService,
3639
EntityManagerInterface $em,
3740
EventLogService $eventLog,
3841
KernelInterface $kernel,
@@ -79,6 +82,18 @@ public function scoreboardAction(
7982

8083
if ($static) {
8184
$data['hide_menu'] = true;
85+
$submissions = $this->submissionService->getSubmissionList(
86+
[$contest->getCid() => $contest],
87+
new SubmissionRestriction(valid: true),
88+
paginated: false
89+
)[0];
90+
91+
$submissionsPerTeamAndProblem = [];
92+
foreach ($submissions as $submission) {
93+
$submissionsPerTeamAndProblem[$submission->getTeam()->getTeamid()][$submission->getProblem()->getProbid()][] = $submission;
94+
}
95+
$data['submissionsPerTeamAndProblem'] = $submissionsPerTeamAndProblem;
96+
$data['verificationRequired'] = $this->config->get('verification_required');
8297
}
8398

8499
$data['current_contest'] = $contest;
@@ -267,4 +282,54 @@ protected function getBinaryFile(int $probId, callable $response): StreamedRespo
267282

268283
return $response($probId, $contest, $contestProblem);
269284
}
285+
286+
#[Route(path: '/submissions/team/{teamId<\d+>}/problem/{problemId<\d+>}', name: 'public_submissions')]
287+
public function submissionsAction(Request $request, int $teamId, int $problemId): Response
288+
{
289+
$contest = $this->dj->getCurrentContest(onlyPublic: true);
290+
291+
if (!$contest) {
292+
throw $this->createNotFoundException('No active contest found');
293+
}
294+
295+
/** @var Team|null $team */
296+
$team = $this->em->getRepository(Team::class)->find($teamId);
297+
if ($team && $team->getCategory() && !$team->getCategory()->getVisible()) {
298+
$team = null;
299+
}
300+
301+
if (!$team) {
302+
throw $this->createNotFoundException('Team not found');
303+
}
304+
305+
/** @var ContestProblem|null $problem */
306+
$problem = $this->em->getRepository(ContestProblem::class)->find([
307+
'problem' => $problemId,
308+
'contest' => $contest,
309+
]);
310+
311+
if (!$problem) {
312+
throw $this->createNotFoundException('Problem not found');
313+
}
314+
315+
$submissions = $this->submissionService->getSubmissionList(
316+
[$contest->getCid() => $contest],
317+
new SubmissionRestriction(teamId: $teamId, problemId: $problemId, valid: true),
318+
paginated: false
319+
)[0];
320+
321+
$data = [
322+
'contest' => $contest,
323+
'problem' => $problem,
324+
'team' => $team,
325+
'submissions' => $submissions,
326+
'verificationRequired' => $this->config->get('verification_required'),
327+
];
328+
329+
if ($request->isXmlHttpRequest()) {
330+
return $this->render('public/team_submissions_modal.html.twig', $data);
331+
}
332+
333+
return $this->render('public/team_submissions.html.twig', $data);
334+
}
270335
}

webapp/src/Controller/Team/SubmissionController.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,4 +250,10 @@ public function downloadAction(int $submitId): Response
250250

251251
return $this->submissionService->getSubmissionZipResponse($submission);
252252
}
253+
254+
#[Route(path: '/submissions/team/{teamId<\d+>}/problem/{problemId<\d+>}', name: 'team_submissions')]
255+
public function listAction(int $probId): Response
256+
{
257+
258+
}
253259
}

webapp/src/DataTransferObject/SubmissionRestriction.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,6 @@ public function __construct(
6969
public ?bool $externallyJudged = null,
7070
public ?bool $externallyVerified = null,
7171
public ?bool $withExternalId = null,
72+
public ?bool $valid = null,
7273
) {}
7374
}

webapp/src/Service/SubmissionService.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,12 @@ public function getSubmissionList(
306306
->setParameter('results', $restrictions->results);
307307
}
308308

309+
if (isset($restrictions->valid)) {
310+
$queryBuilder
311+
->andWhere('s.valid = :valid')
312+
->setParameter('valid', $restrictions->valid);
313+
}
314+
309315
if ($this->dj->shadowMode()) {
310316
// When we are shadow, also load the external results
311317
$queryBuilder

webapp/templates/partials/scoreboard_table.html.twig

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,15 +302,21 @@
302302
{% endif %}
303303
{% endif %}
304304
305-
{% set link = null %}
305+
{% set extra = null %}
306306
{% if jury %}
307307
{% set restrict = {problemId: problem.probid} %}
308308
{% set link = path('jury_team', {teamId: score.team.teamid, restrict: restrict}) %}
309+
{% elseif static %}
310+
{% set link = '#' %}
311+
{% set extra = 'data-bs-toggle="modal" data-bs-target="#team-submissions-modal-' ~ score.team.teamid ~ '-' ~ problem.probid ~ '"' %}
312+
{% else %}
313+
{% set link = path('public_submissions', {teamId: score.team.teamid, problemId: problem.probid}) %}
314+
{% set extra = 'data-ajax-modal' %}
309315
{% endif %}
310316
311317
<td class="score_cell">
312318
{% if numSubmissions != '0' %}
313-
<a {% if link %}href="{{ link }}"{% endif %}>
319+
<a {% if link %}href="{{ link }}"{% endif %} {% if extra %}{{ extra | raw }}{% endif %}>
314320
<div class="{{ scoreCssClass }}">
315321
{% if matrixItem.isCorrect %}{{ time }}{% else %}&nbsp;{% endif %}
316322
<span>
@@ -538,6 +544,16 @@
538544
{% include 'partials/team.html.twig' with {size: 6, team: score.team} %}
539545
{% endblock %}
540546
{% endembed %}
547+
{% for problem in problems %}
548+
{% embed 'partials/modal.html.twig' with {'modalId': 'team-submissions-modal-' ~ score.team.teamid ~ '-' ~ problem.probid} %}
549+
{% block title %}
550+
Submissions for team {{ score.team.effectiveName }} and problem {{ problem | problemBadge }} {{ problem.problem.name }}
551+
{% endblock %}
552+
{% block content %}
553+
{% include 'public/partials/submission_list.html.twig' with {size: 6, team: score.team, submissions: submissionsPerTeamAndProblem[score.team.teamid][problem.probid] ?? []} %}
554+
{% endblock %}
555+
{% endembed %}
556+
{% endfor %}
541557
{% endfor %}
542558
{% endif %}
543559
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{# Render a list of submissions for the scoreboard #}
2+
{# @var submission \App\Entity\Submission #}
3+
4+
{% if submissions is empty %}
5+
<div class="alert alert-warning">No submissions</div>
6+
{% else %}
7+
<table class="data-table table table-hover table-striped table-sm submissions-table">
8+
<thead class="thead-light">
9+
<tr>
10+
<th scope="col">time</th>
11+
<th scope="col">language</th>
12+
<th scope="col">result</th>
13+
{% if contest.getRuntimeAsScoreTiebreaker() %}
14+
<th scope="col">runtime</th>
15+
{% endif %}
16+
</tr>
17+
</thead>
18+
<tbody>
19+
{% for submission in submissions %}
20+
<tr>
21+
<td>
22+
{{ submission.submittime | printtime(null, submission.contest) }}
23+
</td>
24+
<td class="langid">
25+
{{ submission.language.langid }}
26+
</td>
27+
<td>
28+
{% if submission.submittime >= submission.contest.endtime %}
29+
{{ 'too-late' | printResult }}
30+
{% elseif submission.contest.freezetime and submission.submittime >= submission.contest.freezetime and not contest.freezeData.showFinal %}
31+
{{ '' | printResult }}
32+
{% else %}
33+
{% if submission.judgings.first is empty or submission.judgings.first.result is empty %}
34+
{{ '' | printResult }}
35+
{% elseif verificationRequired and not submission.judgings.first.verified %}
36+
{{ '' | printResult }}
37+
{% else %}
38+
{{ submission.judgings.first.result | printResult }}
39+
{% endif %}
40+
{% endif %}
41+
</td>
42+
{% if contest.getRuntimeAsScoreTiebreaker() %}
43+
<td>
44+
{% if link and submission.getResult() == 'correct' %}
45+
{{ "%0.3f s" | format(submission.judgings.first.getMaxRuntime()) }}
46+
{% else %}
47+
-
48+
{% endif %}
49+
</td>
50+
{% endif %}
51+
</tr>
52+
{% endfor %}
53+
</tbody>
54+
</table>
55+
{% endif %}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{% extends "public/base.html.twig" %}
2+
3+
{% block title %}
4+
{% if team is not empty %}Submissions for team {{ team.effectiveName }} and problem {{ problem.problem.name }} - {% endif %}{{ parent() }}
5+
{% endblock %}
6+
7+
{% block content %}
8+
<h1 class="mt-3">
9+
Submissions for team {{ team.effectiveName }} and problem {{ problem | problemBadge }} {{ problem.problem.name }}
10+
</h1>
11+
12+
{% include 'public/partials/submission_list.html.twig' %}
13+
{% endblock %}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{% extends "partials/modal.html.twig" %}
2+
3+
{% block title %}
4+
Submissions for team {{ team.effectiveName }} and problem {{ problem | problemBadge }} {{ problem.problem.name }}
5+
{% endblock %}
6+
7+
{% block content %}
8+
{% include 'public/partials/submission_list.html.twig' %}
9+
{% endblock %}

0 commit comments

Comments
 (0)