From 641116674d9af28b2eefdeab86d48e56339668fc Mon Sep 17 00:00:00 2001 From: Michael Vasseur <14887731+vmcj@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:13:18 +0100 Subject: [PATCH 1/3] Protect function against nonexistent contest --- webapp/src/Service/ScoreboardService.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/webapp/src/Service/ScoreboardService.php b/webapp/src/Service/ScoreboardService.php index db9e99ecd7..1bb0d698ca 100644 --- a/webapp/src/Service/ScoreboardService.php +++ b/webapp/src/Service/ScoreboardService.php @@ -1078,8 +1078,11 @@ protected function getCategories(bool $jury): array * Get the scorecache used to calculate the scoreboard. * @return ScoreCache[] */ - protected function getScorecache(Contest $contest, ?Team $team = null): array + protected function getScorecache(?Contest $contest, ?Team $team = null): array { + if (!$contest) { + return []; + } $queryBuilder = $this->em->createQueryBuilder() ->from(ScoreCache::class, 's') ->select('s') From 0196713aed3f7fe7261afaabd929568818a67708 Mon Sep 17 00:00:00 2001 From: Michael Vasseur <14887731+vmcj@users.noreply.github.com> Date: Mon, 10 Nov 2025 18:50:25 +0100 Subject: [PATCH 2/3] Hide already solved problems for teams This moves the already solved problems under a new header, the teams can still visit those but it hides those by default to make it easier for teams to focus on the next problem. In case we like this we should check if the function should actually be public. --- .../src/Controller/Team/ProblemController.php | 5 +- webapp/src/Service/DOMJudgeService.php | 21 +- webapp/src/Service/ScoreboardService.php | 2 +- .../templates/partials/problem_list.html.twig | 353 +++++++++--------- 4 files changed, 205 insertions(+), 176 deletions(-) diff --git a/webapp/src/Controller/Team/ProblemController.php b/webapp/src/Controller/Team/ProblemController.php index 9374ee46b4..bb36bcfecd 100644 --- a/webapp/src/Controller/Team/ProblemController.php +++ b/webapp/src/Controller/Team/ProblemController.php @@ -8,6 +8,7 @@ use App\Service\ConfigurationService; use App\Service\DOMJudgeService; use App\Service\EventLogService; +use App\Service\ScoreboardService; use App\Service\StatisticsService; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\NonUniqueResultException; @@ -32,6 +33,7 @@ public function __construct( DOMJudgeService $dj, protected readonly ConfigurationService $config, protected readonly StatisticsService $stats, + protected readonly ScoreboardService $scoreboard, protected readonly EventLogService $eventLogService, EntityManagerInterface $em, KernelInterface $kernel, @@ -46,8 +48,9 @@ public function __construct( public function problemsAction(): Response { $teamId = $this->dj->getUser()->getTeam()->getTeamid(); + $cache = $this->scoreboard->getScorecache($this->dj->getCurrentContest(), $this->dj->getUser()->getTeam()); return $this->render('team/problems.html.twig', - $this->dj->getTwigDataForProblemsAction($this->stats, $teamId)); + $this->dj->getTwigDataForProblemsAction($this->stats, $teamId, cache: $cache)); } diff --git a/webapp/src/Service/DOMJudgeService.php b/webapp/src/Service/DOMJudgeService.php index 5101285bf8..d5d570e174 100644 --- a/webapp/src/Service/DOMJudgeService.php +++ b/webapp/src/Service/DOMJudgeService.php @@ -24,6 +24,7 @@ use App\Entity\ProblemAttachment; use App\Entity\QueueTask; use App\Entity\Rejudging; +use App\Entity\ScoreCache; use App\Entity\Submission; use App\Entity\Team; use App\Entity\TeamAffiliation; @@ -1009,7 +1010,8 @@ public function getContestStats(Contest $contest): ContestStatus } /** - * @return array{'problems': ContestProblem[], 'samples': string[], 'showLimits': bool, + * @param ScoreCache[]|null $cache + * @return array{'allproblems': array, 'samples': string[], 'showLimits': bool, * 'defaultMemoryLimit': int, 'timeFactorDiffers': bool, * 'stats': array{'numBuckets': int, 'maxBucketSizeCorrect': int, * 'maxBucketSizeCorrect': int, 'maxBucketSizeIncorrect': int, @@ -1020,7 +1022,8 @@ public function getContestStats(Contest $contest): ContestStatus public function getTwigDataForProblemsAction( StatisticsService $statistics, ?int $teamId = null, - bool $forJury = false + bool $forJury = false, + ?array $cache = null ): array { $contest = isset($teamId) ? $this->getCurrentContest($teamId) : $this->getCurrentContest(onlyPublic: !$forJury); $showLimits = (bool)$this->config->get('show_limits_on_team_page'); @@ -1093,8 +1096,20 @@ public function getTwigDataForProblemsAction( } } + $allProblems = [null => [], 'solved' => []]; + if ($cache) { + foreach ($cache as $ind => $cachedProblem) { + if ($cachedProblem->getIsCorrect(true)) { + $allProblems['solved'][] = $problems[$ind]; + } else { + $allProblems[null][] = $problems[$ind]; + } + } + } else { + $allProblems = [null => $problems]; + } $data = [ - 'problems' => $problems, + 'allproblems' => $allProblems, 'samples' => $samples, 'showLimits' => $showLimits, 'defaultMemoryLimit' => $defaultMemoryLimit, diff --git a/webapp/src/Service/ScoreboardService.php b/webapp/src/Service/ScoreboardService.php index 1bb0d698ca..3810ddb7a7 100644 --- a/webapp/src/Service/ScoreboardService.php +++ b/webapp/src/Service/ScoreboardService.php @@ -1078,7 +1078,7 @@ protected function getCategories(bool $jury): array * Get the scorecache used to calculate the scoreboard. * @return ScoreCache[] */ - protected function getScorecache(?Contest $contest, ?Team $team = null): array + public function getScorecache(?Contest $contest, ?Team $team = null): array { if (!$contest) { return []; diff --git a/webapp/templates/partials/problem_list.html.twig b/webapp/templates/partials/problem_list.html.twig index b85f2437df..8a211cf1c1 100644 --- a/webapp/templates/partials/problem_list.html.twig +++ b/webapp/templates/partials/problem_list.html.twig @@ -18,193 +18,204 @@ {% endif %} -{% if problems is empty %} -
No problem texts available at this point.
-{% else %} -
- {% if show_jury_warning is defined and show_jury_warning %} -
- This is a preview of how the page will look like for teams and the public after the contest has started. -
+{% for identifier, problems in allproblems %} + {% if problems is empty %} + {% if not identifier %} +
No problem texts available at this point.
{% endif %} + {% else %} +
+ {% if show_jury_warning is defined and show_jury_warning %} +
+ This is a preview of how the page will look like for teams and the public after the contest has started. +
+ {% endif %} -
- {% for problem in problems %} -
- {% set numsamples = samples[problem.probid] %} - {% if problem.problem.interactiveProblem %} - {% set numsamples = 0 %} - {% endif %} -
-
-

- {{ problem | problemBadge }} -

-

- {{ problem.problem.name }} -

- {% if showLimits %} -

- Limits: {{ problem.problem.timelimit }} - second - {%- if problem.problem.timelimit > 1 %}s{% endif %} - {%- if timeFactorDiffers -%} - * - {% endif %} - / - {{ ((problem.problem.memlimit | default(defaultMemoryLimit)) * 1024) | printSize }} -

- {% endif %} - {% if problem.problem.languages | length != 0 %} + {% if identifier %} +

+ Already solved problems + +

+ {% endif%} + +
+ {% for problem in problems %} +
+ {% set numsamples = samples[problem.probid] %} + {% if problem.problem.interactiveProblem %} + {% set numsamples = 0 %} + {% endif %} +
+
+

+ {{ problem | problemBadge }} +

+

+ {{ problem.problem.name }} +

+ {% if showLimits %} +

+ Limits: {{ problem.problem.timelimit }} + second + {%- if problem.problem.timelimit > 1 %}s{% endif %} + {%- if timeFactorDiffers -%} + * + {% endif %} + / + {{ ((problem.problem.memlimit | default(defaultMemoryLimit)) * 1024) | printSize }} +

+ {% endif %} + {% if problem.problem.languages | length != 0 %} +

+ Language{% if problem.problem.languages | length > 1 %}s{% endif %}: + {% for lang in problem.problem.languages %} + {{ lang.name }}{% if not loop.last %}, {% endif %} + {% endfor %} +

+ {% endif %}

- Language{% if problem.problem.languages | length > 1 %}s{% endif %}: - {% for lang in problem.problem.languages %} - {{ lang.name }}{% if not loop.last %}, {% endif %} - {% endfor %} + Type: {{ problem.problem.typesAsString }}

- {% endif %} -

- Type: {{ problem.problem.typesAsString }} -

- {% if stats is defined %} -
- {% for correct in [true, false] %} -
- {% for bucket in 0..stats.numBuckets - 1 %} - {% if correct %} - {% set index = 'correct' %} - {% set maxBucketSize = stats.maxBucketSizeCorrect %} - {% else %} - {% set index = 'incorrect' %} - {% set maxBucketSize = stats.maxBucketSizeIncorrect %} - {% endif %} - {% set stat = stats.problems[problem.problem.probid][index][bucket] %} - {% set count = stat.count %} - {% if maxBucketSize == 0 %} - {% set bucket = 0 %} - {% else %} - {% set bucket = (count / maxBucketSize * 9) | round(0, 'ceil') %} - {% endif %} - {% if count == 1 %} - {% set submissionText = 'submission' %} - {% else %} - {% set submissionText = 'submissions' %} - {% endif %} - {% if not contest.freezeData.showFinal and contest.freezetime and stat.end.timestamp >= contest.freezetime %} - {% set maxBucketSize = max(1, stats.maxBucketSizeCorrect, stats.maxBucketSizeIncorrect) %} - {% set bucket = (count / maxBucketSize * 9) | round(0, 'ceil') %} - {% set itemClass = 'frozen' ~ '-' ~ bucket %} - {% set label = count ~ ' ' ~ submissionText ~ ' in freeze' %} - {% else %} - {% set itemClass = index ~ '-' ~ bucket %} - {% set label = count ~ ' ' ~ index ~ ' ' ~ submissionText %} - {% endif %} -
-
- {% endfor %} -
- {% endfor %} -
-
-
-
- {% endif %} - -
- {% if show_submit_button | default(false) %} - {% if is_granted('ROLE_JURY') or (current_team_contest is not null and current_team_contest.freezeData.started) %} - - - Submit - - - {% else %} - - - Submit - - - {% endif %} + {% if stats is defined %} +
+ {% for correct in [true, false] %} +
+ {% for bucket in 0..stats.numBuckets - 1 %} + {% if correct %} + {% set index = 'correct' %} + {% set maxBucketSize = stats.maxBucketSizeCorrect %} + {% else %} + {% set index = 'incorrect' %} + {% set maxBucketSize = stats.maxBucketSizeIncorrect %} + {% endif %} + {% set stat = stats.problems[problem.problem.probid][index][bucket] %} + {% set count = stat.count %} + {% if maxBucketSize == 0 %} + {% set bucket = 0 %} + {% else %} + {% set bucket = (count / maxBucketSize * 9) | round(0, 'ceil') %} + {% endif %} + {% if count == 1 %} + {% set submissionText = 'submission' %} + {% else %} + {% set submissionText = 'submissions' %} + {% endif %} + {% if not contest.freezeData.showFinal and contest.freezetime and stat.end.timestamp >= contest.freezetime %} + {% set maxBucketSize = max(1, stats.maxBucketSizeCorrect, stats.maxBucketSizeIncorrect) %} + {% set bucket = (count / maxBucketSize * 9) | round(0, 'ceil') %} + {% set itemClass = 'frozen' ~ '-' ~ bucket %} + {% set label = count ~ ' ' ~ submissionText ~ ' in freeze' %} + {% else %} + {% set itemClass = index ~ '-' ~ bucket %} + {% set label = count ~ ' ' ~ index ~ ' ' ~ submissionText %} + {% endif %} +
+
+ {% endfor %} +
+ {% endfor %} +
+
+
+
{% endif %} - {% set clarificationsCount = 0 %} - {% set unseenClarificationCount = 0 %} - {% if clarifications[problem.probid] is defined %} - {% set clarificationsCount = clarifications[problem.probid] | length %} - {% for clar in clarifications[problem.probid] %} - {% if team and team.unreadClarifications.contains(clar) %} - {% set unseenClarificationCount = unseenClarificationCount + 1 %} +
+ {% if show_submit_button | default(false) %} + {% if is_granted('ROLE_JURY') or (current_team_contest is not null and current_team_contest.freezeData.started) %} + + + Submit + + + {% else %} + + + Submit + + {% endif %} - {% endfor %} - {% endif %} - {% if clarificationsCount > 0 %} - - - {% if clarificationsCount > 0 %} - {% set badgeClass = 'text-bg-info' %} - {% if unseenClarificationCount > 0 %} - {% set badgeClass = 'text-bg-danger' %} + {% endif %} + + {% set clarificationsCount = 0 %} + {% set unseenClarificationCount = 0 %} + {% if clarifications[problem.probid] is defined %} + {% set clarificationsCount = clarifications[problem.probid] | length %} + {% for clar in clarifications[problem.probid] %} + {% if team and team.unreadClarifications.contains(clar) %} + {% set unseenClarificationCount = unseenClarificationCount + 1 %} {% endif %} - {{ clarificationsCount }} - {% endif %} - clarifications - - {% endif %} + {% endfor %} + {% endif %} + {% if clarificationsCount > 0 %} + + + {% if clarificationsCount > 0 %} + {% set badgeClass = 'text-bg-info' %} + {% if unseenClarificationCount > 0 %} + {% set badgeClass = 'text-bg-danger' %} + {% endif %} + {{ clarificationsCount }} + {% endif %} + clarifications + + {% endif %} -
+
+
+ + {% if problem.problem.problemstatementType is not empty %} + + + statement + + {% endif %} + + {% if numsamples > 0 %} + + samples + + {% endif %}
- {% if problem.problem.problemstatementType is not empty %} - - - statement - + {% if problem.problem.attachments | length > 0 %} +
+
    + {% for attachment in problem.problem.attachments %} +
  1. + + {{ attachment.name }} + +
  2. + {% endfor %} +
{% endif %} - {% if numsamples > 0 %} - - samples - - {% endif %}
- - {% if problem.problem.attachments | length > 0 %} -
-
    - {% for attachment in problem.problem.attachments %} -
  1. - - {{ attachment.name }} - -
  2. - {% endfor %} -
- {% endif %} -
-
-
- {% endfor %} -
+
+ {% endfor %} +
- {% if showLimits and timeFactorDiffers %} -
-
- - {% endif %} -
-{% endif %} + {% endif %} +
+ {% endif %} +{% endfor %} From 32d656013bb08c8afa7da7d58c6b573eca874f60 Mon Sep 17 00:00:00 2001 From: Michael Vasseur <14887731+vmcj@users.noreply.github.com> Date: Mon, 10 Nov 2025 21:05:46 +0100 Subject: [PATCH 3/3] Alternative solution by collapsing cards When you press the button it displays the card of the solved problem. --- webapp/public/index.php | 0 webapp/src/Service/DOMJudgeService.php | 7 +- .../templates/partials/problem_list.html.twig | 239 ++++-------------- .../partials/problem_list_cards.html.twig | 168 ++++++++++++ 4 files changed, 218 insertions(+), 196 deletions(-) mode change 100644 => 100755 webapp/public/index.php create mode 100644 webapp/templates/partials/problem_list_cards.html.twig diff --git a/webapp/public/index.php b/webapp/public/index.php old mode 100644 new mode 100755 diff --git a/webapp/src/Service/DOMJudgeService.php b/webapp/src/Service/DOMJudgeService.php index d5d570e174..ddc797880c 100644 --- a/webapp/src/Service/DOMJudgeService.php +++ b/webapp/src/Service/DOMJudgeService.php @@ -1012,7 +1012,7 @@ public function getContestStats(Contest $contest): ContestStatus /** * @param ScoreCache[]|null $cache * @return array{'allproblems': array, 'samples': string[], 'showLimits': bool, - * 'defaultMemoryLimit': int, 'timeFactorDiffers': bool, + * 'defaultMemoryLimit': int, 'timeFactorDiffers': bool, 'solved': bool[], 'fullallproblems': ContestProblem[], * 'stats': array{'numBuckets': int, 'maxBucketSizeCorrect': int, * 'maxBucketSizeCorrect': int, 'maxBucketSizeIncorrect': int, * 'problems': array, @@ -1097,12 +1097,15 @@ public function getTwigDataForProblemsAction( } $allProblems = [null => [], 'solved' => []]; + $solvedProblem = []; if ($cache) { foreach ($cache as $ind => $cachedProblem) { if ($cachedProblem->getIsCorrect(true)) { $allProblems['solved'][] = $problems[$ind]; + $solvedProblem[] = true; } else { $allProblems[null][] = $problems[$ind]; + $solvedProblem[] = false; } } } else { @@ -1110,6 +1113,8 @@ public function getTwigDataForProblemsAction( } $data = [ 'allproblems' => $allProblems, + 'fullallproblems' => [null => $problems], + 'solved' => $solvedProblem, 'samples' => $samples, 'showLimits' => $showLimits, 'defaultMemoryLimit' => $defaultMemoryLimit, diff --git a/webapp/templates/partials/problem_list.html.twig b/webapp/templates/partials/problem_list.html.twig index 8a211cf1c1..3effa04a04 100644 --- a/webapp/templates/partials/problem_list.html.twig +++ b/webapp/templates/partials/problem_list.html.twig @@ -16,206 +16,55 @@ problemset {% endif %} - - -{% for identifier, problems in allproblems %} - {% if problems is empty %} - {% if not identifier %} -
No problem texts available at this point.
- {% endif %} - {% else %} -
- {% if show_jury_warning is defined and show_jury_warning %} -
- This is a preview of how the page will look like for teams and the public after the contest has started. -
- {% endif %} - - {% if identifier %} -

- Already solved problems - -

- {% endif%} - -
- {% for problem in problems %} -
- {% set numsamples = samples[problem.probid] %} - {% if problem.problem.interactiveProblem %} - {% set numsamples = 0 %} - {% endif %} -
-
-

- {{ problem | problemBadge }} -

-

- {{ problem.problem.name }} -

- {% if showLimits %} -

- Limits: {{ problem.problem.timelimit }} - second - {%- if problem.problem.timelimit > 1 %}s{% endif %} - {%- if timeFactorDiffers -%} - * - {% endif %} - / - {{ ((problem.problem.memlimit | default(defaultMemoryLimit)) * 1024) | printSize }} -

- {% endif %} - {% if problem.problem.languages | length != 0 %} -

- Language{% if problem.problem.languages | length > 1 %}s{% endif %}: - {% for lang in problem.problem.languages %} - {{ lang.name }}{% if not loop.last %}, {% endif %} - {% endfor %} -

- {% endif %} -

- Type: {{ problem.problem.typesAsString }} -

- {% if stats is defined %} -
- {% for correct in [true, false] %} -
- {% for bucket in 0..stats.numBuckets - 1 %} - {% if correct %} - {% set index = 'correct' %} - {% set maxBucketSize = stats.maxBucketSizeCorrect %} - {% else %} - {% set index = 'incorrect' %} - {% set maxBucketSize = stats.maxBucketSizeIncorrect %} - {% endif %} - {% set stat = stats.problems[problem.problem.probid][index][bucket] %} - {% set count = stat.count %} - {% if maxBucketSize == 0 %} - {% set bucket = 0 %} - {% else %} - {% set bucket = (count / maxBucketSize * 9) | round(0, 'ceil') %} - {% endif %} - {% if count == 1 %} - {% set submissionText = 'submission' %} - {% else %} - {% set submissionText = 'submissions' %} - {% endif %} - {% if not contest.freezeData.showFinal and contest.freezetime and stat.end.timestamp >= contest.freezetime %} - {% set maxBucketSize = max(1, stats.maxBucketSizeCorrect, stats.maxBucketSizeIncorrect) %} - {% set bucket = (count / maxBucketSize * 9) | round(0, 'ceil') %} - {% set itemClass = 'frozen' ~ '-' ~ bucket %} - {% set label = count ~ ' ' ~ submissionText ~ ' in freeze' %} - {% else %} - {% set itemClass = index ~ '-' ~ bucket %} - {% set label = count ~ ' ' ~ index ~ ' ' ~ submissionText %} - {% endif %} -
-
- {% endfor %} -
- {% endfor %} -
-
-
-
- {% endif %} - -
- {% if show_submit_button | default(false) %} - {% if is_granted('ROLE_JURY') or (current_team_contest is not null and current_team_contest.freezeData.started) %} - - - Submit - - - {% else %} - - - Submit - - - {% endif %} - {% endif %} - - {% set clarificationsCount = 0 %} - {% set unseenClarificationCount = 0 %} - {% if clarifications[problem.probid] is defined %} - {% set clarificationsCount = clarifications[problem.probid] | length %} - {% for clar in clarifications[problem.probid] %} - {% if team and team.unreadClarifications.contains(clar) %} - {% set unseenClarificationCount = unseenClarificationCount + 1 %} - {% endif %} - {% endfor %} - {% endif %} - {% if clarificationsCount > 0 %} - - - {% if clarificationsCount > 0 %} - {% set badgeClass = 'text-bg-info' %} - {% if unseenClarificationCount > 0 %} - {% set badgeClass = 'text-bg-danger' %} - {% endif %} - {{ clarificationsCount }} - {% endif %} - clarifications - - {% endif %} - -
-
+ {% if allproblems['solved'] is defined and allproblems['solved'] is not empty %} + + + show solved problems + + {% endif %} + - {% if problem.problem.problemstatementType is not empty %} - - - statement - - {% endif %} +{% set found_problem_texts = false %} +{% for problems in allproblems %} + {% if problems is not empty %} + {% set found_problem_texts = true %} + {% endif %} +{% endfor %} - {% if numsamples > 0 %} - - samples - - {% endif %} -
+{% if not found_problem_texts %} +
No problem texts available at this point.
+{% else %} +
+ {% if show_jury_warning is defined and show_jury_warning %} +
+ This is a preview of how the page will look like for teams and the public after the contest has started. +
+ {% endif %} - {% if problem.problem.attachments | length > 0 %} -
-
    - {% for attachment in problem.problem.attachments %} -
  1. - - {{ attachment.name }} - -
  2. - {% endfor %} -
- {% endif %} +

Alternative 1: Enable/Disable with button

+ {% include 'partials/problem_list_cards.html.twig' with {'problems': fullallproblems[null], 'collapse': 2} %} +
+

Alternative 2: Those would be there by default

+ {% include 'partials/problem_list_cards.html.twig' with {'problems': allproblems[null], 'collapse': 0} %} -
-
-
- {% endfor %} -
+ {% if allproblems['solved'] is defined %} +

+ Alternative 2: Already solved problems + +

+ {% include 'partials/problem_list_cards.html.twig' with {'problems': allproblems['solved'], 'collapse': 1} %} + {% endif %} - {% if showLimits and timeFactorDiffers %} -
-
- + {% if showLimits and timeFactorDiffers %} +
+
+
- {% endif %} -
- {% endif %} -{% endfor %} +
+ {% endif %} +
+{% endif %} diff --git a/webapp/templates/partials/problem_list_cards.html.twig b/webapp/templates/partials/problem_list_cards.html.twig new file mode 100644 index 0000000000..7baa3a55e0 --- /dev/null +++ b/webapp/templates/partials/problem_list_cards.html.twig @@ -0,0 +1,168 @@ +
+ {% for ind, problem in problems %} +
+ {% set numsamples = samples[problem.probid] %} + {% if problem.problem.interactiveProblem %} + {% set numsamples = 0 %} + {% endif %} +
+
+

+ {{ problem | problemBadge }} +

+

+ {{ problem.problem.name }} +

+ {% if showLimits %} +

+ Limits: {{ problem.problem.timelimit }} + second + {%- if problem.problem.timelimit > 1 %}s{% endif %} + {%- if timeFactorDiffers -%} + * + {% endif %} + / + {{ ((problem.problem.memlimit | default(defaultMemoryLimit)) * 1024) | printSize }} +

+ {% endif %} + {% if problem.problem.languages | length != 0 %} +

+ Language{% if problem.problem.languages | length > 1 %}s{% endif %}: + {% for lang in problem.problem.languages %} + {{ lang.name }}{% if not loop.last %}, {% endif %} + {% endfor %} +

+ {% endif %} +

+ Type: {{ problem.problem.typesAsString }} +

+ + {% if stats is defined %} +
+ {% for correct in [true, false] %} +
+ {% for bucket in 0..stats.numBuckets - 1 %} + {% if correct %} + {% set index = 'correct' %} + {% set maxBucketSize = stats.maxBucketSizeCorrect %} + {% else %} + {% set index = 'incorrect' %} + {% set maxBucketSize = stats.maxBucketSizeIncorrect %} + {% endif %} + {% set stat = stats.problems[problem.problem.probid][index][bucket] %} + {% set count = stat.count %} + {% if maxBucketSize == 0 %} + {% set bucket = 0 %} + {% else %} + {% set bucket = (count / maxBucketSize * 9) | round(0, 'ceil') %} + {% endif %} + {% if count == 1 %} + {% set submissionText = 'submission' %} + {% else %} + {% set submissionText = 'submissions' %} + {% endif %} + {% if not contest.freezeData.showFinal and contest.freezetime and stat.end.timestamp >= contest.freezetime %} + {% set maxBucketSize = max(1, stats.maxBucketSizeCorrect, stats.maxBucketSizeIncorrect) %} + {% set bucket = (count / maxBucketSize * 9) | round(0, 'ceil') %} + {% set itemClass = 'frozen' ~ '-' ~ bucket %} + {% set label = count ~ ' ' ~ submissionText ~ ' in freeze' %} + {% else %} + {% set itemClass = index ~ '-' ~ bucket %} + {% set label = count ~ ' ' ~ index ~ ' ' ~ submissionText %} + {% endif %} +
+
+ {% endfor %} +
+ {% endfor %} +
+
+
+
+ {% endif %} + +
+ {% if show_submit_button | default(false) %} + {% if is_granted('ROLE_JURY') or (current_team_contest is not null and current_team_contest.freezeData.started) %} + + + Submit + + + {% else %} + + + Submit + + + {% endif %} + {% endif %} + + {% set clarificationsCount = 0 %} + {% set unseenClarificationCount = 0 %} + {% if clarifications[problem.probid] is defined %} + {% set clarificationsCount = clarifications[problem.probid] | length %} + {% for clar in clarifications[problem.probid] %} + {% if team and team.unreadClarifications.contains(clar) %} + {% set unseenClarificationCount = unseenClarificationCount + 1 %} + {% endif %} + {% endfor %} + {% endif %} + {% if clarificationsCount > 0 %} + + + {% if clarificationsCount > 0 %} + {% set badgeClass = 'text-bg-info' %} + {% if unseenClarificationCount > 0 %} + {% set badgeClass = 'text-bg-danger' %} + {% endif %} + {{ clarificationsCount }} + {% endif %} + clarifications + + {% endif %} + +
+
+ + {% if problem.problem.problemstatementType is not empty %} + + + statement + + {% endif %} + + {% if numsamples > 0 %} + + samples + + {% endif %} +
+ + {% if problem.problem.attachments | length > 0 %} +
+
    + {% for attachment in problem.problem.attachments %} +
  1. + + {{ attachment.name }} + +
  2. + {% endfor %} +
+ {% endif %} + +
+
+
+ {% endfor %} +