Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.13% covered (warning)
89.13%
41 / 46
57.14% covered (warning)
57.14%
4 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
SyncSolvResultsCommand
89.13% covered (warning)
89.13%
41 / 46
57.14% covered (warning)
57.14%
4 / 7
15.29
0.00% covered (danger)
0.00%
0 / 1
 getAllowedAppEnvs
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 configure
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setSolvResultParser
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 handle
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
3.21
 syncSolvResultsForYear
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
2
 importSolvResultsForYear
86.67% covered (warning)
86.67%
13 / 15
0.00% covered (danger)
0.00%
0 / 1
6.09
1<?php
2
3namespace Olz\Command;
4
5use Olz\Command\Common\OlzCommand;
6use Olz\Entity\SolvEvent;
7use Olz\Parsers\SolvResultParser;
8use Symfony\Component\Console\Attribute\AsCommand;
9use Symfony\Component\Console\Command\Command;
10use Symfony\Component\Console\Input\InputArgument;
11use Symfony\Component\Console\Input\InputInterface;
12use Symfony\Component\Console\Output\OutputInterface;
13
14#[AsCommand(name: 'olz:sync-solv-results')]
15class SyncSolvResultsCommand extends OlzCommand {
16    /** @return array<string> */
17    protected function getAllowedAppEnvs(): array {
18        return ['dev', 'test', 'staging', 'prod'];
19    }
20
21    protected function configure(): void {
22        $this->addArgument('year', InputArgument::REQUIRED, 'Year (YYYY; 1996 or later)');
23    }
24    protected SolvResultParser $solvResultParser;
25
26    public function __construct() {
27        parent::__construct();
28        $this->solvResultParser = new SolvResultParser();
29    }
30
31    public function setSolvResultParser(SolvResultParser $solvResultParser): void {
32        $this->solvResultParser = $solvResultParser;
33    }
34
35    protected function handle(InputInterface $input, OutputInterface $output): int {
36        $year = $input->getArgument('year');
37        if (!preg_match('/^[0-9]{4}$/', $year) || intval($year) < 1996) {
38            $this->logAndOutput("Invalid year: {$year}. Must be in format YYYY and 1996 or later.", level: 'notice');
39            return Command::INVALID;
40        }
41        $year = intval($year);
42        $this->syncSolvResultsForYear($year);
43        return Command::SUCCESS;
44    }
45
46    public function syncSolvResultsForYear(int $year): void {
47        $this->logAndOutput("Syncing SOLV results for {$year}...");
48        $solv_event_repo = $this->entityManager()->getRepository(SolvEvent::class);
49        $json = $this->solvFetcher()->fetchYearlyResultsJson($year);
50        $this->generalUtils()->checkNotNull($json, "No yearly results JSON");
51
52        $json_excerpt = mb_substr($json, 0, 255);
53        $json_length = mb_strlen($json);
54        $this->logAndOutput("Successfully read JSON: {$json_excerpt}... ({$json_length}).");
55
56        $result_id_by_uid = $this->solvResultParser->parse_solv_yearly_results_json($json);
57        $num_results = count($result_id_by_uid);
58        $this->logAndOutput("Parsed JSON: {$num_results} results.");
59
60        $existing_solv_events = $solv_event_repo->getSolvEventsForYear($year);
61        $num_events = count($existing_solv_events);
62        $this->logAndOutput("SOLV events from DB: {$num_events} results.");
63
64        $known_result_index = [];
65        foreach ($existing_solv_events as $existing_solv_event) {
66            $solv_uid = $existing_solv_event->getSolvUid();
67            $rank_link = $existing_solv_event->getRankLink();
68            $known_result_index[$solv_uid] = ($rank_link !== null);
69        }
70
71        $this->importSolvResultsForYear($result_id_by_uid, $known_result_index);
72    }
73
74    /**
75     * @param array<int, array{result_list_id: string}> $result_id_by_uid
76     * @param array<int, bool>                          $known_result_index
77     */
78    private function importSolvResultsForYear(
79        array $result_id_by_uid,
80        array $known_result_index
81    ): void {
82        $solv_event_repo = $this->entityManager()->getRepository(SolvEvent::class);
83        foreach ($result_id_by_uid as $solv_uid => $event_result) {
84            if (!($known_result_index[$solv_uid] ?? null) && $event_result['result_list_id']) {
85                $this->logAndOutput("Event with SOLV ID {$solv_uid} has new results.");
86                $html = $this->solvFetcher()->fetchEventResultsHtml($event_result['result_list_id']);
87                $this->generalUtils()->checkNotNull($html, "No event result HTML");
88                $results = $this->solvResultParser->parse_solv_event_result_html($html, $solv_uid);
89                $results_count = count($results);
90                $this->logAndOutput("Number of results fetched & parsed: {$results_count}");
91                foreach ($results as $result) {
92                    try {
93                        $this->entityManager()->persist($result);
94                        $this->entityManager()->flush();
95                    } catch (\Exception $e) {
96                        $this->logAndOutput("Result could not be inserted: {$result->getName()} - {$e->getMessage()}", level: 'warning');
97                    }
98                }
99                $solv_event_repo->setResultForSolvEvent($solv_uid, $event_result['result_list_id']);
100            }
101        }
102    }
103}