Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified webapp/public/index.php
100644 → 100755
Empty file.
5 changes: 4 additions & 1 deletion webapp/src/Controller/Team/ProblemController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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));
}


Expand Down
28 changes: 24 additions & 4 deletions webapp/src/Service/DOMJudgeService.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1009,18 +1010,20 @@
}

/**
* @return array{'problems': ContestProblem[], 'samples': string[], 'showLimits': bool,
* 'defaultMemoryLimit': int, 'timeFactorDiffers': bool,
* @param ScoreCache[]|null $cache
* @return array{'allproblems': <bool, ContestProblem[]>, 'samples': string[], 'showLimits': bool,

Check failure on line 1014 in webapp/src/Service/DOMJudgeService.php

View workflow job for this annotation

GitHub Actions / phpstan

PHPDoc tag @return has invalid value (array{'allproblems': <bool, ContestProblem[]>, 'samples': string[], 'showLimits': bool, 'defaultMemoryLimit': int, 'timeFactorDiffers': bool, 'solved': bool[], 'fullallproblems': array<int, ContestProblem[]>, 'stats': array{'numBuckets': int, 'maxBucketSizeCorrect': int, 'maxBucketSizeCorrect': int, 'maxBucketSizeIncorrect': int, 'problems': array<array{'correct': array<array{'start': DateTime, 'end': DateTime, 'count': int}>, 'incorrect': array<array{'start': DateTime, 'end': DateTime, 'count': int}>}>}}): Unexpected token ":", expected '}' at offset 77 on line 3
* 'defaultMemoryLimit': int, 'timeFactorDiffers': bool, 'solved': bool[], 'fullallproblems': array<int, ContestProblem[]>,
* 'stats': array{'numBuckets': int, 'maxBucketSizeCorrect': int,
* 'maxBucketSizeCorrect': int, 'maxBucketSizeIncorrect': int,
* 'problems': array<array{'correct': array<array{'start': DateTime, 'end': DateTime, 'count': int}>,
* 'incorrect': array<array{'start': DateTime, 'end': DateTime, 'count': int}>}>}}
* @throws NonUniqueResultException
*/
public function getTwigDataForProblemsAction(

Check failure on line 1022 in webapp/src/Service/DOMJudgeService.php

View workflow job for this annotation

GitHub Actions / phpstan

Method App\Service\DOMJudgeService::getTwigDataForProblemsAction() return type has no value type specified in iterable type array.
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');
Expand Down Expand Up @@ -1093,8 +1096,25 @@
}
}

$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,
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/Service/ScoreboardService.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
$queryBuilder = $this->em->createQueryBuilder()
->from(ScoreCache::class, 's')
Expand Down
196 changes: 28 additions & 168 deletions webapp/templates/partials/problem_list.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,24 @@
problemset
</a>
{% endif %}

{% if allproblems['solved'] is defined and allproblems['solved'] is not empty %}
<a class="btn btn-secondary" role="button"
data-bs-toggle="collapse" data-bs-target=".collapse-solved-problems">
<i class="fas fa-check-double"></i>
show solved problems
</a>
{% endif %}
</h1>

{% 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 %}
<div class="alert alert-secondary">No problem texts available at this point.</div>
{% else %}
<div class="container">
Expand All @@ -28,174 +43,19 @@
</div>
{% endif %}

<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3 problem-list">
{% for problem in problems %}
<div class="col">
{% set numsamples = samples[problem.probid] %}
{% if problem.problem.interactiveProblem %}
{% set numsamples = 0 %}
{% endif %}
<div class="card">
<div class="card-body">
<h2 class="card-title">
{{ problem | problemBadge }}
</h2>
<h3 class="card-subtitle mb-2 text-muted">
{{ problem.problem.name }}
</h3>
{% if showLimits %}
<h4 class="card-subtitle mb-2 text-muted">
Limits: {{ problem.problem.timelimit }}
second
{%- if problem.problem.timelimit > 1 %}s{% endif %}
{%- if timeFactorDiffers -%}
<sup>*</sup>
{% endif %}
/
{{ ((problem.problem.memlimit | default(defaultMemoryLimit)) * 1024) | printSize }}
</h4>
{% endif %}
{% if problem.problem.languages | length != 0 %}
<h4 class="card-subtitle mb-2 text-muted">
Language{% if problem.problem.languages | length > 1 %}s{% endif %}:
{% for lang in problem.problem.languages %}
<code>{{ lang.name }}</code>{% if not loop.last %}, {% endif %}
{% endfor %}
</h4>
{% endif %}
<h4 class="card-subtitle mb-2 text-muted">
Type: {{ problem.problem.typesAsString }}
</h4>

{% if stats is defined %}
<div class="mt-3">
{% for correct in [true, false] %}
<div class="problem-stats d-flex justify-content-center">
{% 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 %}
<div
class="problem-stats-item {{ itemClass }}"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-html="true"
title="Between {{ stat.start.timestamp | printtime(null, contest) }} and {{ stat.end.timestamp | printtime(null, contest) }}:<br/>{{ label }}">
</div>
{% endfor %}
</div>
{% endfor %}
</div>
<div>
<br/>
</div>
{% endif %}

<div class="text-center">
{% if show_submit_button | default(false) %}
{% if is_granted('ROLE_JURY') or (current_team_contest is not null and current_team_contest.freezeData.started) %}
<a class="justify-content-center" data-ajax-modal data-ajax-modal-after="initSubmitModal" href="{{ path('team_submit', {problem: problem.probid}) }}">
<span class="btn btn-success">
<i class="fas fa-cloud-upload-alt"></i> Submit
</span>
</a>
{% else %}
<a class="justify-content-center">
<span class="btn btn-success disabled" disabled>
<i class="fas fa-cloud-upload-alt"></i> Submit
</span>
</a>
{% endif %}
{% endif %}
<h3>Alternative 1: Enable/Disable with button</h3>
{% include 'partials/problem_list_cards.html.twig' with {'problems': fullallproblems[null], 'collapse': 2} %}
<hr>
<h3>Alternative 2: Those would be there by default</h3>
{% 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 %}
<a data-ajax-modal class="btn btn-secondary" role="button"
href="{{ path('team_clarification_by_prob', {'probId': problem.probid}) }}">
<i class="fas fa-question-circle"></i>
{% if clarificationsCount > 0 %}
{% set badgeClass = 'text-bg-info' %}
{% if unseenClarificationCount > 0 %}
{% set badgeClass = 'text-bg-danger' %}
{% endif %}
<span class="badge {{ badgeClass }}">{{ clarificationsCount }}</span>
{% endif %}
clarifications
</a>
{% endif %}

<div style="margin-top: 10px;">
</div>

{% if problem.problem.problemstatementType is not empty %}
<a class="btn btn-secondary" role="button"
href="{{ path(problem_statement_path, {'probId': problem.probid}) }}">
<i class="fas fa-file-{{ problem.problem.problemstatementType }}"></i>
statement
</a>
{% endif %}

{% if numsamples > 0 %}
<a class="btn btn-secondary" role="button"
href="{{ path(problem_sample_zip_path, {'probId': problem.probid}) }}">
<i class="fas fa-file-archive"></i> samples
</a>
{% endif %}
</div>

{% if problem.problem.attachments | length > 0 %}
<hr/>
<ol class="text-center list-group list-group-flush">
{% for attachment in problem.problem.attachments %}
<li class="list-group-item">
<a class="btn btn-outline-secondary" role="button"
href="{{ path(problem_attachment_path, {'probId': problem.probid, 'attachmentId': attachment.attachmentid}) }}">
<i class="{{ attachment.type | fileTypeIcon }}"></i> {{ attachment.name }}
</a>
</li>
{% endfor %}
</ol>
{% endif %}

</div>
</div>
</div>
{% endfor %}
</div>
{% if allproblems['solved'] is defined %}
<h1 class="mt-4 text-center" data-bs-toggle="collapse" href="#alreadySolved">
Alternative 2: Already solved problems
<i class="fas fa-chevron-down"></i>
</h1>
{% include 'partials/problem_list_cards.html.twig' with {'problems': allproblems['solved'], 'collapse': 1} %}
{% endif %}

{% if showLimits and timeFactorDiffers %}
<div class="row">
Expand Down
Loading
Loading