diff --git a/app/Jobs/Presidential241124/Turnouts/FetchTurnoutsJob.php b/app/Jobs/Presidential241124/Turnouts/FetchTurnoutsJob.php new file mode 100644 index 0000000..838553e --- /dev/null +++ b/app/Jobs/Presidential241124/Turnouts/FetchTurnoutsJob.php @@ -0,0 +1,97 @@ +deleteWhenDestroyed(); + + $cwd = $temporaryDirectory->path(); + + $tmpDisk = Storage::build([ + 'driver' => 'local', + 'root' => $cwd, + ]); + + $tmpDisk->put('turnout.csv', $this->scheduledJob->fetchSource()->resource()); + + // Split the CSV by county + Process::path($cwd) + ->run([ + config('import.awk_path'), + '-F,', + 'FNR==1 {header = $0; next} !seen[$1]++ {print header > $1".csv"} {print > $1".csv"}', + 'turnout.csv', + ]); + + $tmpDisk->delete('turnout.csv'); + + collect($tmpDisk->allFiles()) + ->each(function (string $file) use ($tmpDisk) { + $this->scheduledJob->disk() + ->writeStream( + $this->scheduledJob->getSourcePath($file), + $tmpDisk->readStream($file) + ); + }); + + $counties = County::all(); + + $electionName = $this->scheduledJob->election->getFilamentName(); + $electionId = $this->scheduledJob->election_id; + + $time = now()->toDateTimeString(); + + $jobs = $counties + ->map(fn (County $county) => new ImportCountyTurnoutsJob($this->scheduledJob, $county)) + ->push(new ImportAbroadTurnoutsJob($this->scheduledJob)); + + $persistAndClean = fn () => Bus::chain([ + new PersistTemporaryTableData(Turnout::class, $electionId), + new DeleteTemporaryTableData(Turnout::class, $electionId), + ])->dispatch(); + + Bus::batch($jobs) + ->catch($persistAndClean) + ->then($persistAndClean) + ->name("$electionName / Prezență / $time") + ->allowFailures() + ->dispatch(); + } + + /** + * Get the tags that should be assigned to the job. + * + * @return array + */ + public function tags(): array + { + return [ + 'import', + 'turnout', + 'scheduled_job:' . $this->scheduledJob->id, + 'election:' . $this->scheduledJob->election_id, + static::name(), + ]; + } +} diff --git a/app/Jobs/Presidential241124/Turnouts/ImportAbroadTurnoutsJob.php b/app/Jobs/Presidential241124/Turnouts/ImportAbroadTurnoutsJob.php new file mode 100644 index 0000000..49f3c01 --- /dev/null +++ b/app/Jobs/Presidential241124/Turnouts/ImportAbroadTurnoutsJob.php @@ -0,0 +1,118 @@ +scheduledJob = $scheduledJob; + } + + public function handle(): void + { + $disk = $this->scheduledJob->disk(); + $path = $this->scheduledJob->getSourcePath('SR.csv'); + + if (! $disk->exists($path)) { + throw new MissingSourceFileException($path); + } + + $reader = Reader::createFromStream($disk->readStream($path)); + $reader->setHeaderOffset(0); + + $values = collect(); + + $segments = Turnout::segmentsMap(); + + foreach ($reader->getRecords() as $record) { + try { + $values->push([ + 'election_id' => $this->scheduledJob->election_id, + 'country_id' => $this->getCountryId($record['UAT']), + 'section' => $record['Nr sectie de votare'], + + 'initial_permanent' => $record['Înscriși pe liste permanente'], + 'initial_complement' => 0, + 'permanent' => $record['LP'], + 'complement' => $record['LSC'], + 'supplement' => $record['LS'], + 'mobile' => $record['UM'], + + 'area' => $record['Mediu'], + 'has_issues' => $this->determineIfHasIssues($record), + + ...$segments->map(fn (string $segment) => $record[$segment]), + ]); + } catch (CountryCodeNotFoundException $th) { + CountryCodeNotFound::dispatch($record['UAT'], $this->scheduledJob->election); + } + } + + Turnout::saveToTemporaryTable($values->all()); + } + + protected function determineIfHasIssues(array $record): bool + { + $computedTotal = collect(['LP', 'LSC', 'LS', 'UM']) + ->map(fn (string $key) => $record[$key]) + ->sum(); + + if ($computedTotal !== $record['LT']) { + return true; + } + + return false; + } + + protected function getCountryId(string $name): string + { + $country = Country::search($name)->first(); + + if (! $country) { + throw new CountryCodeNotFoundException($name); + } + + return $country->id; + } + + /** + * Get the tags that should be assigned to the job. + * + * @return array + */ + public function tags(): array + { + return [ + 'import', + 'turnout', + 'scheduled_job:' . $this->scheduledJob->id, + 'election:' . $this->scheduledJob->election_id, + 'abroad', + ]; + } +} diff --git a/app/Jobs/Presidential241124/Turnouts/ImportCountyTurnoutsJob.php b/app/Jobs/Presidential241124/Turnouts/ImportCountyTurnoutsJob.php new file mode 100644 index 0000000..1c0e979 --- /dev/null +++ b/app/Jobs/Presidential241124/Turnouts/ImportCountyTurnoutsJob.php @@ -0,0 +1,106 @@ +scheduledJob = $scheduledJob; + $this->county = $county; + } + + public function handle(): void + { + $disk = $this->scheduledJob->disk(); + $path = $this->scheduledJob->getSourcePath("{$this->county->code}.csv"); + + if (! $disk->exists($path)) { + throw new MissingSourceFileException($path); + } + + $reader = Reader::createFromStream($disk->readStream($path)); + $reader->setHeaderOffset(0); + + $values = collect(); + + $segments = Turnout::segmentsMap(); + + $records = $reader->getRecords(); + foreach ($records as $record) { + $values->push([ + 'election_id' => $this->scheduledJob->election_id, + 'county_id' => $this->county->id, + 'locality_id' => $record['Siruta'], + 'section' => $record['Nr sectie de votare'], + + 'initial_permanent' => $record['Înscriși pe liste permanente'], + 'initial_complement' => 0, + 'permanent' => $record['LP'], + 'complement' => $record['LSC'], + 'supplement' => $record['LS'], + 'mobile' => $record['UM'], + + 'area' => $record['Mediu'], + 'has_issues' => $this->determineIfHasIssues($record), + + ...$segments->map(fn (string $segment) => $record[$segment]), + ]); + } + + Turnout::saveToTemporaryTable($values->all()); + } + + protected function determineIfHasIssues(array $record): bool + { + $computedTotal = collect(['LP', 'LSC', 'LS', 'UM']) + ->map(fn (string $key) => $record[$key]) + ->sum(); + + if ($computedTotal !== $record['LT']) { + return true; + } + + return false; + } + + /** + * Get the tags that should be assigned to the job. + * + * @return array + */ + public function tags(): array + { + return [ + 'import', + 'turnout', + 'scheduled_job:' . $this->scheduledJob->id, + 'election:' . $this->scheduledJob->election_id, + 'county:' . $this->county->code, + ]; + } +}