Spaces:
No application file
No application file
| declare(strict_types=1); | |
| namespace Mautic\CampaignBundle\Model; | |
| use Mautic\CampaignBundle\Entity\LeadEventLog; | |
| use Mautic\CampaignBundle\Entity\LeadEventLogRepository; | |
| use Mautic\CampaignBundle\Entity\Summary; | |
| use Mautic\CampaignBundle\Entity\SummaryRepository; | |
| use Mautic\CoreBundle\Helper\ProgressBarHelper; | |
| use Mautic\CoreBundle\Model\AbstractCommonModel; | |
| use Symfony\Component\Console\Output\OutputInterface; | |
| /** | |
| * @extends AbstractCommonModel<Summary> | |
| */ | |
| class SummaryModel extends AbstractCommonModel | |
| { | |
| private array $logData = []; | |
| /** | |
| * Collapse Event Log entities into insert/update queries for the campaign summary. | |
| * | |
| * @throws \Doctrine\DBAL\Exception | |
| */ | |
| public function updateSummary(iterable $logs): void | |
| { | |
| $now = new \DateTime(); | |
| /** @var LeadEventLog $log */ | |
| foreach ($logs as $log) { | |
| if (!$log->getDateTriggered()) { | |
| // This shouldn't normally happen but it's possible to have a log without a date triggered | |
| // as it is a nullable field and it can be created without date triggered for example via API. | |
| continue; | |
| } | |
| $timestamp = $log->getDateTriggered()->getTimestamp(); | |
| $timestamp -= ($timestamp % 3600); | |
| $dateFrom = $now->setTimestamp($timestamp); | |
| $dateTo = (clone $dateFrom)->modify('+1 hour -1 second'); | |
| $campaign = $log->getCampaign(); | |
| $event = $log->getEvent(); | |
| $key = $campaign->getId().'.'.$event->getId().'.'.$timestamp; | |
| $this->logData[$key] = [ | |
| 'campaignId' => $campaign->getId(), | |
| 'eventId' => $event->getId(), | |
| 'dateFrom' => $dateFrom, | |
| 'dateTo' => $dateTo, | |
| ]; | |
| } | |
| if (count($this->logData) >= 100) { | |
| $this->persistSummaries(); | |
| } | |
| } | |
| public function getRepository(): SummaryRepository | |
| { | |
| return $this->em->getRepository(Summary::class); | |
| } | |
| public function getPermissionBase(): string | |
| { | |
| return 'campaign:campaigns'; | |
| } | |
| /** | |
| * Summarize all of history. | |
| * | |
| * @throws \Doctrine\DBAL\Exception | |
| */ | |
| public function summarize(OutputInterface $output, int $hoursPerBatch = 1, int $maxHours = null, bool $rebuild = false): void | |
| { | |
| $start = null; | |
| if (!$rebuild) { | |
| $start = $this->getRepository()->getOldestTriggeredDate(); | |
| } | |
| // Start with the current hour. | |
| $start ??= new \DateTime('+1 hour'); | |
| $start->setTimestamp($start->getTimestamp() - ($start->getTimestamp() % 3600)); | |
| $end = $this->getCampaignLeadEventLogRepository()->getOldestTriggeredDate(); | |
| if (!$end) { | |
| $output->writeln('There are no records in the campaign lead event log table. Nothing to summarize.'); | |
| return; | |
| } | |
| $end = $end->setTimestamp($end->getTimestamp() - ($end->getTimestamp() % 3600)); | |
| $startedAt = new \DateTime(); | |
| $output->writeln('<comment>Started at: '.$startedAt->format('Y-m-d H:i:s').'</comment>'); | |
| if ($end <= $start) { | |
| $hours = ($end->diff($start)->days * 24) + $end->diff($start)->h; | |
| if ($maxHours && $hours > $maxHours) { | |
| $end = clone $start; | |
| $end = $end->sub(new \DateInterval('PT'.$maxHours.'H')); | |
| } | |
| $progressBar = ProgressBarHelper::init($output, $hours); | |
| $progressBar->start(); | |
| $interval = new \DateInterval('PT'.$hoursPerBatch.'H'); | |
| $dateFrom = clone $start; | |
| $dateTo = (clone $start)->modify('-1 second'); | |
| do { | |
| $dateFrom = $dateFrom->sub($interval); | |
| $dateFromFormatted = $dateFrom->format('Y-m-d H:i:s'); | |
| $dateToFormatted = $dateTo->format('Y-m-d H:i:s'); | |
| $output->write("\t".$dateFromFormatted.' - '.$dateToFormatted); | |
| $this->getRepository()->summarize($dateFrom, $dateTo); | |
| $progressBar->advance($hoursPerBatch); | |
| $dateTo = $dateTo->sub($interval); | |
| } while ($end < $dateFrom); | |
| $progressBar->finish(); | |
| $output->writeln("\n".'<info>Updating summary for log counts processed</info>'); | |
| } | |
| $this->outputProcessTime($startedAt, $output); | |
| } | |
| public function getCampaignLeadEventLogRepository(): LeadEventLogRepository | |
| { | |
| return $this->em->getRepository(LeadEventLog::class); | |
| } | |
| /** | |
| * @throws \Doctrine\DBAL\Exception | |
| */ | |
| public function persistSummaries(): void | |
| { | |
| foreach ($this->logData as $log) { | |
| $dateFrom = $log['dateFrom']; | |
| $dateTo = $log['dateTo']; | |
| $campaignId = $log['campaignId']; | |
| $eventId = $log['eventId']; | |
| $this->getRepository()->summarize($dateFrom, $dateTo, $campaignId, $eventId); | |
| } | |
| } | |
| private function outputProcessTime(\DateTime $startedAt, OutputInterface $output): void | |
| { | |
| $endedAt = new \DateTime(); | |
| $output->writeln("\n".'<comment>Ended at: '.$endedAt->format('Y-m-d H:i:s').'</comment>'); | |
| $completedInterval = $startedAt->diff($endedAt); | |
| $output->writeln('<info>Summary completed in: '.$completedInterval->format('%H:%I:%S').'</info>'); | |
| } | |
| } | |