-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[TASK] Symfony command implementation
- Loading branch information
1 parent
0214daf
commit 270d483
Showing
6 changed files
with
775 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,272 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of the package jweiland/weather2. | ||
* | ||
* For the full copyright and license information, please read the | ||
* LICENSE file that was distributed with this source code. | ||
*/ | ||
|
||
namespace JWeiland\Weather2\Command; | ||
|
||
use Doctrine\DBAL\Exception; | ||
use JWeiland\Weather2\Domain\Model\DwdWarnCell; | ||
use JWeiland\Weather2\Domain\Model\WeatherAlert; | ||
use JWeiland\Weather2\Domain\Repository\DwdWarnCellRepository; | ||
use JWeiland\Weather2\Utility\WeatherUtility; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Log\LogLevel; | ||
use Symfony\Component\Console\Attribute\AsCommand; | ||
use Symfony\Component\Console\Command\Command; | ||
use Symfony\Component\Console\Input\InputInterface; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; | ||
use TYPO3\CMS\Core\Utility\GeneralUtility; | ||
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager; | ||
|
||
#[AsCommand(name: 'weather2:fetch:deutscherWetterdienstAPI')] | ||
final class DeutscherWetterdienstCommand extends WeatherAbstractCommand | ||
{ | ||
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<string, mixed> | ||
*/ | ||
protected array $decodedResponse = []; | ||
|
||
/** | ||
* Fetch only these warn cells | ||
* | ||
* @var array<string, mixed> | ||
*/ | ||
public array $selectedWarnCells = []; | ||
|
||
/** | ||
* @var array<string, mixed> | ||
*/ | ||
protected array $keepRecords = []; | ||
|
||
/** | ||
* @var array<string, mixed> | ||
*/ | ||
protected array $warnCellRecords = []; | ||
|
||
public int $recordStoragePage = 0; | ||
|
||
public string $clearCache = ''; | ||
|
||
protected PersistenceManager $persistenceManager; | ||
|
||
protected DwdWarnCellRepository $dwdWarnCellRepository; | ||
|
||
protected function configure(): void | ||
{ | ||
$this->setHelp('Calls the Deutscher Wetterdienst api and saves response in weather2 format into database'); | ||
} | ||
|
||
public function execute(InputInterface $input, OutputInterface $output): int | ||
{ | ||
$this->dwdWarnCellRepository = $this->getDwdWarnCellRepository(); | ||
$response = $this->getRequestFactory()->request(self::API_URL); | ||
if (!$this->checkResponse($response)) { | ||
return Command::FAILURE; | ||
} | ||
|
||
try { | ||
$this->decodedResponse = $this->decodeResponse($response); | ||
} catch (\Exception $e) { | ||
$this->logger->log(LogLevel::ERROR, $e->getMessage()); | ||
return Command::FAILURE; | ||
} | ||
|
||
$this->handleResponse(); | ||
|
||
return Command::SUCCESS; | ||
} | ||
|
||
/** | ||
* Decodes the response string | ||
* You cannot use json_decode for that only, because dwd adds JavaScript code into | ||
* the json file... | ||
* | ||
* @return array<string, mixed> | ||
* @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(): void | ||
{ | ||
$this->persistenceManager = $this->getPersistenceManager(); | ||
if (array_key_exists('warnings', $this->decodedResponse)) { | ||
$this->processDwdItems($this->decodedResponse['warnings'], false); | ||
} | ||
if (array_key_exists('vorabInformation', $this->decodedResponse)) { | ||
$this->processDwdItems($this->decodedResponse['vorabInformation'], true); | ||
} | ||
$this->removeOldAlertsFromDb(); | ||
$this->persistenceManager->persistAll(); | ||
|
||
if (!empty($this->clearCache)) { | ||
$cacheService = $this->getCacheService(); | ||
$cacheService->clearPageCache(GeneralUtility::intExplode(',', $this->clearCache)); | ||
} | ||
} | ||
|
||
/** | ||
* @param array<int, mixed> $category | ||
*/ | ||
protected function processDwdItems(array $category, bool $isPreliminaryInformation): void | ||
{ | ||
foreach ($this->selectedWarnCells as $warnCellId) { | ||
if (array_key_exists($warnCellId, $category) && is_array($category[$warnCellId])) { | ||
foreach ($category[$warnCellId] as $alert) { | ||
if ($alertUid = $this->getUidOfAlert($alert)) { | ||
// alert does already exist as record | ||
$this->keepRecords[] = $alertUid; | ||
} else { | ||
// create a new alert record | ||
$this->persistenceManager->add($this->getWeatherAlertInstanceForAlert($alert, $warnCellId, $isPreliminaryInformation)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @param array<string, mixed> $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<string, mixed> $alert | ||
* @throws Exception | ||
*/ | ||
protected function getUidOfAlert(array $alert): int | ||
{ | ||
$connection = $this->getConnectionPool()->getConnectionForTable($this->dbExtTable); | ||
$identicalAlert = $connection | ||
->select( | ||
['uid'], | ||
$this->dbExtTable, | ||
[ | ||
'comparison_hash' => $this->getComparisonHashForAlert($alert), | ||
'pid' => $this->recordStoragePage, | ||
], | ||
) | ||
->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; | ||
} | ||
|
||
protected function getBackendUserAuthentication(): BackendUserAuthentication | ||
{ | ||
return $GLOBALS['BE_USER']; | ||
} | ||
|
||
/** | ||
* Returns filled WeatherAlert instance | ||
* | ||
* @param array<string, mixed> $alert | ||
*/ | ||
protected function getWeatherAlertInstanceForAlert( | ||
array $alert, | ||
string $warnCellId, | ||
bool $isPreliminaryInformation, | ||
): WeatherAlert { | ||
$weatherAlert = new WeatherAlert(); | ||
$weatherAlert->setPid($this->recordStoragePage); | ||
$weatherAlert->setDwdWarnCell($this->getDwdWarnCell($warnCellId)); | ||
$weatherAlert->setComparisonHash($this->getComparisonHashForAlert($alert)); | ||
$weatherAlert->setPreliminaryInformation($isPreliminaryInformation); | ||
|
||
if (isset($alert['level'])) { | ||
$weatherAlert->setLevel($alert['level']); | ||
} | ||
if (isset($alert['type'])) { | ||
$weatherAlert->setType($alert['type']); | ||
} | ||
if (isset($alert['headline'])) { | ||
$weatherAlert->setTitle($alert['headline']); | ||
} | ||
if (isset($alert['description'])) { | ||
$weatherAlert->setDescription($alert['description']); | ||
} | ||
if (isset($alert['instruction'])) { | ||
$weatherAlert->setInstruction($alert['instruction']); | ||
} | ||
if (isset($alert['start'])) { | ||
$startTime = new \DateTime(); | ||
$startTime->setTimestamp((int)substr((string)$alert['start'], 0, -3)); | ||
$weatherAlert->setStartDate($startTime); | ||
} | ||
if (isset($alert['end'])) { | ||
$endTime = new \DateTime(); | ||
$endTime->setTimestamp((int)substr((string)$alert['end'], 0, -3)); | ||
$weatherAlert->setEndDate($endTime); | ||
} | ||
|
||
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->getConnectionPool()->getQueryBuilderForTable($this->dbExtTable); | ||
$queryBuilder->delete($this->dbExtTable); | ||
|
||
if ($this->keepRecords) { | ||
$queryBuilder->where( | ||
$queryBuilder | ||
->expr() | ||
->notIn('uid', $this->keepRecords), | ||
); | ||
} | ||
|
||
$queryBuilder->executeStatement(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
/* | ||
* This file is part of the package jweiland/weather2. | ||
* | ||
* For the full copyright and license information, please read the | ||
* LICENSE file that was distributed with this source code. | ||
*/ | ||
|
||
namespace JWeiland\Weather2\Command; | ||
|
||
use JWeiland\Weather2\Utility\WeatherUtility; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Log\LogLevel; | ||
use Symfony\Component\Console\Command\Command; | ||
use Symfony\Component\Console\Input\InputInterface; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
|
||
final class DeutscherWetterdienstWarnCellCommand extends WeatherAbstractCommand | ||
{ | ||
public const API_URL = 'https://www.dwd.de/DE/leistungen/opendata/help/warnungen/cap_warncellids_csv.csv?__blob=publicationFile&v=3'; | ||
|
||
protected function configure(): void | ||
{ | ||
$this->setHelp('Calls the Deutscher Wetterdienst api and saves warn cells into database. Required before using DeutscherWetterdienstTask!'); | ||
} | ||
public function execute(InputInterface $input, OutputInterface $output): int | ||
{ | ||
$response = $this->getRequestFactory()->request($this::API_URL); | ||
if (!$this->checkResponse($response)) { | ||
return Command::FAILURE; | ||
} | ||
$this->processResponse($response); | ||
return Command::SUCCESS; | ||
} | ||
|
||
protected function processResponse(ResponseInterface $response): void | ||
{ | ||
$connection = $this->getConnectionPool() | ||
->getConnectionForTable('tx_weather2_domain_model_dwdwarncell'); | ||
|
||
$rawRows = explode(PHP_EOL, (string)$response->getBody()); | ||
// remove header | ||
array_shift($rawRows); | ||
|
||
$data = []; | ||
$i = 0; | ||
foreach ($rawRows as $rawRow) { | ||
if ($rawRow === '') { | ||
continue; | ||
} | ||
|
||
[$warnCellId, $name, $nuts, $shortName, $sign] = str_getcsv($rawRow, ';'); | ||
// check if a record for this id already exists | ||
if ($connection->count('uid', 'tx_weather2_domain_model_dwdwarncell', ['warn_cell_id' => $warnCellId]) === 0) { | ||
$data['tx_weather2_domain_model_dwdwarncell']['NEW' . $i++] = [ | ||
'pid' => 0, | ||
'warn_cell_id' => $warnCellId, | ||
'name' => $name, | ||
'short_name' => $shortName, | ||
'sign' => $sign, | ||
]; | ||
} | ||
} | ||
|
||
$dataHandler = $this->getDataHandler(); | ||
$dataHandler->start($data, []); | ||
$dataHandler->process_datamap(); | ||
} | ||
|
||
/** | ||
* @param ResponseInterface $response | ||
* @return bool Returns true if response is valid or false in case of an error | ||
*/ | ||
private 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; | ||
} | ||
} |
Oops, something went wrong.