From b83c31d505d8c7752f8af10043b642239792eab6 Mon Sep 17 00:00:00 2001 From: Tobias Werth Date: Fri, 24 Nov 2023 18:16:17 +0100 Subject: [PATCH] Warn if a submission was judged by two different compiler/runner versions. --- webapp/migrations/Version20231124133956.php | 40 +++++++++++++++++++ .../Controller/API/JudgehostController.php | 17 +++++--- .../Controller/Jury/SubmissionController.php | 35 +++++++++++++--- webapp/src/Entity/JudgeTask.php | 16 ++++++++ webapp/src/Entity/Version.php | 6 +++ webapp/templates/jury/submission.html.twig | 17 ++++++++ 6 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 webapp/migrations/Version20231124133956.php diff --git a/webapp/migrations/Version20231124133956.php b/webapp/migrations/Version20231124133956.php new file mode 100644 index 00000000000..c52de3427a2 --- /dev/null +++ b/webapp/migrations/Version20231124133956.php @@ -0,0 +1,40 @@ +addSql('ALTER TABLE judgetask ADD versionid INT UNSIGNED DEFAULT NULL COMMENT \'Version ID\''); + $this->addSql('ALTER TABLE judgetask ADD CONSTRAINT FK_83142B704034DFAF FOREIGN KEY (versionid) REFERENCES version (versionid) ON DELETE SET NULL'); + $this->addSql('CREATE INDEX IDX_83142B704034DFAF ON judgetask (versionid)'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE judgetask DROP FOREIGN KEY FK_83142B704034DFAF'); + $this->addSql('DROP INDEX IDX_83142B704034DFAF ON judgetask'); + $this->addSql('ALTER TABLE judgetask DROP versionid'); + } + + public function isTransactional(): bool + { + return false; + } +} diff --git a/webapp/src/Controller/API/JudgehostController.php b/webapp/src/Controller/API/JudgehostController.php index dfb75098fcb..bd3491edb1b 100644 --- a/webapp/src/Controller/API/JudgehostController.php +++ b/webapp/src/Controller/API/JudgehostController.php @@ -1284,27 +1284,29 @@ public function checkVersions(Request $request, string $judgetaskid): array $this->em->wrapInTransaction(function () use ( $judgehost, $reportedVersions, - $language + $language, + $judgeTask ) { $activeVersion = $this->em->getRepository(Version::class) ->findOneBy(['language' => $language, 'judgehost' => $judgehost, 'active' => true]); - $newVersion = false; + $isNewVersion = false; if (!$activeVersion) { - $newVersion = true; + $isNewVersion = true; } else { $reportedCompilerVersion = $reportedVersions['compiler'] ?? null; if ($activeVersion->getCompilerVersion() !== $reportedCompilerVersion) { - $newVersion = true; + $isNewVersion = true; } $reportedRunnerVersion = $reportedVersions['runner'] ?? null; if ($activeVersion->getRunnerVersion() !== $reportedRunnerVersion) { - $newVersion = true; + $isNewVersion = true; } } - if ($newVersion) { + if ($isNewVersion) { if ($activeVersion) { $activeVersion->setActive(false); + $this->em->flush(); } $activeVersion = new Version(); $activeVersion @@ -1326,6 +1328,9 @@ public function checkVersions(Request $request, string $judgetaskid): array $this->em->persist($activeVersion); $this->em->flush(); } + + $judgeTask->setVersion($activeVersion); + // TODO: Optionally check version here against canonical version. }); return []; diff --git a/webapp/src/Controller/Jury/SubmissionController.php b/webapp/src/Controller/Jury/SubmissionController.php index d92a3e281e8..0fe350d8941 100644 --- a/webapp/src/Controller/Jury/SubmissionController.php +++ b/webapp/src/Controller/Jury/SubmissionController.php @@ -517,6 +517,7 @@ public function viewAction( 'claimWarning' => $claimWarning, 'combinedRunCompare' => $submission->getProblem()->getCombinedRunCompare(), 'requestedOutputCount' => $requestedOutputCount, + 'version_warnings' => [], ]; if ($selectedJudging === null) { @@ -527,21 +528,43 @@ public function viewAction( ]; } else { $contestProblem = $submission->getContestProblem(); - /** @var JudgeTask $judgeTask */ - $judgeTask = $this->em->getRepository(JudgeTask::class)->findOneBy(['jobid' => $selectedJudging->getJudgingid()]); - if ($judgeTask !== null) { + /** @var JudgeTask[] $judgeTasks */ + $judgeTasks = $this->em->getRepository(JudgeTask::class)->findBy(['jobid' => $selectedJudging->getJudgingid()]); + $unique_compiler_versions = []; + $unique_runner_versions = []; + $sampleJudgeTask = null; + foreach ($judgeTasks as $judgeTask) { + $sampleJudgeTask = $judgeTask; + $version = $judgeTask->getVersion(); + if (!$version) { + continue; + } + if ($version->getCompilerVersion()) { + $unique_compiler_versions[$version->getCompilerVersion()] = true; + } + if ($version->getRunnerVersion()) { + $unique_runner_versions[$version->getRunnerVersion()] = true; + } + } + if (count($unique_compiler_versions) > 1) { + $twigData['version_warnings']['compiler'] = array_keys($unique_compiler_versions); + } + if (count($unique_runner_versions) > 1) { + $twigData['version_warnings']['runner'] = array_keys($unique_runner_versions); + } + if ($sampleJudgeTask !== null) { $errors = []; $this->maybeGetErrors('Compile config', $this->dj->getCompileConfig($submission), - $judgeTask->getCompileConfig(), + $sampleJudgeTask->getCompileConfig(), $errors); $this->maybeGetErrors('Run config', $this->dj->getRunConfig($contestProblem, $submission), - $judgeTask->getRunConfig(), + $sampleJudgeTask->getRunConfig(), $errors); $this->maybeGetErrors('Compare config', $this->dj->getCompareConfig($contestProblem), - $judgeTask->getCompareConfig(), + $sampleJudgeTask->getCompareConfig(), $errors); if (!empty($errors)) { if ($selectedJudging->getValid()) { diff --git a/webapp/src/Entity/JudgeTask.php b/webapp/src/Entity/JudgeTask.php index c4453f0e265..5dcbcdb0dad 100644 --- a/webapp/src/Entity/JudgeTask.php +++ b/webapp/src/Entity/JudgeTask.php @@ -161,6 +161,11 @@ public function getSubmitid(): ?int #[Serializer\Exclude] private Collection $judging_runs; + #[ORM\ManyToOne(inversedBy: 'judgetasks')] + #[ORM\JoinColumn(name: 'versionid', referencedColumnName: 'versionid', onDelete: 'SET NULL')] + #[Serializer\Exclude] + private ?Version $version = null; + public function __construct() { $this->judging_runs = new ArrayCollection(); @@ -379,4 +384,15 @@ public function getFirstJudgingRun(): ?JudgingRun { return $this->judging_runs->first() ?: null; } + + public function setVersion(Version $version): JudgeTask + { + $this->version = $version; + return $this; + } + + public function getVersion(): ?Version + { + return $this->version; + } } diff --git a/webapp/src/Entity/Version.php b/webapp/src/Entity/Version.php index dc2a55bf948..bdaf25454a9 100644 --- a/webapp/src/Entity/Version.php +++ b/webapp/src/Entity/Version.php @@ -2,7 +2,9 @@ namespace App\Entity; +use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Doctrine\ORM\PersistentCollection; use JMS\Serializer\Annotation as Serializer; /** @@ -59,6 +61,10 @@ class Version #[Serializer\Exclude] private bool $active = true; + #[ORM\OneToMany(mappedBy: 'version', targetEntity: JudgeTask::class)] + #[Serializer\Exclude] + private Collection $judgeTasks; + public function getVersionid(): ?int { return $this->versionid; diff --git a/webapp/templates/jury/submission.html.twig b/webapp/templates/jury/submission.html.twig index ee8d9cf0ac4..ce24fe7f70c 100644 --- a/webapp/templates/jury/submission.html.twig +++ b/webapp/templates/jury/submission.html.twig @@ -62,6 +62,23 @@ {% endif %} {% endif %} + {% if version_warnings is not null and version_warnings is not empty %} + {% for type, versions in version_warnings %} +
+

+ This judging did not use the same {{ type}} version for all testcases. + This may lead to unexpected results. Versions used: +

+

+ {% for version in versions %} +

{{ version }}
+ {% if not loop.last %}
{% endif %} + {% endfor %} +

+
+ {% endfor %} + {% endif %} +

Submission {{ submission.submitid }}