From 9b3787b7fea2c0e8a4cf57883d0da6aa484aead3 Mon Sep 17 00:00:00 2001 From: Hoja Mustaffa Abdul Latheef Date: Wed, 11 Dec 2024 08:48:05 +0100 Subject: [PATCH] [TASK] Refactoring all commands --- .../Command/DeutscherWetterdienstCommand.php | 300 +----------------- .../DeutscherWetterdienstWarnCellCommand.php | 4 +- Classes/Command/OpenWeatherMapCommand.php | 41 +-- .../Repository/WeatherAlertRepository.php | 80 ++++- .../WeatherAlertRepositoryInterface.php | 17 + .../{Service => Fetcher}/WarnCellFetcher.php | 2 +- .../WarnCellFetcherInterface.php | 2 +- Classes/Fetcher/WeatherAlertFetcher.php | 43 +++ .../Fetcher/WeatherAlertFetcherInterface.php | 19 ++ .../{Service => Parser}/WarnCellParser.php | 2 +- .../WarnCellParserInterface.php | 2 +- Classes/Parser/WeatherAlertParser.php | 29 ++ .../Parser/WeatherAlertParserInterface.php | 17 + .../DeutscherWetterdienstAlertService.php | 188 +++++++++++ ... DeutscherWetterdienstWarncellService.php} | 4 +- Classes/Service/OpenWeatherService.php | 44 +++ Classes/Service/WeatherDataHandlerService.php | 3 + ...x_weather2_domain_model_currentweather.php | 5 +- ext_tables.sql | 51 +-- 19 files changed, 502 insertions(+), 351 deletions(-) create mode 100644 Classes/Domain/Repository/WeatherAlertRepositoryInterface.php rename Classes/{Service => Fetcher}/WarnCellFetcher.php (96%) rename Classes/{Service => Fetcher}/WarnCellFetcherInterface.php (90%) create mode 100644 Classes/Fetcher/WeatherAlertFetcher.php create mode 100644 Classes/Fetcher/WeatherAlertFetcherInterface.php rename Classes/{Service => Parser}/WarnCellParser.php (96%) rename Classes/{Service => Parser}/WarnCellParserInterface.php (89%) create mode 100644 Classes/Parser/WeatherAlertParser.php create mode 100644 Classes/Parser/WeatherAlertParserInterface.php create mode 100644 Classes/Service/DeutscherWetterdienstAlertService.php rename Classes/Service/{DeutscherWetterdienstService.php => DeutscherWetterdienstWarncellService.php} (87%) diff --git a/Classes/Command/DeutscherWetterdienstCommand.php b/Classes/Command/DeutscherWetterdienstCommand.php index 43f3c78..6b29ba6 100644 --- a/Classes/Command/DeutscherWetterdienstCommand.php +++ b/Classes/Command/DeutscherWetterdienstCommand.php @@ -11,53 +11,16 @@ namespace JWeiland\Weather2\Command; -use Doctrine\DBAL\Exception; -use JWeiland\Weather2\Domain\Model\DwdWarnCell; -use JWeiland\Weather2\Domain\Repository\DwdWarnCellRepository; -use JWeiland\Weather2\Utility\WeatherUtility; -use Psr\Http\Message\ResponseInterface; -use Psr\Log\LoggerInterface; -use Psr\Log\LogLevel; +use JWeiland\Weather2\Service\DeutscherWetterdienstAlertService; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use TYPO3\CMS\Core\Database\ConnectionPool; -use TYPO3\CMS\Core\Http\RequestFactory; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager; -use TYPO3\CMS\Extbase\Service\CacheService; final class DeutscherWetterdienstCommand extends Command { - public const API_URL = 'https://www.dwd.de/DWD/warnungen/warnapp/json/warnings.json'; - protected string $dbExtTable = 'tx_weather2_domain_model_weatheralert'; - - /** - * JSON response from dwd api - * - * @var array - */ - protected array $decodedResponse = []; - - /** - * @var array - */ - protected array $keepRecords = []; - - /** - * @var array - */ - protected array $warnCellRecords = []; - public function __construct( - protected readonly PersistenceManager $persistenceManager, - protected readonly DwdWarnCellRepository $dwdWarnCellRepository, - protected readonly RequestFactory $requestFactory, - protected readonly LoggerInterface $logger, - protected readonly ConnectionPool $connectionPool, - protected readonly CacheService $cacheService, + private readonly DeutscherWetterdienstAlertService $alertService, ) { parent::__construct(); } @@ -69,16 +32,16 @@ protected function configure(): void ->setHelp('Calls the Deutscher Wetterdienst api and saves response in weather2 format into database') ->addArgument( 'selectedWarnCells', - InputArgument::OPTIONAL, + InputArgument::REQUIRED, 'Fetch alerts for selected cities (e.g. Pforzheim)', ) ->addArgument( 'recordStoragePage', - InputArgument::OPTIONAL, + InputArgument::REQUIRED, 'Record storage page (optional)', ) ->addArgument( - 'clearCache', + 'pageIdsToClear', InputArgument::OPTIONAL, 'Clear cache for pages (comma separated list with IDs)', ); @@ -88,259 +51,14 @@ public function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('Starting to fetch warn cell data...'); try { - $response = $this->requestFactory->request(self::API_URL); - if (!$this->checkResponse($response)) { - return Command::FAILURE; - } - - $this->decodedResponse = $this->decodeResponse($response); - - $this->handleResponse($input, $output); + $this->alertService->fetchAndStoreAlerts($input, $output); + $output->writeln('Warn alert data has been successfully updated.'); - $output->writeln('Warn cell data has been successfully updated.'); return Command::SUCCESS; } catch (\Throwable $exception) { - $this->logger->error($exception->getMessage(), ['exception' => $exception]); - $output->writeln('Failed to process weather alerts: ' . $exception->getMessage() . ''); - return Command::FAILURE; - } + $output->writeln(sprintf('Failed to process weather alerts: %s', $exception->getMessage())); - } - - /** - * Decodes the response string - * You cannot use json_decode for that only, because dwd adds JavaScript code into - * the json file... - * - * @return array - * @throws \UnexpectedValueException - */ - protected function decodeResponse(ResponseInterface $response): array - { - $pattern = '/^warnWetter\.loadWarnings\(|\);$/'; - $decodedResponse = json_decode(preg_replace($pattern, '', (string)$response->getBody()), true); - if ($decodedResponse === null) { - throw new \UnexpectedValueException( - 'Response can not be decoded because it is an invalid string', - 1485944083, - ); - } - - return $decodedResponse; - } - - /** - * Checks the responseClass for alerts in selected regions - */ - protected function handleResponse(InputInterface $input, OutputInterface $output): void - { - if (array_key_exists('warnings', $this->decodedResponse)) { - $this->processDwdItems($this->decodedResponse['warnings'], false, $input, $output); - } - if (array_key_exists('vorabInformation', $this->decodedResponse)) { - $this->processDwdItems($this->decodedResponse['vorabInformation'], true, $input, $output); - } - $this->removeOldAlertsFromDb(); - $this->persistenceManager->persistAll(); - - if (!empty($input->getArgument('clearCache'))) { - $this->cacheService->clearPageCache(GeneralUtility::intExplode(',', $input->getArgument('clearCache'))); - } - } - - /** - * @param array $category - */ - protected function processDwdItems(array $category, bool $isPreliminaryInformation, InputInterface $input, OutputInterface $output): void - { - $selectedWarnCells = GeneralUtility::trimExplode(',', $input->getArgument('selectedWarnCells')); - $recordStoragePid = (int)$input->getArgument('recordStoragePage'); - foreach ($selectedWarnCells as $warnCellId) { - $dwdWarnCells = $this->getDwdRecordsFindByName( - htmlspecialchars(strip_tags($warnCellId)), - ); - $progressBar = new ProgressBar($output, count($dwdWarnCells)); - $progressBar->start(); - foreach ($dwdWarnCells as $dwdWarnCell) { - $suggestion = $dwdWarnCell['warn_cell_id']; - if (array_key_exists($suggestion, $category) && is_array($category[$suggestion])) { - foreach ($category[$suggestion] as $alert) { - if ($alertUid = $this->getUidOfAlert($alert, $recordStoragePid)) { - // alert does already exist as record - $this->keepRecords[] = $alertUid; - } else { - // create a new alert record - $row = $this->getWeatherAlertInstanceForAlert( - $alert, - $dwdWarnCell['uid'], - $isPreliminaryInformation, - $recordStoragePid, - ); - $this->insertRecord($row); - $progressBar->advance(); - } - } - } - } - $progressBar->finish(); - $output->writeln(''); - } - } - - /** - * @param array $row - */ - private function insertRecord(array $row): void - { - $queryBuilder = $this->connectionPool->getQueryBuilderForTable('tx_weather2_domain_model_weatheralert'); - $affectedRows = $queryBuilder - ->insert('tx_weather2_domain_model_weatheralert') - ->values($row) - ->executeStatement(); - $this->keepRecords[] = $queryBuilder->getConnection()->lastInsertId(); - } - - /** - * @param array $alert - */ - protected function getComparisonHashForAlert(array $alert): string - { - return md5(serialize($alert)); - } - - /** - * Either returns the uid of a record that equals $alert - * OR returns zero if there is no record for that $alert - * - * @param array $alert - * @throws Exception - */ - protected function getUidOfAlert(array $alert, int $recordStoragePid): int - { - $connection = $this->connectionPool->getConnectionForTable($this->dbExtTable); - $identicalAlert = $connection - ->select( - ['uid'], - $this->dbExtTable, - [ - 'comparison_hash' => $this->getComparisonHashForAlert($alert), - 'pid' => $recordStoragePid, - ], - ) - ->fetchAssociative(); - - return $identicalAlert['uid'] ?? 0; - } - - protected function checkResponse(ResponseInterface $response): bool - { - if ($response->getStatusCode() !== 200 || (string)$response->getBody() === '') { - $this->logger->log( - LogLevel::ERROR, - WeatherUtility::translate('message.api_response_null', 'deutscherwetterdienst'), - ); - return false; - } - return true; - } - - /** - * Returns filled WeatherAlert instance - * - * @param array $alert - * @return array - */ - protected function getWeatherAlertInstanceForAlert( - array $alert, - int $warnCellId, - bool $isPreliminaryInformation, - int $recordStoragePid, - ): array { - $weatherAlert['pid'] = $recordStoragePid; - $weatherAlert['dwd_warn_cell'] = $warnCellId; - $weatherAlert['comparison_hash'] = $alert; - $weatherAlert['preliminary_information'] = (int)$isPreliminaryInformation; - - if (isset($alert['level'])) { - $weatherAlert['level'] = $alert['level']; - } - if (isset($alert['type'])) { - $weatherAlert['type'] = $alert['type']; - } - if (isset($alert['headline'])) { - $weatherAlert['title'] = $alert['headline']; - } - if (isset($alert['description'])) { - $weatherAlert['description'] = $alert['description']; - } - if (isset($alert['instruction'])) { - $weatherAlert['instruction'] = $alert['instruction']; - } - if (isset($alert['start'])) { - $startTime = new \DateTime(); - $startTime->setTimestamp((int)substr((string)$alert['start'], 0, -3)); - $weatherAlert['start_date'] = (int)$startTime->getTimestamp(); - } - if (isset($alert['end'])) { - $endTime = new \DateTime(); - $endTime->setTimestamp((int)substr((string)$alert['end'], 0, -3)); - $weatherAlert['end_date'] = (int)$endTime->getTimestamp(); - } - - return $weatherAlert; - } - - protected function getDwdWarnCell(string $warnCellId): DwdWarnCell - { - if (!array_key_exists($warnCellId, $this->warnCellRecords)) { - $this->warnCellRecords[$warnCellId] = $this->dwdWarnCellRepository - ->findOneByWarnCellId($warnCellId); - } - return $this->warnCellRecords[$warnCellId]; - } - - protected function removeOldAlertsFromDb(): void - { - $queryBuilder = $this->connectionPool->getQueryBuilderForTable($this->dbExtTable); - $queryBuilder->delete($this->dbExtTable); - - if ($this->keepRecords) { - $queryBuilder->where( - $queryBuilder - ->expr() - ->notIn('uid', $this->keepRecords), - ); - } - - $queryBuilder->executeStatement(); - } - - /** - * @return array - * @throws Exception - */ - protected function getDwdRecordsFindByName(string $name): array - { - $table = 'tx_weather2_domain_model_dwdwarncell'; - $connection = $this->connectionPool->getConnectionForTable($table); - $queryBuilder = $connection->createQueryBuilder(); - - try { - // Build the query - $queryBuilder->select('*') - ->from($table) // Replace 'your_table_name' with your actual table name - ->where( - $queryBuilder->expr()->or( - $queryBuilder->expr()->eq('name', $queryBuilder->createNamedParameter(trim($name))), - $queryBuilder->expr()->eq('warn_cell_id', $queryBuilder->createNamedParameter($name)), - ), - ) - ->orderBy('uid', 'ASC'); - - return $queryBuilder->executeQuery()->fetchAllAssociative(); - } catch (\Exception $e) { - // Handle exception if needed - return []; + return Command::FAILURE; } } } diff --git a/Classes/Command/DeutscherWetterdienstWarnCellCommand.php b/Classes/Command/DeutscherWetterdienstWarnCellCommand.php index 2ae5475..89dc3b3 100644 --- a/Classes/Command/DeutscherWetterdienstWarnCellCommand.php +++ b/Classes/Command/DeutscherWetterdienstWarnCellCommand.php @@ -11,7 +11,7 @@ namespace JWeiland\Weather2\Command; -use JWeiland\Weather2\Service\DeutscherWetterdienstService; +use JWeiland\Weather2\Service\DeutscherWetterdienstWarncellService; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -19,7 +19,7 @@ final class DeutscherWetterdienstWarnCellCommand extends Command { public function __construct( - private readonly DeutscherWetterdienstService $warnCellService, + private readonly DeutscherWetterdienstWarncellService $warnCellService, ) { parent::__construct(); } diff --git a/Classes/Command/OpenWeatherMapCommand.php b/Classes/Command/OpenWeatherMapCommand.php index 6da8b9a..6752d0e 100644 --- a/Classes/Command/OpenWeatherMapCommand.php +++ b/Classes/Command/OpenWeatherMapCommand.php @@ -50,33 +50,10 @@ public function execute(InputInterface $input, OutputInterface $output): int $output->writeln('Starting OpenWeatherMap data fetch...'); try { - // Gather inputs - $name = $input->getArgument('name'); - $city = $input->getArgument('city'); - $country = $input->getArgument('country'); - $apiKey = $input->getArgument('apiKey'); - $recordStoragePage = (int)$input->getArgument('recordStoragePage'); - $pageIdsToClear = $input->getArgument('pageIdsToClear') ?? ''; - - // Delegate logic to services - $this->weatherDataHandlerService->removeOldRecords($name, $recordStoragePage); - $response = $this->weatherService->fetchWeatherData($city, $country, $apiKey); - - // Decode the JSON response into an stdClass - $responseClass = json_decode((string)$response->getBody(), false); - if (!$responseClass) { - throw new \RuntimeException('Failed to decode API response as JSON.'); - } - - // Save the weather data to the database - $this->weatherDataHandlerService->saveWeatherData($responseClass, $recordStoragePage, $name); - - // Clear cache if IDs are provided - if (!empty($pageIdsToClear)) { - $this->weatherDataHandlerService->clearCache($pageIdsToClear); - } - + $arguments = $this->getArgumentsFromInput($input); + $this->weatherService->processWeatherData($arguments, $output); $output->writeln('Weather data successfully updated!'); + return Command::SUCCESS; } catch (\Throwable $e) { $this->logger->error('Error fetching weather data: ' . $e->getMessage()); @@ -84,4 +61,16 @@ public function execute(InputInterface $input, OutputInterface $output): int return Command::FAILURE; } } + + private function getArgumentsFromInput(InputInterface $input): array + { + return [ + 'name' => $input->getArgument('name'), + 'city' => $input->getArgument('city'), + 'country' => $input->getArgument('country'), + 'apiKey' => $input->getArgument('apiKey'), + 'recordStoragePage' => (int)$input->getArgument('recordStoragePage'), + 'pageIdsToClear' => $input->getArgument('pageIdsToClear') ?? '', + ]; + } } diff --git a/Classes/Domain/Repository/WeatherAlertRepository.php b/Classes/Domain/Repository/WeatherAlertRepository.php index e1e9018..cf8016b 100644 --- a/Classes/Domain/Repository/WeatherAlertRepository.php +++ b/Classes/Domain/Repository/WeatherAlertRepository.php @@ -13,6 +13,8 @@ use JWeiland\Weather2\Domain\Model\WeatherAlert; use TYPO3\CMS\Core\Context\Context; +use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException; +use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException; use TYPO3\CMS\Extbase\Persistence\QueryResultInterface; @@ -23,12 +25,20 @@ * * @extends Repository */ -class WeatherAlertRepository extends Repository +class WeatherAlertRepository extends Repository implements WeatherAlertRepositoryInterface { + private const WARN_CELL_TABLE_NAME = 'tx_weather2_domain_model_dwdwarncell'; + private const ALERT_TABLE_NAME = 'tx_weather2_domain_model_weatheralert'; + + public function __construct( + private readonly ConnectionPool $connectionPool, + ) {} + /** * Returns current alerts filtered by user selection * * @return QueryResultInterface The result containing WeatherAlert objects + * @throws AspectNotFoundException */ public function findByUserSelection( string $warnCellIds, @@ -77,4 +87,72 @@ public function findByUserSelection( return $query->execute(); } + + public function getDwdAlertsFindByName(string $alertName): array + { + $connection = $this->connectionPool->getConnectionForTable(self::WARN_CELL_TABLE_NAME); + $queryBuilder = $connection->createQueryBuilder(); + + try { + // Build the query + $queryBuilder->select('*') + ->from(self::WARN_CELL_TABLE_NAME) + ->where( + $queryBuilder->expr()->or( + $queryBuilder->expr()->eq('name', $queryBuilder->createNamedParameter(trim($alertName))), + $queryBuilder->expr()->eq('warn_cell_id', $queryBuilder->createNamedParameter($alertName)), + ), + ) + ->orderBy('uid', 'ASC'); + + return $queryBuilder->executeQuery()->fetchAllAssociative(); + } catch (\Exception $e) { + // Handle exception if needed + return []; + } + } + + public function getUidOfAlert(array $alert, int $recordStoragePid, string $comparisonHash): int + { + $connection = $this->connectionPool->getConnectionForTable(self::ALERT_TABLE_NAME); + $identicalAlert = $connection + ->select( + ['uid'], + self::ALERT_TABLE_NAME, + [ + 'comparison_hash' => $comparisonHash, + 'pid' => $recordStoragePid, + ], + ) + ->fetchAssociative(); + + return $identicalAlert['uid'] ?? 0; + } + + public function insertAlertRecord(array $row): int + { + $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::ALERT_TABLE_NAME); + $queryBuilder + ->insert(self::ALERT_TABLE_NAME) + ->values($row) + ->executeStatement(); + + return (int)$queryBuilder->getConnection()->lastInsertId(); + } + + public function removeOldAlertsFromDb(array $keepRecords): int + { + $queryBuilder = $this->connectionPool->getQueryBuilderForTable(self::ALERT_TABLE_NAME); + $queryBuilder->delete(self::ALERT_TABLE_NAME); + + if ($keepRecords !== []) { + $queryBuilder->where( + $queryBuilder + ->expr() + ->notIn('uid', $keepRecords), + ); + } + + return $queryBuilder->executeStatement(); + } } diff --git a/Classes/Domain/Repository/WeatherAlertRepositoryInterface.php b/Classes/Domain/Repository/WeatherAlertRepositoryInterface.php new file mode 100644 index 0000000..d2488a9 --- /dev/null +++ b/Classes/Domain/Repository/WeatherAlertRepositoryInterface.php @@ -0,0 +1,17 @@ +requestFactory->request(self::API_URL); + + if ($response->getStatusCode() !== 200 || (string)$response->getBody() === '') { + $this->logger->log( + LogLevel::ERROR, + WeatherUtility::translate('message.api_response_null', 'deutscherwetterdienst'), + ); + throw new \RuntimeException('Invalid response from API.'); + } + + return $response; + } +} diff --git a/Classes/Fetcher/WeatherAlertFetcherInterface.php b/Classes/Fetcher/WeatherAlertFetcherInterface.php new file mode 100644 index 0000000..7187cfc --- /dev/null +++ b/Classes/Fetcher/WeatherAlertFetcherInterface.php @@ -0,0 +1,19 @@ +fetcher->fetchData(); + $rows = $this->parser->parse((string)$response->getBody()); + $this->handleResponse($input, $output, $rows); + } + + protected function handleResponse(InputInterface $input, OutputInterface $output, array $rows): void + { + if (array_key_exists('warnings', $rows)) { + $this->processAlertData($rows['warnings'], false, $input, $output); + } + if (array_key_exists('vorabInformation', $rows)) { + $this->processAlertData($rows['vorabInformation'], true, $input, $output); + } + + $this->cleanupOldAlerts($output); + $this->clearCacheIfNeeded($input); + } + + protected function cleanupOldAlerts(OutputInterface $output): void + { + if ($this->keepRecords !== []) { + $this->repository->removeOldAlertsFromDb($this->keepRecords); + $output->writeln('Deleting old alerts'); + } + } + + protected function clearCacheIfNeeded(InputInterface $input): void + { + $pageIdsToClear = $input->getArgument('pageIdsToClear'); + if ($pageIdsToClear !== null) { + $this->cacheService->clearPageCache(GeneralUtility::intExplode(',', $pageIdsToClear)); + } + } + + protected function processAlertData(array $data, bool $isPreliminaryInformation, InputInterface $input, OutputInterface $output): void + { + $selectedWarnCells = GeneralUtility::trimExplode(',', $input->getArgument('selectedWarnCells')); + $recordStoragePid = (int)$input->getArgument('recordStoragePage'); + $progressBar = new ProgressBar($output, count($selectedWarnCells)); + $progressBar->start(); + + foreach ($selectedWarnCells as $warnCellId) { + $this->processWarnCellAlerts($warnCellId, $data, $isPreliminaryInformation, $recordStoragePid, $progressBar, $output); + } + $progressBar->finish(); + $output->writeln(''); + } + + protected function processWarnCellAlerts( + string $warnCellId, + array $data, + bool $isPreliminaryInformation, + int $recordStoragePid, + ProgressBar $progressBar, + OutputInterface $output, + ) { + $dwdWarnCells = $this->repository->getDwdAlertsFindByName( + htmlspecialchars(strip_tags($warnCellId)), + ); + foreach ($dwdWarnCells as $dwdWarnCell) { + $suggestion = $dwdWarnCell['warn_cell_id']; + if (array_key_exists($suggestion, $data) && is_array($data[$suggestion])) { + foreach ($data[$suggestion] as $alert) { + $this->processAlert($alert, $dwdWarnCell['uid'], $isPreliminaryInformation, $recordStoragePid, $progressBar, $output); + } + } + } + } + + protected function processAlert( + array $alert, + int $warnCellId, + bool $isPreliminaryInformation, + int $recordStoragePid, + ProgressBar $progressBar, + OutputInterface $output, + ) { + $comparisonHash = $this->getComparisonHashForAlert($alert); + if ($alertUid = $this->repository->getUidOfAlert($alert, $recordStoragePid, $comparisonHash)) { + $this->keepRecords[] = $alertUid; + $output->writeln(''); + $output->writeln(sprintf('Alert with hash %s already exists.', $comparisonHash)); + } else { + $this->insertNewAlert($alert, $warnCellId, $isPreliminaryInformation, $recordStoragePid, $output); + } + $progressBar->advance(); + } + + protected function insertNewAlert( + array $alert, + int $warnCellId, + bool $isPreliminaryInformation, + int $recordStoragePid, + OutputInterface $output, + ): void { + $row = $this->getWeatherAlertInstanceForAlert($alert, $warnCellId, $isPreliminaryInformation, $recordStoragePid); + $alertUid = $this->repository->insertAlertRecord($row); + $this->keepRecords[] = $alertUid; + $output->writeln(''); + $output->writeln(sprintf('Inserted new alert with UID %u.', $alertUid)); + } + + /** + * Returns filled WeatherAlert instance + * + * @param array $alert + * @return array + */ + protected function getWeatherAlertInstanceForAlert( + array $alert, + int $warnCellId, + bool $isPreliminaryInformation, + int $recordStoragePid, + ): array { + $weatherAlert['pid'] = $recordStoragePid; + $weatherAlert['dwd_warn_cell'] = $warnCellId; + $weatherAlert['comparison_hash'] = $this->getComparisonHashForAlert($alert); + $weatherAlert['preliminary_information'] = (int)$isPreliminaryInformation; + + if (isset($alert['level'])) { + $weatherAlert['level'] = $alert['level']; + } + if (isset($alert['type'])) { + $weatherAlert['type'] = $alert['type']; + } + if (isset($alert['headline'])) { + $weatherAlert['title'] = $alert['headline']; + } + if (isset($alert['description'])) { + $weatherAlert['description'] = $alert['description']; + } + if (isset($alert['instruction'])) { + $weatherAlert['instruction'] = $alert['instruction']; + } + if (isset($alert['start'])) { + $startTime = new \DateTime(); + $startTime->setTimestamp((int)substr((string)$alert['start'], 0, -3)); + $weatherAlert['start_date'] = (int)$startTime->getTimestamp(); + } + if (isset($alert['end'])) { + $endTime = new \DateTime(); + $endTime->setTimestamp((int)substr((string)$alert['end'], 0, -3)); + $weatherAlert['end_date'] = (int)$endTime->getTimestamp(); + } + + return $weatherAlert; + } + + protected function getComparisonHashForAlert(array $alert): string + { + return md5(serialize($alert)); + } +} diff --git a/Classes/Service/DeutscherWetterdienstService.php b/Classes/Service/DeutscherWetterdienstWarncellService.php similarity index 87% rename from Classes/Service/DeutscherWetterdienstService.php rename to Classes/Service/DeutscherWetterdienstWarncellService.php index 3f859a6..65ec875 100644 --- a/Classes/Service/DeutscherWetterdienstService.php +++ b/Classes/Service/DeutscherWetterdienstWarncellService.php @@ -12,10 +12,12 @@ namespace JWeiland\Weather2\Service; use JWeiland\Weather2\Domain\Repository\WarnCellRepositoryInterface; +use JWeiland\Weather2\Fetcher\WarnCellFetcherInterface; +use JWeiland\Weather2\Parser\WarnCellParserInterface; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Output\OutputInterface; -class DeutscherWetterdienstService +class DeutscherWetterdienstWarncellService { public function __construct( private readonly WarnCellFetcherInterface $fetcher, diff --git a/Classes/Service/OpenWeatherService.php b/Classes/Service/OpenWeatherService.php index 582f14d..49c3412 100644 --- a/Classes/Service/OpenWeatherService.php +++ b/Classes/Service/OpenWeatherService.php @@ -13,6 +13,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Log\LoggerInterface; +use Symfony\Component\Console\Output\OutputInterface; use TYPO3\CMS\Core\Http\RequestFactory; class OpenWeatherService implements WeatherServiceInterface @@ -22,8 +23,32 @@ class OpenWeatherService implements WeatherServiceInterface public function __construct( private readonly RequestFactory $requestFactory, private readonly LoggerInterface $logger, + private readonly WeatherDataHandlerService $weatherDataHandlerService, ) {} + public function processWeatherData(array $arguments, OutputInterface $output): void + { + $this->removeOldRecords($arguments['name'], $arguments['recordStoragePage'], $output); + + // Fetch the weather data + $response = $this->fetchWeatherData($arguments['city'], $arguments['country'], $arguments['apiKey']); + + // Save the weather data to the database + $this->saveWeatherData($response, $arguments['recordStoragePage'], $arguments['name'], $output); + + // Clear cache if needed + if ($arguments['pageIdsToClear']) { + $this->clearCache($arguments['pageIdsToClear'], $output); + } + } + + private function removeOldRecords(string $name, int $recordStoragePage, OutputInterface $output): void + { + // Perform the removal of old records + $this->weatherDataHandlerService->removeOldRecords($name, $recordStoragePage); + $output->writeln('Old records removed successfully.'); + } + public function fetchWeatherData(string $city, string $country, string $apiKey): ResponseInterface { $url = sprintf(self::API_URL, urlencode($city), urlencode($country), 'metric', $apiKey); @@ -32,4 +57,23 @@ public function fetchWeatherData(string $city, string $country, string $apiKey): return $this->requestFactory->request($url); } + + private function saveWeatherData($response, int $recordStoragePage, string $name, OutputInterface $output): void + { + // Perform saving logic + $responseClass = json_decode((string)$response->getBody(), false); + if (!$responseClass) { + throw new \RuntimeException('Failed to decode API response as JSON.'); + } + + $this->weatherDataHandlerService->saveWeatherData($responseClass, $recordStoragePage, $name); + $output->writeln('Weather data saved successfully!'); + } + + private function clearCache(string $pageIdsToClear, OutputInterface $output): void + { + // Perform cache clearing logic + $this->weatherDataHandlerService->clearCache($pageIdsToClear); + $output->writeln('Cache cleared for pages: ' . $pageIdsToClear . ''); + } } diff --git a/Classes/Service/WeatherDataHandlerService.php b/Classes/Service/WeatherDataHandlerService.php index b010c00..5d53ed0 100644 --- a/Classes/Service/WeatherDataHandlerService.php +++ b/Classes/Service/WeatherDataHandlerService.php @@ -86,6 +86,9 @@ public function saveWeatherData( $weatherObjectArray['condition_code'] = $responseClass->weather[0]->id; } + // Serialized Object + $weatherObjectArray['serialized_array'] = serialize($responseClass->weather[0]); + try { $this->connectionPool ->getQueryBuilderForTable(self::CURRENT_WEATHER_TABLE_NAME) diff --git a/Configuration/TCA/tx_weather2_domain_model_currentweather.php b/Configuration/TCA/tx_weather2_domain_model_currentweather.php index a935cca..0972e36 100644 --- a/Configuration/TCA/tx_weather2_domain_model_currentweather.php +++ b/Configuration/TCA/tx_weather2_domain_model_currentweather.php @@ -178,9 +178,10 @@ 'exclude' => 1, 'label' => 'LLL:EXT:weather2/Resources/Private/Language/locallang_db.xlf:tx_weather2_domain_model_currentweather.serialized_array', 'config' => [ - 'type' => 'input', - 'size' => 30, + 'type' => 'text', 'eval' => 'trim', + 'default' => '', + 'readOnly' => true, ], ], 'icon' => [ diff --git a/ext_tables.sql b/ext_tables.sql index 8f035b5..30c538b 100644 --- a/ext_tables.sql +++ b/ext_tables.sql @@ -1,47 +1,50 @@ # # Table structure for table 'tx_weather2_domain_model_currentweather' # -CREATE TABLE tx_weather2_domain_model_currentweather ( - name varchar(255) DEFAULT '' NOT NULL, - measure_timestamp int(11) DEFAULT '0' NOT NULL, +CREATE TABLE tx_weather2_domain_model_currentweather +( + name varchar(255) DEFAULT '' NOT NULL, + measure_timestamp int(11) DEFAULT '0' NOT NULL, temperature_c double(4,2) DEFAULT '0.0' NOT NULL, pressure_hpa double(4,2) DEFAULT '0' NOT NULL, humidity_percentage int(11) unsigned DEFAULT '0' NOT NULL, min_temp_c double(4,2) DEFAULT '0.0' NOT NULL, max_temp_c double(4,2) DEFAULT '0.0' NOT NULL, wind_speed_m_p_s double(4,2) DEFAULT '0.0' NOT NULL, - wind_direction_deg int(11) DEFAULT '0' NOT NULL, - pop_percentage int(11) unsigned DEFAULT '0' NOT NULL, + wind_direction_deg int(11) DEFAULT '0' NOT NULL, + pop_percentage int(11) unsigned DEFAULT '0' NOT NULL, rain_volume double (4,2) DEFAULT '0.0' NOT NULL, snow_volume double(4,2) DEFAULT '0.0' NOT NULL, - clouds_percentage int(11) unsigned DEFAULT '0' NOT NULL, - serialized_array varchar(255) DEFAULT '' NOT NULL, - icon varchar(30) DEFAULT '' NOT NULL, - condition_code int(11) unsigned DEFAULT '0' NOT NULL + clouds_percentage int(11) unsigned DEFAULT '0' NOT NULL, + serialized_array text DEFAULT '' NOT NULL, + icon varchar(30) DEFAULT '' NOT NULL, + condition_code int(11) unsigned DEFAULT '0' NOT NULL ); # # Table structure for table 'tx_weather2_domain_model_weatheralert' # -CREATE TABLE tx_weather2_domain_model_weatheralert ( - dwd_warn_cell int(11) NOT NULL, - level int(11) DEFAULT '0' NOT NULL, - type int(11) DEFAULT '0' NOT NULL, - title varchar(255) DEFAULT '' NOT NULL, - description text DEFAULT '' NOT NULL, - instruction text DEFAULT '' NOT NULL, - start_date int(11) unsigned DEFAULT '0' NOT NULL, - end_date int(11) unsigned DEFAULT '0' NOT NULL, - comparison_hash varchar(32) DEFAULT '' NOT NULL, +CREATE TABLE tx_weather2_domain_model_weatheralert +( + dwd_warn_cell int(11) NOT NULL, + level int(11) DEFAULT '0' NOT NULL, + type int(11) DEFAULT '0' NOT NULL, + title varchar(255) DEFAULT '' NOT NULL, + description text DEFAULT '' NOT NULL, + instruction text DEFAULT '' NOT NULL, + start_date int(11) unsigned DEFAULT '0' NOT NULL, + end_date int(11) unsigned DEFAULT '0' NOT NULL, + comparison_hash varchar(32) DEFAULT '' NOT NULL, preliminary_information tinyint(4) unsigned DEFAULT '0' NOT NULL ); # # Table structure for table 'tx_weather2_domain_model_dwdwarncell' # -CREATE TABLE tx_weather2_domain_model_dwdwarncell ( - warn_cell_id varchar(30) DEFAULT '' NOT NULL, - name varchar(100) DEFAULT '' NOT NULL, - short_name varchar(30) DEFAULT '' NOT NULL, - sign varchar(10) DEFAULT '' NOT NULL +CREATE TABLE tx_weather2_domain_model_dwdwarncell +( + warn_cell_id varchar(30) DEFAULT '' NOT NULL, + name varchar(100) DEFAULT '' NOT NULL, + short_name varchar(30) DEFAULT '' NOT NULL, + sign varchar(10) DEFAULT '' NOT NULL );