From f8f6576d7f2ec24e1f40ac9125eaca8e81d5bbbf Mon Sep 17 00:00:00 2001 From: Tobias Werth Date: Wed, 13 Mar 2024 20:59:05 +0100 Subject: [PATCH] Revert "Keep track of potential first to solves so we can update them when we get 'delayed' results." This reverts commit 9f4dcc0e2be0288ffda458c77e2fbca23dbf17d2. https://github.com/DOMjudge/domjudge/pull/2282 was merged by accident and needs more thought. --- webapp/migrations/Version20231229094359.php | 36 ------ .../DataFixtures/Test/SampleTeamsFixture.php | 45 ------- webapp/src/Entity/ScoreCache.php | 17 --- webapp/src/Service/ScoreboardService.php | 69 ++--------- .../Unit/Service/ScoreboardServiceTest.php | 117 ------------------ 5 files changed, 12 insertions(+), 272 deletions(-) delete mode 100644 webapp/migrations/Version20231229094359.php delete mode 100644 webapp/src/DataFixtures/Test/SampleTeamsFixture.php delete mode 100644 webapp/tests/Unit/Service/ScoreboardServiceTest.php diff --git a/webapp/migrations/Version20231229094359.php b/webapp/migrations/Version20231229094359.php deleted file mode 100644 index 0d2ebfba4d..0000000000 --- a/webapp/migrations/Version20231229094359.php +++ /dev/null @@ -1,36 +0,0 @@ -addSql('ALTER TABLE scorecache ADD is_potential_first_to_solve TINYINT(1) DEFAULT 0 NOT NULL COMMENT \'Is this potentially the first solution to this problem?\' AFTER is_first_to_solve'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('ALTER TABLE scorecache DROP is_potential_first_to_solve'); - } - - public function isTransactional(): bool - { - return false; - } -} diff --git a/webapp/src/DataFixtures/Test/SampleTeamsFixture.php b/webapp/src/DataFixtures/Test/SampleTeamsFixture.php deleted file mode 100644 index 3b82793c8f..0000000000 --- a/webapp/src/DataFixtures/Test/SampleTeamsFixture.php +++ /dev/null @@ -1,45 +0,0 @@ -getRepository(TeamAffiliation::class)->findOneBy(['externalid' => 'utrecht']); - $category = $manager->getRepository(TeamCategory::class)->findOneBy(['externalid' => 'participants']); - - $team1 = new Team(); - $team1 - ->setExternalid('team1') - ->setIcpcid('team1') - ->setLabel('team1') - ->setName('Team 1') - ->setAffiliation($affiliation) - ->setCategory($category); - - $team2 = new Team(); - $team2 - ->setExternalid('team2') - ->setIcpcid('team2') - ->setLabel('team2') - ->setName('Team 2') - ->setAffiliation($affiliation) - ->setCategory($category); - - $manager->persist($team1); - $manager->persist($team2); - $manager->flush(); - - $this->addReference(self::FIRST_TEAM_REFERENCE, $team1); - $this->addReference(self::SECOND_TEAM_REFERENCE, $team2); - } -} diff --git a/webapp/src/Entity/ScoreCache.php b/webapp/src/Entity/ScoreCache.php index df8d7acb92..67aa8bcf0b 100644 --- a/webapp/src/Entity/ScoreCache.php +++ b/webapp/src/Entity/ScoreCache.php @@ -94,12 +94,6 @@ class ScoreCache ])] private bool $is_first_to_solve = false; - #[ORM\Column(options: [ - 'comment' => 'Is this potentially the first solution to this problem?', - 'default' => 0, - ])] - private bool $is_potential_first_to_solve = false; - #[ORM\Id] #[ORM\ManyToOne] #[ORM\JoinColumn(name: 'cid', referencedColumnName: 'cid', onDelete: 'CASCADE')] @@ -236,17 +230,6 @@ public function getIsFirstToSolve() : bool return $this->is_first_to_solve; } - public function setIsPotentialFirstToSolve(bool $isPotentialFirstToSolve): ScoreCache - { - $this->is_potential_first_to_solve = $isPotentialFirstToSolve; - return $this; - } - - public function getIsPotentialFirstToSolve() : bool - { - return $this->is_potential_first_to_solve; - } - public function setContest(?Contest $contest = null): ScoreCache { $this->contest = $contest; diff --git a/webapp/src/Service/ScoreboardService.php b/webapp/src/Service/ScoreboardService.php index e54f7516e9..4bad3a1db8 100644 --- a/webapp/src/Service/ScoreboardService.php +++ b/webapp/src/Service/ScoreboardService.php @@ -254,8 +254,7 @@ public function calculateScoreRow( Contest $contest, Team $team, Problem $problem, - bool $updateRankCache = true, - bool $updatePotentialFirstToSolves = true + bool $updateRankCache = true ): void { $this->logger->debug( "ScoreboardService::calculateScoreRow '%d' '%d' '%d'", @@ -418,7 +417,6 @@ public function calculateScoreRow( // See if this submission was the first to solve this problem. // Only relevant if it was correct in the first place. $firstToSolve = false; - $potentialFirstToSolve = false; if ($correctJury) { $params = [ 'cid' => $contest->getCid(), @@ -439,49 +437,32 @@ public function calculateScoreRow( // - or already judged to be correct (if it is judged but not correct, // it is not a first to solve) // - or the submission is still queued for judgement (judgehost is NULL). - // If there are no valid correct submissions submitted earlier but there - // are submissions awaiting judgement, we are potentially the first to solve. - // We need to keep track of this since we later need to set the actual first to solve. - $verificationRequiredExtra = ($verificationRequired && !$useExternalJudgements) ? 'OR j.verified = 0' : ''; + $verificationRequiredExtra = $verificationRequired ? 'OR j.verified = 0' : ''; if ($useExternalJudgements) { - $baseQuery = ' + $firstToSolve = 0 == $this->em->getConnection()->fetchOne(' SELECT count(*) FROM submission s LEFT JOIN external_judgement ej USING (submitid) LEFT JOIN external_judgement ej2 ON ej2.submitid = s.submitid AND ej2.starttime > ej.starttime LEFT JOIN team t USING(teamid) LEFT JOIN team_category tc USING (categoryid) WHERE s.valid = 1 AND + (ej.result IS NULL OR ej.result = :correctResult '. + $verificationRequiredExtra.') AND s.cid = :cid AND s.probid = :probid AND tc.sortorder = :teamSortOrder AND - round(s.submittime,4) < :submitTime'; - $judgingTable = 'ej'; + round(s.submittime,4) < :submitTime', $params); } else { - $baseQuery = ' + $firstToSolve = 0 == $this->em->getConnection()->fetchOne(' SELECT count(*) FROM submission s LEFT JOIN judging j ON (s.submitid=j.submitid AND j.valid=1) LEFT JOIN team t USING (teamid) LEFT JOIN team_category tc USING (categoryid) WHERE s.valid = 1 AND + (j.judgingid IS NULL OR j.result IS NULL OR j.result = :correctResult '. + $verificationRequiredExtra.') AND s.cid = :cid AND s.probid = :probid AND tc.sortorder = :teamSortOrder AND - round(s.submittime,4) < :submitTime'; - $judgingTable = 'j'; - } - - $numEarlierCorrect = $this->em->getConnection()->fetchOne( - "$baseQuery AND $judgingTable.result = :correctResult", - $params); - $numEarlierPending = $this->em->getConnection()->fetchOne( - "$baseQuery AND ($judgingTable.result IS NULL $verificationRequiredExtra)", - $params); - - if ($numEarlierCorrect == 0) { - // This is either a first to solve or potential first to solve - if ($numEarlierPending == 0) { - $firstToSolve = true; - } else { - $potentialFirstToSolve = true; - } + round(s.submittime,4) < :submitTime', $params); } } @@ -501,14 +482,13 @@ public function calculateScoreRow( 'runtimePublic' => $runtimePubl === PHP_INT_MAX ? 0 : $runtimePubl, 'isCorrectPublic' => (int)$correctPubl, 'isFirstToSolve' => (int)$firstToSolve, - 'isPotentialFirstToSolve' => (int)$potentialFirstToSolve, ]; $this->em->getConnection()->executeQuery('REPLACE INTO scorecache (cid, teamid, probid, submissions_restricted, pending_restricted, solvetime_restricted, runtime_restricted, is_correct_restricted, - submissions_public, pending_public, solvetime_public, runtime_public, is_correct_public, is_first_to_solve, is_potential_first_to_solve) + submissions_public, pending_public, solvetime_public, runtime_public, is_correct_public, is_first_to_solve) VALUES (:cid, :teamid, :probid, :submissionsRestricted, :pendingRestricted, :solvetimeRestricted, :runtimeRestricted, :isCorrectRestricted, - :submissionsPublic, :pendingPublic, :solvetimePublic, :runtimePublic, :isCorrectPublic, :isFirstToSolve, :isPotentialFirstToSolve)', $params); + :submissionsPublic, :pendingPublic, :solvetimePublic, :runtimePublic, :isCorrectPublic, :isFirstToSolve)', $params); if ($this->em->getConnection()->fetchOne('SELECT RELEASE_LOCK(:lock)', ['lock' => $lockString]) != 1) { @@ -519,31 +499,6 @@ public function calculateScoreRow( if ($updateRankCache && ($correctJury || $correctPubl)) { $this->updateRankCache($contest, $team); } - - // If we did not have a first to solve, we need to check if we have any - // potential first to solve that are now the first to solve. - // We only do this if there are no pending submissions for this problem - // for this team and if this submission is not a potential first to solve itself. - // We also do this only once, to not have infinite loops if we have multiple - // potential first to solves. - if ($updatePotentialFirstToSolves && $pendingJury === 0 && !$potentialFirstToSolve) { - /** @var ScoreCache[] $potentialFirstToSolves */ - $potentialFirstToSolves = $this->em->createQueryBuilder() - ->from(ScoreCache::class, 's') - ->join('s.team', 't') - ->select('s', 't') - ->andWhere('s.contest = :contest') - ->andWhere('s.problem = :problem') - ->andWhere('s.is_potential_first_to_solve = 1') - ->setParameter('contest', $contest) - ->setParameter('problem', $problem) - ->getQuery() - ->getResult(); - - foreach ($potentialFirstToSolves as $row) { - $this->calculateScoreRow($contest, $row->getTeam(), $problem, $updateRankCache, false); - } - } } /** diff --git a/webapp/tests/Unit/Service/ScoreboardServiceTest.php b/webapp/tests/Unit/Service/ScoreboardServiceTest.php deleted file mode 100644 index be00ac8313..0000000000 --- a/webapp/tests/Unit/Service/ScoreboardServiceTest.php +++ /dev/null @@ -1,117 +0,0 @@ -get(EntityManagerInterface::class); - $this->loadFixture(SampleTeamsFixture::class); - $team1 = $this->fixtureExecutor->getReferenceRepository()->getReference(SampleTeamsFixture::FIRST_TEAM_REFERENCE); - $team2 = $this->fixtureExecutor->getReferenceRepository()->getReference(SampleTeamsFixture::SECOND_TEAM_REFERENCE); - $contest = $em->getRepository(Contest::class)->findOneBy(['shortname' => 'demo']); - $contestProblem = $contest->getProblems()->first(); - $problem = $contestProblem->getProblem(); - - $scoreboardService = static::getContainer()->get(ScoreboardService::class); - - // Create a submission for both team 1 and team 2. - // The submission for team 1 will be pending, while the submission - // for team 2 will be correct. - $submission1 = $this->createSubmission($em, $team1, $contestProblem, $contest, null); - $submission2 = $this->createSubmission($em, $team2, $contestProblem, $contest, 'correct'); - $em->flush(); - - $scoreboardService->calculateScoreRow($contest, $team1, $problem); - $scoreboardService->calculateScoreRow($contest, $team2, $problem); - - /** @var ScoreCache $scoreCacheTeam1 */ - $scoreCacheTeam1 = $em->getRepository(ScoreCache::class)->findOneBy([ - 'contest' => $contest, - 'team' => $team1, - 'problem' => $problem, - ]); - /** @var ScoreCache $scoreCacheTeam2 */ - $scoreCacheTeam2 = $em->getRepository(ScoreCache::class)->findOneBy([ - 'contest' => $contest, - 'team' => $team2, - 'problem' => $problem, - ]); - - static::assertFalse($scoreCacheTeam1->getIsFirstToSolve()); - static::assertFalse($scoreCacheTeam2->getIsFirstToSolve()); - - // Now update the submission for team 1 to be wrong - $submission1->getJudgings()->first()->setResult('wrong'); - $em->flush(); - - $scoreboardService->calculateScoreRow($contest, $team1, $problem); - - // We need to clear the entity manager to make sure we get the updated score caches. - $em->clear(); - - /** @var ScoreCache $scoreCacheTeam1 */ - $scoreCacheTeam1 = $em->getRepository(ScoreCache::class)->findOneBy([ - 'contest' => $contest, - 'team' => $team1, - 'problem' => $problem, - ]); - /** @var ScoreCache $scoreCacheTeam2 */ - $scoreCacheTeam2 = $em->getRepository(ScoreCache::class)->findOneBy([ - 'contest' => $contest, - 'team' => $team2, - 'problem' => $problem, - ]); - - static::assertFalse($scoreCacheTeam1->getIsFirstToSolve()); - static::assertTrue($scoreCacheTeam2->getIsFirstToSolve()); - } - - protected function createSubmission( - EntityManagerInterface $em, - Team $team, - ContestProblem $problem, - Contest $contest, - ?string $result - ): Submission { - $submission = new Submission(); - $submission - ->setTeam($team) - ->setProblem($problem->getProblem()) - ->setContest($contest) - ->setContestProblem($problem) - ->setLanguage($em->getRepository(Language::class)->findOneBy(['name' => 'C++'])) - ->setValid(true) - ->setSubmittime(Utils::now()) - ->addJudging( - $judging = (new Judging()) - ->setValid(true) - ->setStarttime(Utils::now()) - ->setEndtime(Utils::now()) - ->setSubmission($submission) - ->setResult($result) - ); - - $em->persist($submission); - $em->persist($judging); - - return $submission; - } -}