Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
84.42% covered (warning)
84.42%
65 / 77
42.86% covered (danger)
42.86%
3 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
SyncSolvEventsCommand
84.42% covered (warning)
84.42%
65 / 77
42.86% covered (danger)
42.86%
3 / 7
23.83
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
84.62% covered (warning)
84.62%
11 / 13
0.00% covered (danger)
0.00%
0 / 1
2.01
 importSolvEventsForYear
86.54% covered (warning)
86.54%
45 / 52
0.00% covered (danger)
0.00%
0 / 1
13.41
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        if ($solv_events === null) {
60            $this->logAndOutput("Could not parse events out of CSV.");
61            return;
62        }
63
64        $solv_event_count = count($solv_events);
65        $this->logAndOutput("Parsed {$solv_event_count} events out of CSV.");
66
67        $this->importSolvEventsForYear($solv_events, $year);
68    }
69
70    /** @param array<SolvEvent> $solv_events */
71    private function importSolvEventsForYear(array $solv_events, int $year): void {
72        $solv_event_repo = $this->entityManager()->getRepository(SolvEvent::class);
73        $termin_repo = $this->entityManager()->getRepository(Termin::class);
74        $existing_solv_events = $solv_event_repo->getSolvEventsForYear($year);
75        $existing_solv_events_index = [];
76        foreach ($existing_solv_events as $existing_solv_event) {
77            $solv_uid = $existing_solv_event->getSolvUid();
78            $existing_solv_events_index[$solv_uid] = $existing_solv_event;
79        }
80        $solv_uid_still_exists = [];
81        foreach ($existing_solv_events_index as $solv_uid => $existing_solv_event) {
82            $solv_uid_still_exists[$solv_uid] = false;
83        }
84        foreach ($solv_events as $solv_event) {
85            $solv_uid = $solv_event->getSolvUid();
86            $solv_uid_still_exists[$solv_uid] = true;
87            $existing_solv_event = $existing_solv_events_index[$solv_uid] ?? null;
88            $outdated = $existing_solv_event ? $solv_event->getLastModification() > $existing_solv_event->getLastModification() : false;
89            if (!$existing_solv_event) {
90                try {
91                    $this->entityManager()->persist($solv_event);
92                    $this->entityManager()->flush();
93                    $this->logAndOutput("INSERTED {$solv_uid}");
94                } catch (\Exception $e) {
95                    $this->logAndOutput("INSERT FAILED {$solv_uid}{$e}");
96                }
97            } elseif ($outdated) {
98                $existing_solv_event->setDate($solv_event->getDate());
99                $existing_solv_event->setDuration($solv_event->getDuration());
100                $existing_solv_event->setKind($solv_event->getKind());
101                $existing_solv_event->setDayNight($solv_event->getDayNight());
102                $existing_solv_event->setNational($solv_event->getNational());
103                $existing_solv_event->setRegion($solv_event->getRegion());
104                $existing_solv_event->setType($solv_event->getType());
105                $existing_solv_event->setName($solv_event->getName());
106                $existing_solv_event->setLink($solv_event->getLink());
107                $existing_solv_event->setClub($solv_event->getClub());
108                $existing_solv_event->setMap($solv_event->getMap());
109                $existing_solv_event->setLocation($solv_event->getLocation());
110                $existing_solv_event->setCoordX($solv_event->getCoordX());
111                $existing_solv_event->setCoordY($solv_event->getCoordY());
112                $existing_solv_event->setDeadline($solv_event->getDeadline());
113                $existing_solv_event->setEntryportal($solv_event->getEntryportal());
114                $existing_solv_event->setLastModification($solv_event->getLastModification());
115
116                $termine = $termin_repo->findBy(['solv_uid' => $solv_uid]);
117                foreach ($termine as $termin) {
118                    $this->termineUtils()->updateTerminFromSolvEvent($termin, $solv_event);
119                }
120
121                try {
122                    $this->entityManager()->flush();
123                    $this->logAndOutput("UPDATED {$solv_uid}");
124                } catch (\Exception $e) {
125                    $this->logAndOutput("UPDATE FAILED {$solv_uid}{$e}");
126                }
127            }
128        }
129        foreach ($solv_uid_still_exists as $solv_uid => $still_exists) {
130            if (!$still_exists) {
131                try {
132                    $solv_event_repo->deleteBySolvUid($solv_uid);
133                    $this->logAndOutput("DELETED {$solv_uid}");
134                } catch (\Exception $e) {
135                    $this->logAndOutput("DELETE FAILED {$solv_uid}{$e}");
136                }
137            }
138        }
139    }
140}