Skip to content

Commit

Permalink
[TASK] Symfony command implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
hojalatheef committed Dec 5, 2024
1 parent 0214daf commit 270d483
Show file tree
Hide file tree
Showing 6 changed files with 775 additions and 18 deletions.
272 changes: 272 additions & 0 deletions Classes/Command/DeutscherWetterdienstCommand.php
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();
}
}
90 changes: 90 additions & 0 deletions Classes/Command/DeutscherWetterdienstWarnCellCommand.php
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;
}
}
Loading

0 comments on commit 270d483

Please sign in to comment.