diff --git a/webapp/public/index.php b/webapp/public/index.php old mode 100644 new mode 100755 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..ddc797880c 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,8 +1010,9 @@ public function getContestStats(Contest $contest): ContestStatus } /** - * @return array{'problems': ContestProblem[], 'samples': string[], 'showLimits': bool, - * 'defaultMemoryLimit': int, 'timeFactorDiffers': bool, + * @param ScoreCache[]|null $cache + * @return array{'allproblems': array, 'samples': string[], 'showLimits': bool, + * 'defaultMemoryLimit': int, 'timeFactorDiffers': bool, 'solved': bool[], 'fullallproblems': ContestProblem[], * 'stats': array{'numBuckets': int, 'maxBucketSizeCorrect': int, * 'maxBucketSizeCorrect': int, 'maxBucketSizeIncorrect': int, * 'problems': array, @@ -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,25 @@ 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 { + $allProblems = [null => $problems]; + } $data = [ - 'problems' => $problems, + 'allproblems' => $allProblems, + 'fullallproblems' => [null => $problems], + 'solved' => $solvedProblem, 'samples' => $samples, 'showLimits' => $showLimits, 'defaultMemoryLimit' => $defaultMemoryLimit, diff --git a/webapp/src/Service/ScoreboardService.php b/webapp/src/Service/ScoreboardService.php index db9e99ecd7..3810ddb7a7 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 + public function getScorecache(?Contest $contest, ?Team $team = null): array { + if (!$contest) { + return []; + } $queryBuilder = $this->em->createQueryBuilder() ->from(ScoreCache::class, 's') ->select('s') diff --git a/webapp/templates/partials/problem_list.html.twig b/webapp/templates/partials/problem_list.html.twig index b85f2437df..3effa04a04 100644 --- a/webapp/templates/partials/problem_list.html.twig +++ b/webapp/templates/partials/problem_list.html.twig @@ -16,9 +16,24 @@ problemset {% endif %} + + {% if allproblems['solved'] is defined and allproblems['solved'] is not empty %} + + + show solved problems + + {% endif %} -{% if problems is empty %} +{% set found_problem_texts = false %} +{% for problems in allproblems %} + {% if problems is not empty %} + {% set found_problem_texts = true %} + {% endif %} +{% endfor %} + +{% if not found_problem_texts %}
No problem texts available at this point.
{% else %}
@@ -28,174 +43,19 @@
{% 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 %} +

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} %} - {% 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 %} -
+ {% 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 %}
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 %} +