|
| 1 | +<?php declare(strict_types=1); |
| 2 | + |
| 3 | +namespace App\Command; |
| 4 | + |
| 5 | +use App\DataTransferObject\Result; |
| 6 | +use Symfony\Component\Console\Attribute\AsCommand; |
| 7 | +use Symfony\Component\Serializer\Encoder\CsvEncoder; |
| 8 | +use Symfony\Component\Serializer\Exception\ExceptionInterface; |
| 9 | + |
| 10 | +#[AsCommand( |
| 11 | + name: 'compare:results', |
| 12 | + description: 'Compare results between two files' |
| 13 | +)] |
| 14 | +class CompareResultsCommand extends AbstractCompareCommand |
| 15 | +{ |
| 16 | + |
| 17 | + protected function compare( |
| 18 | + array &$messages, |
| 19 | + bool &$success, |
| 20 | + string $file1, |
| 21 | + string $file2, |
| 22 | + ): void { |
| 23 | + $results1Contents = file_get_contents($file1); |
| 24 | + if (!str_starts_with($results1Contents, "results\t1")) { |
| 25 | + $this->addMessage($messages, $success, 'error', sprintf("File \"%s\" does not start with \"results\t1\"", $file1)); |
| 26 | + } |
| 27 | + $results2Contents = file_get_contents($file2); |
| 28 | + if (!str_starts_with($results2Contents, "results\t1")) { |
| 29 | + $this->addMessage($messages, $success, 'error', sprintf("File \"%s\" does not start with \"results\t1\"", $file2)); |
| 30 | + } |
| 31 | + |
| 32 | + if (!$success) { |
| 33 | + return; |
| 34 | + } |
| 35 | + |
| 36 | + $results1Contents = substr($results1Contents, strpos($results1Contents, "\n") + 1); |
| 37 | + $results2Contents = substr($results2Contents, strpos($results2Contents, "\n") + 1); |
| 38 | + |
| 39 | + // Prefix both files with a fake header, so we can deserialize them |
| 40 | + $results1Contents = "team_id\trank\taward\tnum_solved\ttotal_time\tlast_time\tgroup_winner\n" . $results1Contents; |
| 41 | + $results2Contents = "team_id\trank\taward\tnum_solved\ttotal_time\tlast_time\tgroup_winner\n" . $results2Contents; |
| 42 | + |
| 43 | + try { |
| 44 | + /** @var Result[] $results1 */ |
| 45 | + $results1 = $this->serializer->deserialize($results1Contents, Result::class . '[]', 'csv', [ |
| 46 | + CsvEncoder::DELIMITER_KEY => "\t", |
| 47 | + ]); |
| 48 | + } catch (ExceptionInterface $e) { |
| 49 | + $this->addMessage($messages, $success, 'error', sprintf('Error deserializing file "%s": %s', $file1, $e->getMessage())); |
| 50 | + } |
| 51 | + |
| 52 | + try { |
| 53 | + /** @var Result[] $results2 */ |
| 54 | + $results2 = $this->serializer->deserialize($results2Contents, Result::class . '[]', 'csv', [ |
| 55 | + CsvEncoder::DELIMITER_KEY => "\t", |
| 56 | + ]); |
| 57 | + } catch (ExceptionInterface $e) { |
| 58 | + $this->addMessage($messages, $success, 'error', sprintf('Error deserializing file "%s": %s', $file2, $e->getMessage())); |
| 59 | + } |
| 60 | + |
| 61 | + if (!$success || !isset($results1) || !isset($results2)) { |
| 62 | + return; |
| 63 | + } |
| 64 | + |
| 65 | + // Sort results for both files: first by num_solved, then by total_time |
| 66 | + usort($results1, fn( |
| 67 | + Result $a, |
| 68 | + Result $b |
| 69 | + ) => $a->numSolved === $b->numSolved ? $a->totalTime <=> $b->totalTime : $b->numSolved <=> $a->numSolved); |
| 70 | + usort($results2, fn( |
| 71 | + Result $a, |
| 72 | + Result $b |
| 73 | + ) => $a->numSolved === $b->numSolved ? $a->totalTime <=> $b->totalTime : $b->numSolved <=> $a->numSolved); |
| 74 | + |
| 75 | + /** @var array<string,Result> $results1Indexed */ |
| 76 | + $results1Indexed = []; |
| 77 | + foreach ($results1 as $result) { |
| 78 | + $results1Indexed[$result->teamId] = $result; |
| 79 | + } |
| 80 | + |
| 81 | + /** @var array<string,Result> $results2Indexed */ |
| 82 | + $results2Indexed = []; |
| 83 | + foreach ($results2 as $result) { |
| 84 | + $results2Indexed[$result->teamId] = $result; |
| 85 | + } |
| 86 | + |
| 87 | + foreach ($results1 as $result) { |
| 88 | + if (!isset($results2Indexed[$result->teamId])) { |
| 89 | + $this->addMessage($messages, $success, 'error', sprintf('Team "%s" not found in second file', $result->teamId)); |
| 90 | + } else { |
| 91 | + $result2 = $results2Indexed[$result->teamId]; |
| 92 | + if ($result->rank !== $result2->rank) { |
| 93 | + $this->addMessage($messages, $success, 'error', sprintf('Team %s has different rank', $result->teamId), (string)$result->rank, (string)$result2->rank); |
| 94 | + } |
| 95 | + if ($result->award !== $result2->award) { |
| 96 | + $this->addMessage($messages, $success, 'error', sprintf('Team %s has different award', $result->teamId), $result->award, $result2->award); |
| 97 | + } |
| 98 | + if ($result->numSolved !== $result2->numSolved) { |
| 99 | + $this->addMessage($messages, $success, 'error', sprintf('Team %s has different num_solved', $result->teamId), (string)$result->numSolved, (string)$result2->numSolved); |
| 100 | + } |
| 101 | + if ($result->totalTime !== $result2->totalTime) { |
| 102 | + $this->addMessage($messages, $success, 'error', sprintf('Team %s has different total_time', $result->teamId), (string)$result->totalTime, (string)$result2->totalTime); |
| 103 | + } |
| 104 | + if ($result->lastTime !== $result2->lastTime) { |
| 105 | + $this->addMessage($messages, $success, 'error', sprintf('Team %s has different last_time', $result->teamId), (string)$result->lastTime, (string)$result2->lastTime); |
| 106 | + } |
| 107 | + if ($result->groupWinner !== $result2->groupWinner) { |
| 108 | + $this->addMessage($messages, $success, 'warning', sprintf('Team %s has different group_winner', $result->teamId), (string)$result->groupWinner, (string)$result2->groupWinner); |
| 109 | + } |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + foreach ($results2 as $result) { |
| 114 | + if (!isset($results1Indexed[$result->teamId])) { |
| 115 | + $this->addMessage($messages, $success, 'error', sprintf('Team %s not found in first file', $result->teamId)); |
| 116 | + } |
| 117 | + } |
| 118 | + } |
| 119 | +} |
0 commit comments