Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.84% covered (warning)
87.84%
65 / 74
57.14% covered (warning)
57.14%
4 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
SyncSolvEventsCommand
87.84% covered (warning)
87.84%
65 / 74
57.14% covered (warning)
57.14%
4 / 7
21.79
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
 setSolvEventParser
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
 syncSolvEventsForYear
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 importSolvEventsForYear
88.46% covered (warning)
88.46%
46 / 52
0.00% covered (danger)
0.00%
0 / 1
13.26
1<?php
2
3namespace Olz\Command;
4
5use Olz\Command\Common\OlzCommand;
6use Olz\Entity\SolvEvent;
7use Olz\Entity\Termine\Termin;
8use Olz\Parsers\SolvEventParser;
9use Symfony\Component\Console\Attribute\AsCommand;
10use Symfony\Component\Console\Command\Command;
11use Symfony\Component\Console\Input\InputArgument;
12use Symfony\Component\Console\Input\InputInterface;
13use Symfony\Component\Console\Output\OutputInterface;
14
15#[AsCommand(name: 'olz:sync-solv-events')]
16class SyncSolvEventsCommand extends OlzCommand {
17    /** @return array<string> */
18    protected function getAllowedAppEnvs(): array {
19        return ['dev', 'test', 'staging', 'prod'];
20    }
21
22    protected function configure(): void {
23        $this->addArgument('year', InputArgument::REQUIRED, 'Year (YYYY; 1996 or later)');
24    }
25
26    protected SolvEventParser $solvEventParser;
27
28    public function __construct() {
29        parent::__construct();
30        $this->solvEventParser = new SolvEventParser();
31    }
32
33    public function setSolvEventParser(SolvEventParser $solvEventParser): void {
34        $this->solvEventParser = $solvEventParser;
35    }
36
37    protected function handle(InputInterface $input, OutputInterface $output): int {
38        $year = $input->getArgument('year');
39        if (!preg_match('/^[0-9]{4}$/', $year) || intval($year) < 1996) {
40            $this->logAndOutput("Invalid year: {$year}. Must be in format YYYY and 1996 or later.", level: 'notice');
41            return Command::INVALID;
42        }
43        $year = intval($year);
44        $this->syncSolvEventsForYear($year);
45        return Command::SUCCESS;
46    }
47
48    public function syncSolvEventsForYear(int $year): void {
49        $this->logAndOutput("Syncing SOLV events for {$year}...");
50
51        $csv = $this->solvFetcher()->fetchEventsCsvForYear($year);
52        $this->generalUtils()->checkNotNull($csv, "No events CSV for year {$year}");
53
54        $csv_excerpt = mb_substr($csv, 0, 255);
55        $csv_length = mb_strlen($csv);
56        $this->logAndOutput("Successfully read CSV: {$csv_excerpt}... ({$csv_length}).");
57
58        $solv_events = $this->solvEventParser->parse_solv_events_csv($csv);
59
60        $solv_event_count = count($solv_events);
61        $this->logAndOutput("Parsed {$solv_event_count} events out of CSV.");
62
63        $this->importSolvEventsForYear($solv_events, $year);
64    }
65
66    /** @param array<SolvEvent> $solv_events */
67    private function importSolvEventsForYear(array $solv_events, int $year): void {
68        $solv_event_repo = $this->entityManager()->getRepository(SolvEvent::class);
69        $termin_repo = $this->entityManager()->getRepository(Termin::class);
70        $existing_solv_events = $solv_event_repo->getSolvEventsForYear($year);
71        $existing_solv_events_index = [];
72        foreach ($existing_solv_events as $existing_solv_event) {
73            $solv_uid = $existing_solv_event->getSolvUid();
74            $existing_solv_events_index[$solv_uid] = $existing_solv_event;
75        }
76        $solv_uid_still_exists = [];
77        foreach ($existing_solv_events_index as $solv_uid => $existing_solv_event) {
78            $solv_uid_still_exists[$solv_uid] = false;
79        }
80        foreach ($solv_events as $solv_event) {
81            $solv_uid = $solv_event->getSolvUid();
82            $solv_uid_still_exists[$solv_uid] = true;
83            $existing_solv_event = $existing_solv_events_index[$solv_uid] ?? null;
84            $outdated = $existing_solv_event ? $solv_event->getLastModification() > $existing_solv_event->getLastModification() : false;
85            if (!$existing_solv_event) {
86                try {
87                    $this->entityManager()->persist($solv_event);
88                    $this->entityManager()->flush();
89                    $this->logAndOutput("INSERTED {$solv_uid}");
90                } catch (\Exception $e) {
91                    $this->logAndOutput("INSERT FAILED {$solv_uid}{$e}");
92                }
93            } elseif ($outdated) {
94                $existing_solv_event->setDate($solv_event->getDate());
95                $existing_solv_event->setDuration($solv_event->getDuration());
96                $existing_solv_event->setKind($solv_event->getKind());
97                $existing_solv_event->setDayNight($solv_event->getDayNight());
98                $existing_solv_event->setNational($solv_event->getNational());
99                $existing_solv_event->setRegion($solv_event->getRegion());
100                $existing_solv_event->setType($solv_event->getType());
101                $existing_solv_event->setName($solv_event->getName());
102                $existing_solv_event->setLink($solv_event->getLink());
103                $existing_solv_event->setClub($solv_event->getClub());
104                $existing_solv_event->setMap($solv_event->getMap());
105                $existing_solv_event->setLocation($solv_event->getLocation());
106                $existing_solv_event->setCoordX($solv_event->getCoordX());
107                $existing_solv_event->setCoordY($solv_event->getCoordY());
108                $existing_solv_event->setDeadline($solv_event->getDeadline());
109                $existing_solv_event->setEntryportal($solv_event->getEntryportal());
110                $existing_solv_event->setLastModification($solv_event->getLastModification());
111
112                $termine = $termin_repo->findBy(['solv_uid' => $solv_uid]);
113                foreach ($termine as $termin) {
114                    $this->termineUtils()->updateTerminFromSolvEvent($termin, $solv_event);
115                }
116
117                try {
118                    $this->entityManager()->flush();
119                    $this->logAndOutput("UPDATED {$solv_uid}");
120                } catch (\Exception $e) {
121                    $this->logAndOutput("UPDATE FAILED {$solv_uid}{$e}");
122                }
123            }
124        }
125        foreach ($solv_uid_still_exists as $solv_uid => $still_exists) {
126            if (!$still_exists) {
127                try {
128                    $solv_event_repo->deleteBySolvUid($solv_uid);
129                    $this->logAndOutput("DELETED {$solv_uid}");
130                } catch (\Exception $e) {
131                    $this->logAndOutput("DELETE FAILED {$solv_uid}{$e}");
132                }
133            }
134        }
135    }
136}