<?php declare(strict_types=1); namespace GameDev\Platform\Command\Aggregator; use DateTimeImmutable; use GameDev\Component\Filter\FilterCondition; use GameDev\Platform\Command\Common\IOTrait; use GameDev\Platform\Filter\Catalog\AggregatorFilter; use GameDev\Platform\Filter\Statistic\AggregatorStatisticFilter; use GameDev\Platform\Filter\Statistic\AggregatorStatisticGroupBy; use GameDev\Platform\Model\Repository\Statistic\AggregatorStatisticItem; use GameDev\Platform\Model\Shared\Session\Player\PlayerMode; use GameDev\Platform\Repository\Catalog\AggregatorRepository; use GameDev\Platform\Repository\Statistic\AggregatorStatisticRepository; use GameDev\Platform\Utils\EmailSender; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Shared\DaemonCommandBundle\Command\AbstractDaemon; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; use Throwable; use Twig\Error\LoaderError; use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; use Webmozart\Assert\Assert; #[AsCommand( name: 'platform:aggregator:aggregators-without-statistic-report-job', description: 'Check aggregators without statistics', )] class AggregatorsWithoutStatisticReportJob extends AbstractDaemon { use IOTrait; public const CATALOG_AGGREGATORS_DO_NOT_EXIST = 'Aggregators do not exist'; public const STATISTIC_AGGREGATOR_ID_MUST_BE_NOT_NULL = 'Aggregator ID must not be empty'; public const MESSAGE_TEMPLATE = 'email/aggregators_without_statistic.txt.twig'; private const ARGUMENT_EMAILS = 'emails'; private array $emails = []; public function __construct( private readonly AggregatorRepository $aggregatorRepository, private readonly AggregatorStatisticRepository $aggregatorStatisticRepository, private readonly EmailSender $emailSender, private readonly LoggerInterface $logger = new NullLogger(), ) { parent::__construct(); } protected function configure(): void { parent::configure(); $this ->setDescription('Arguments aggregators without statistic report') ->addArgument( self::ARGUMENT_EMAILS, InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'Separate multiple emails with a space', ) ->addUsage('hello@world.com'); } /** * @throws Throwable */ protected function execute(InputInterface $input, OutputInterface $output): int { $this->setIO($input, $output); $this->initParameters($input); $dateStart = new DateTimeImmutable('-2 days'); $dateEnd = new DateTimeImmutable('-1 day'); try { $aggregatorsMap = $this->getAggregatorNamesById(); if (count($aggregatorsMap) === 0) { $this->printError(self::CATALOG_AGGREGATORS_DO_NOT_EXIST); return self::FAILURE; } $messages = $this->buildMessageData($aggregatorsMap, $dateStart, $dateEnd); if (count($messages) === 0) { $this->printInfo('Statistics for the aggregator for yesterday and the day before yesterday exist'); return self::SUCCESS; } $this->sendNotifications($messages, $dateEnd); $this->logInfo($messages, $output->getVerbosity()); $this->addContext(['messages' => $messages]); } catch (Throwable $exception) { $this->logError($exception); return self::FAILURE; } return self::SUCCESS; } private function initParameters(InputInterface $input): void { $this->emails = $input->getArgument(self::ARGUMENT_EMAILS); } /** * @return array<int, string> [id => name] */ private function getAggregatorNamesById(): array { $result = []; $aggregators = $this->aggregatorRepository->findAllByFilter($this->createAggregatorsFilter()); foreach ($aggregators as $aggregator) { $result[$aggregator->getId()] = $aggregator->getName(); } return $result; } private function createAggregatorsFilter(): AggregatorFilter { return AggregatorFilter::create() ->addCondition(FilterCondition::eq(AggregatorFilter::PRODUCTION, true)) ->addCondition(FilterCondition::eq(AggregatorFilter::DISABLED, false)) ->addOrdering(AggregatorFilter::ID, true); } /** * @param array<int> $aggregatorsIds * @return array<int> */ private function getActiveAggregatorsOnDate(array $aggregatorsIds, DateTimeImmutable $date): array { $statistics = $this->aggregatorStatisticRepository->getAggregatedAggregatorStatistic( $this->createAggregatorStatisticFilter($aggregatorsIds, $date), ); return array_map(function (AggregatorStatisticItem $aggregatorStatisticItem) { Assert::notNull($aggregatorStatisticItem->aggregatorId, self::STATISTIC_AGGREGATOR_ID_MUST_BE_NOT_NULL); return $aggregatorStatisticItem->aggregatorId; }, $statistics); } /** * @param array<int, string> $aggregatorsMap */ private function buildMessageData( array $aggregatorsMap, DateTimeImmutable $dateStart, DateTimeImmutable $dateEnd, ): array { $messages = []; $aggregatorsIds = array_keys($aggregatorsMap); $activeAggregatorsOnDateStart = $this->getActiveAggregatorsOnDate($aggregatorsIds, $dateStart); $activeAggregatorsOnDateEnd = $this->getActiveAggregatorsOnDate($aggregatorsIds, $dateEnd); foreach ($activeAggregatorsOnDateStart as $aggregatorId) { if ( !in_array($aggregatorId, $activeAggregatorsOnDateEnd, true) && array_key_exists($aggregatorId, $aggregatorsMap) ) { $messages[] = [ 'aggregator_id' => $aggregatorId, 'aggregator_name' => $aggregatorsMap[$aggregatorId], ]; } } return $messages; } /** * @param array<int, array{aggregator_id: int, aggregator_name: string}> $messages * @throws Throwable * @throws TransportExceptionInterface * @throws LoaderError * @throws RuntimeError * @throws SyntaxError */ private function sendNotifications(array $messages, DateTimeImmutable $dateEnd): void { $date = $dateEnd->format('Y-m-d'); $this->emailSender->send( $this->emails, sprintf('Aggregators lost traffic %s', $date), self::MESSAGE_TEMPLATE, [ 'messages' => $messages, 'date' => $date, ], ); } /** * @param array<int> $aggregatorsIds */ private function createAggregatorStatisticFilter( array $aggregatorsIds, DateTimeImmutable $date, ): AggregatorStatisticFilter { $filter = AggregatorStatisticFilter::create(); $filter->addCondition(FilterCondition::eq(AggregatorStatisticFilter::DATE, $date->format('Y-m-d'))); $filter->addCondition( FilterCondition::in(AggregatorStatisticFilter::AGGREGATOR_ID, $aggregatorsIds), ); $filter->addCondition(FilterCondition::eq(AggregatorStatisticFilter::MODE, PlayerMode::MODE_REAL)); $filter->groupBy(AggregatorStatisticGroupBy::Aggregator); $filter->addOrdering(AggregatorStatisticFilter::AGGREGATOR_ID, true); return $filter; } private function logInfo(array $messages, int $verbosity): void { if (OutputInterface::VERBOSITY_NORMAL <= $verbosity) { $messageToLog = json_encode($messages); $this->printInfo($messageToLog); $this->logger->info($messageToLog); } } private function logError(Throwable $exception): void { $message = sprintf('An error: %s', $exception->getMessage()); $this->printError($message); $this->logger->error($message, ['exception' => $exception]); } } <?php declare(strict_types=1); namespace GameDev\Platform\Command\Aggregator; use DateTimeImmutable; use GameDev\Component\Filter\FilterCondition; use GameDev\Platform\Command\Common\IOTrait; use GameDev\Platform\Filter\Catalog\AggregatorFilter; use GameDev\Platform\Filter\Statistic\AggregatorStatisticFilter; use GameDev\Platform\Filter\Statistic\AggregatorStatisticGroupBy; use GameDev\Platform\Model\Repository\Statistic\AggregatorStatisticItem; use GameDev\Platform\Model\Shared\Session\Player\PlayerMode; use GameDev\Platform\Repository\Catalog\AggregatorRepository; use GameDev\Platform\Repository\Statistic\AggregatorStatisticRepository; use GameDev\Platform\Utils\EmailSender; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Shared\DaemonCommandBundle\Command\AbstractDaemon; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Mailer\Exception\TransportExceptionInterface; use Throwable; use Twig\Error\LoaderError; use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; use Webmozart\Assert\Assert; #[AsCommand( name: 'platform:aggregator:aggregators-without-statistic-report-job', description: 'Check aggregators without statistics', )] class AggregatorsWithoutStatisticReportJob extends AbstractDaemon { use IOTrait; public const CATALOG_AGGREGATORS_DO_NOT_EXIST = 'Aggregators do not exist'; public const STATISTIC_AGGREGATOR_ID_MUST_BE_NOT_NULL = 'Aggregator ID must not be empty'; public const MESSAGE_TEMPLATE = 'email/aggregators_without_statistic.txt.twig'; private const ARGUMENT_EMAILS = 'emails'; private array $emails = []; public function __construct( private readonly AggregatorRepository $aggregatorRepository, private readonly AggregatorStatisticRepository $aggregatorStatisticRepository, private readonly EmailSender $emailSender, private readonly LoggerInterface $logger = new NullLogger(), ) { parent::__construct(); } protected function configure(): void { parent::configure(); $this ->setDescription('Arguments aggregators without statistic report') ->addArgument( self::ARGUMENT_EMAILS, InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'Separate multiple emails with a space', ) ->addUsage('hello@world.com'); } /** * @throws Throwable */ protected function execute(InputInterface $input, OutputInterface $output): int { $this->setIO($input, $output); $this->initParameters($input); $dateStart = new DateTimeImmutable('-2 days'); $dateEnd = new DateTimeImmutable('-1 day'); try { $aggregatorsMap = $this->getAggregatorNamesById(); if (count($aggregatorsMap) === 0) { $this->printError(self::CATALOG_AGGREGATORS_DO_NOT_EXIST); return self::FAILURE; } $messages = $this->buildMessageData($aggregatorsMap, $dateStart, $dateEnd); if (count($messages) === 0) { $this->printInfo('Statistics for the aggregator for yesterday and the day before yesterday exist'); return self::SUCCESS; } $this->sendNotifications($messages, $dateEnd); $this->logInfo($messages, $output->getVerbosity()); $this->addContext(['messages' => $messages]); } catch (Throwable $exception) { $this->logError($exception); return self::FAILURE; } return self::SUCCESS; } private function initParameters(InputInterface $input): void { $this->emails = $input->getArgument(self::ARGUMENT_EMAILS); } /** * @return array<int, string> [id => name] */ private function getAggregatorNamesById(): array { $result = []; $aggregators = $this->aggregatorRepository->findAllByFilter($this->createAggregatorsFilter()); foreach ($aggregators as $aggregator) { $result[$aggregator->getId()] = $aggregator->getName(); } return $result; } private function createAggregatorsFilter(): AggregatorFilter { return AggregatorFilter::create() ->addCondition(FilterCondition::eq(AggregatorFilter::PRODUCTION, true)) ->addCondition(FilterCondition::eq(AggregatorFilter::DISABLED, false)) ->addOrdering(AggregatorFilter::ID, true); } /** * @param array<int> $aggregatorsIds * @return array<int> */ private function getActiveAggregatorsOnDate(array $aggregatorsIds, DateTimeImmutable $date): array { $statistics = $this->aggregatorStatisticRepository->getAggregatedAggregatorStatistic( $this->createAggregatorStatisticFilter($aggregatorsIds, $date), ); return array_map(function (AggregatorStatisticItem $aggregatorStatisticItem) { Assert::notNull($aggregatorStatisticItem->aggregatorId, self::STATISTIC_AGGREGATOR_ID_MUST_BE_NOT_NULL); return $aggregatorStatisticItem->aggregatorId; }, $statistics); } /** * @param array<int, string> $aggregatorsMap */ private function buildMessageData( array $aggregatorsMap, DateTimeImmutable $dateStart, DateTimeImmutable $dateEnd, ): array { $messages = []; $aggregatorsIds = array_keys($aggregatorsMap); $activeAggregatorsOnDateStart = $this->getActiveAggregatorsOnDate($aggregatorsIds, $dateStart); $activeAggregatorsOnDateEnd = $this->getActiveAggregatorsOnDate($aggregatorsIds, $dateEnd); foreach ($activeAggregatorsOnDateStart as $aggregatorId) { if ( !in_array($aggregatorId, $activeAggregatorsOnDateEnd, true) && array_key_exists($aggregatorId, $aggregatorsMap) ) { $messages[] = [ 'aggregator_id' => $aggregatorId, 'aggregator_name' => $aggregatorsMap[$aggregatorId], ]; } } return $messages; } /** * @param array<int, array{aggregator_id: int, aggregator_name: string}> $messages * @throws Throwable * @throws TransportExceptionInterface * @throws LoaderError * @throws RuntimeError * @throws SyntaxError */ private function sendNotifications(array $messages, DateTimeImmutable $dateEnd): void { $date = $dateEnd->format('Y-m-d'); $this->emailSender->send( $this->emails, sprintf('Aggregators lost traffic %s', $date), self::MESSAGE_TEMPLATE, [ 'messages' => $messages, 'date' => $date, ], ); } /** * @param array<int> $aggregatorsIds */ private function createAggregatorStatisticFilter( array $aggregatorsIds, DateTimeImmutable $date, ): AggregatorStatisticFilter { $filter = AggregatorStatisticFilter::create(); $filter->addCondition(FilterCondition::eq(AggregatorStatisticFilter::DATE, $date->format('Y-m-d'))); $filter->addCondition( FilterCondition::in(AggregatorStatisticFilter::AGGREGATOR_ID, $aggregatorsIds), ); $filter->addCondition(FilterCondition::eq(AggregatorStatisticFilter::MODE, PlayerMode::MODE_REAL)); $filter->groupBy(AggregatorStatisticGroupBy::Aggregator); $filter->addOrdering(AggregatorStatisticFilter::AGGREGATOR_ID, true); return $filter; } private function logInfo(array $messages, int $verbosity): void { if (OutputInterface::VERBOSITY_NORMAL <= $verbosity) { $messageToLog = json_encode($messages); $this->printInfo($messageToLog); $this->logger->info($messageToLog); } } private function logError(Throwable $exception): void { $message = sprintf('An error: %s', $exception->getMessage()); $this->printError($message); $this->logger->error($message, ['exception' => $exception]); } }
You have javascript disabled. You will not be able to edit any code.