Initial commit: CloudOps infrastructure platform

This commit is contained in:
root
2026-04-09 19:58:57 +02:00
commit 1166a52f26
7762 changed files with 839452 additions and 0 deletions

View File

@@ -0,0 +1,173 @@
<?php
namespace Mautic\StatsBundle\Aggregate;
use Mautic\StatsBundle\Aggregate\Collection\DAO\StatDAO;
use Mautic\StatsBundle\Aggregate\Collection\DAO\StatsDAO;
use Mautic\StatsBundle\Aggregate\Helper\CalculatorHelper;
class Calculator
{
public function __construct(
private StatsDAO $statsDAO,
private ?\DateTimeInterface $fromDateTime = null,
private ?\DateTimeInterface $toDateTime = null,
) {
}
/**
* @param string $labelFormat
*
* @throws \Exception
*/
public function getSumsByYear($labelFormat = 'Y'): StatDAO
{
$statDAO = new StatDAO();
$lastYear = $this->fromDateTime ? $this->fromDateTime->format('Y') : null;
foreach ($this->statsDAO->getYears() as $thisYear => $stats) {
CalculatorHelper::fillInMissingYears($statDAO, $lastYear, $thisYear, $labelFormat);
$statDAO->addStat(
CalculatorHelper::getYearLabel($thisYear, $labelFormat),
$stats->getSum()
);
$lastYear = $thisYear;
}
if ($this->toDateTime) {
CalculatorHelper::fillInMissingYears($statDAO, $lastYear, $this->toDateTime->format('Y'), $labelFormat);
}
return $statDAO;
}
/**
* @param string $labelFormat
*
* @throws \Exception
*/
public function getSumsByMonth($labelFormat = 'Y-m'): StatDAO
{
$statDAO = new StatDAO();
$lastMonth = $this->fromDateTime ? $this->fromDateTime->format('Y-m') : null;
foreach ($this->statsDAO->getMonths() as $thisMonth => $stats) {
CalculatorHelper::fillInMissingMonths($statDAO, $lastMonth, $thisMonth, $labelFormat);
$statDAO->addStat(
CalculatorHelper::getMonthLabel($thisMonth, $labelFormat),
$stats->getSum()
);
$lastMonth = $thisMonth;
}
if ($this->toDateTime) {
CalculatorHelper::fillInMissingMonths($statDAO, $lastMonth, $this->toDateTime->format('Y-m'), $labelFormat);
}
return $statDAO;
}
/**
* @param string $labelFormat
*
* @throws \Exception
*/
public function getSumsByDay($labelFormat = 'Y-m-d'): StatDAO
{
$statDAO = new StatDAO();
$yesterday = $this->fromDateTime ? $this->fromDateTime->format('Y-m-d') : null;
foreach ($this->statsDAO->getDays() as $today => $stats) {
CalculatorHelper::fillInMissingDays($statDAO, $yesterday, $today, $labelFormat);
$statDAO->addStat(
CalculatorHelper::getDayLabel($today, $labelFormat),
$stats->getSum()
);
$yesterday = $today;
}
if ($this->toDateTime) {
CalculatorHelper::fillInMissingDays($statDAO, $yesterday, $this->toDateTime->format('Y-m-d'), $labelFormat);
}
return $statDAO;
}
/**
* @param string $labelFormat
*
* @throws \Exception
*/
public function getSumsByWeek($labelFormat = 'Y-W'): StatDAO
{
$statDAO = new StatDAO();
$yesterday = $this->fromDateTime ? $this->fromDateTime->format('Y-W') : null;
foreach ($this->statsDAO->getWeeks() as $today => $stats) {
CalculatorHelper::fillInMissingWeeks($statDAO, $yesterday, $today, $labelFormat);
$statDAO->addStat(
$today,
$stats->getCount()
);
$yesterday = $today;
}
$yesterday = (new \DateTime(CalculatorHelper::getWeekDateString($yesterday)))->modify('+1 week')->format('Y-W');
if ($this->toDateTime) {
/** @var \DateTime $tomorrow */
$tomorrow = clone $this->toDateTime;
CalculatorHelper::fillInMissingWeeks($statDAO, $yesterday, $tomorrow->modify('+1 week')->format('Y-W'), $labelFormat);
}
return $statDAO;
}
/**
* @param string $labelFormat
*
* @throws \Exception
*/
public function getCountsByHour($labelFormat = 'Y-m-d H'): StatDAO
{
$statDAO = new StatDAO();
$lastHour = $this->fromDateTime ? $this->fromDateTime->format('Y-m-d H') : null;
foreach ($this->statsDAO->getHours() as $thisHour => $stats) {
CalculatorHelper::fillInMissingHours($statDAO, $lastHour, $thisHour, $labelFormat);
$statDAO->addStat(
CalculatorHelper::getHourLabel($thisHour, $labelFormat),
$stats->getCount()
);
$lastHour = $thisHour;
}
if ($this->toDateTime) {
CalculatorHelper::fillInMissingHours($statDAO, $lastHour, $this->toDateTime->format('Y-m-d H'), $labelFormat);
}
return $statDAO;
}
public function getSum(): StatDAO
{
$statDAO = new StatDAO();
$sum = 0;
foreach ($this->statsDAO->getYears() as $stats) {
$sum += $stats->getSum();
}
return $statDAO;
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Mautic\StatsBundle\Aggregate\Collection\DAO;
class StatDAO
{
private array $stats = [];
/**
* @return $this
*/
public function addStat($key, $value)
{
if (!isset($this->stats[$key])) {
$this->stats[$key] = 0;
}
$this->stats[$key] += $value;
return $this;
}
/**
* @return mixed
*/
public function getStat($key)
{
if (!isset($this->stats[$key])) {
throw new \InvalidArgumentException($key.' does not exist');
}
return $this->stats[$key];
}
/**
* @return array
*/
public function getStats()
{
return $this->stats;
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace Mautic\StatsBundle\Aggregate\Collection\DAO;
use Mautic\StatsBundle\Aggregate\Collection\Stats\DayStat;
use Mautic\StatsBundle\Aggregate\Collection\Stats\HourStat;
use Mautic\StatsBundle\Aggregate\Collection\Stats\MonthStat;
use Mautic\StatsBundle\Aggregate\Collection\Stats\WeekStat;
use Mautic\StatsBundle\Aggregate\Collection\Stats\YearStat;
use Mautic\StatsBundle\Aggregate\Helper\CalculatorHelper;
class StatsDAO
{
/**
* @var YearStat[]
*/
private array $years = [];
/**
* @return YearStat
*/
public function getYear($year)
{
if (!isset($this->years[$year])) {
$this->years[$year] = new YearStat($year);
}
return $this->years[$year];
}
/**
* @return YearStat[]
*/
public function getYears()
{
ksort($this->years);
return $this->years;
}
/**
* @return MonthStat[]
*
* @throws \Exception
*/
public function getMonths(): array
{
$flattenedMonths = [];
foreach ($this->years as $yearStats) {
$months = $yearStats->getStats();
foreach ($months as $month => $monthStats) {
$flattenedMonths[$month] = $monthStats;
}
}
ksort($flattenedMonths);
return $flattenedMonths;
}
/**
* @return WeekStat[]
*
* @throws \Exception
*/
public function getWeeks(): array
{
$flattenedWeeks = [];
foreach ($this->getDays() as $day => $stats) {
$week = CalculatorHelper::getWeekFromDayString($day);
if (!isset($flattenedWeeks[$week])) {
$flattenedWeeks[$week] = new WeekStat();
$flattenedWeeks[$week]->setCount($stats->getSum());
} else {
$flattenedWeeks[$week]->addToCount($stats->getSum());
}
}
ksort($flattenedWeeks);
return $flattenedWeeks;
}
/**
* @return DayStat[]
*
* @throws \Exception
*/
public function getDays(): array
{
$flattenedDays = [];
$months = $this->getMonths();
foreach ($months as $monthStats) {
$stats = $monthStats->getStats();
foreach ($stats as $day => $dayStats) {
$flattenedDays[$day] = $dayStats;
}
}
ksort($flattenedDays);
return $flattenedDays;
}
/**
* @return HourStat[]
*
* @throws \Exception
*/
public function getHours(): array
{
$flattenedHours = [];
$days = $this->getDays();
foreach ($days as $dayStats) {
$stats = $dayStats->getStats();
foreach ($stats as $hour => $hourStat) {
$flattenedHours[$hour] = $hourStat;
}
}
ksort($flattenedHours);
return $flattenedHours;
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace Mautic\StatsBundle\Aggregate\Collection;
use Mautic\StatsBundle\Aggregate\Calculator;
use Mautic\StatsBundle\Aggregate\Collection\DAO\StatsDAO;
use Mautic\StatsBundle\Aggregate\Helper\CalculatorHelper;
class StatCollection
{
private StatsDAO $stats;
private ?Calculator $calculator = null;
public function __construct()
{
$this->stats = new StatsDAO();
}
/**
* @param int $year
* @param int $month
* @param int $day
* @param int $hour
* @param int $count
*
* @return $this
*
* @throws \Exception
*/
public function addStat($year, $month, $day, $hour, $count)
{
$this->stats
->getYear($year)
->getMonth($month)
->getDay($day)
->getHour($hour)
->setCount($count);
return $this;
}
/**
* @param int $count
*
* @return $this
*
* @throws \Exception
*/
public function addStatByDateTime(\DateTime $dateTime, $count)
{
$dateTime->setTimezone(new \DateTimeZone('UTC'));
$this->addStat(
$dateTime->format('Y'),
$dateTime->format('n'),
$dateTime->format('j'),
$dateTime->format('H'),
$count
);
return $this;
}
/**
* @return $this
*
* @throws \Exception
*/
public function addStatByDateTimeStringInUTC($dateTimeInUTC, $count)
{
if (preg_match('/([0-9]{4})\\s([0-9]{2})/', $dateTimeInUTC, $matches)) { // Is this a week?
$dateTimeString = CalculatorHelper::getWeekDateString($matches[1].'-'.$matches[2]);
$dateTime = new \DateTime($dateTimeString, new \DateTimeZone('UTC'));
} elseif (4 === strlen($dateTimeInUTC) and is_numeric($dateTimeInUTC)) {
$dateTime = (new \DateTime('now', new \DateTimeZone('UTC')))
->setDate($dateTimeInUTC, 1, 1)
->setTime(0, 0);
} else {
$dateTime = new \DateTime($dateTimeInUTC, new \DateTimeZone('UTC'));
}
$this->addStatByDateTime($dateTime, $count);
return $this;
}
/**
* @return StatsDAO
*/
public function getStats()
{
return $this->stats;
}
/**
* @return Calculator
*/
public function getCalculator(\DateTime $fromDateTime, \DateTime $toDateTime)
{
if (is_null($this->calculator)) {
$this->calculator = new Calculator($this->stats, $fromDateTime, $toDateTime);
}
return $this->calculator;
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Mautic\StatsBundle\Aggregate\Collection\Stats;
class DayStat implements StatInterface
{
/**
* @var HourStat[]
*/
private array $stats = [];
/**
* @param string $day "2019-11-07" format
*/
public function __construct(
private $day,
) {
}
/**
* @param int $hour
*
* @return HourStat
*
* @throws \Exception
*/
public function getHour($hour)
{
$key = (new \DateTime("{$this->day} $hour:00:00"))->format('Y-m-d H');
if (!isset($this->stats[$key])) {
$this->stats[$key] = new HourStat($key);
}
return $this->stats[$key];
}
/**
* @return HourStat[]
*/
public function getStats()
{
return $this->stats;
}
/**
* @return int
*/
public function getSum()
{
$sum = 0;
foreach ($this->stats as $stat) {
$sum += $stat->getCount();
}
return $sum;
}
public function getCount(): int
{
return count($this->stats);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Mautic\StatsBundle\Aggregate\Collection\Stats;
class HourStat
{
private int $count = 0;
/**
* @param string $hour "2018-12-07 12" format
*/
public function __construct(
private $hour,
) {
}
/**
* @return string
*/
public function getHour()
{
return $this->hour;
}
/**
* @param int $count
*/
public function setCount($count): void
{
$this->count = (int) $count;
}
public function getCount(): int
{
return $this->count;
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace Mautic\StatsBundle\Aggregate\Collection\Stats;
class MonthStat implements StatInterface
{
/**
* @var DayStat[]
*/
private array $stats = [];
/**
* @param string $month "2019-01" format
*/
public function __construct(
private $month,
) {
}
/**
* @param int $day
*
* @return DayStat
*
* @throws \Exception
*/
public function getDay($day)
{
$key = (new \DateTime("{$this->month}-$day 00:00:00"))->format('Y-m-d');
if (!isset($this->stats[$key])) {
$this->stats[$key] = new DayStat($key);
}
return $this->stats[$key];
}
/**
* @return DayStat[]
*/
public function getStats()
{
return $this->stats;
}
/**
* @return int
*/
public function getSum()
{
$sum = 0;
foreach ($this->stats as $stat) {
$sum += $stat->getSum();
}
return $sum;
}
public function getCount(): int
{
return count($this->stats);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Mautic\StatsBundle\Aggregate\Collection\Stats;
interface StatInterface
{
/**
* @return array
*/
public function getStats();
/**
* @return int
*/
public function getSum();
/**
* @return int
*/
public function getCount();
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Mautic\StatsBundle\Aggregate\Collection\Stats;
class WeekStat
{
private int $count = 0;
public function getCount(): int
{
return $this->count;
}
/**
* @param int $count
*/
public function setCount($count): void
{
$this->count = (int) $count;
}
/**
* @param int $count
*/
public function addToCount($count): void
{
$this->count += $count;
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Mautic\StatsBundle\Aggregate\Collection\Stats;
class YearStat implements StatInterface
{
/**
* @var MonthStat[]
*/
private array $stats = [];
private int $year;
/**
* @param int $year
*/
public function __construct($year)
{
$this->year = (int) $year;
}
/**
* @param int $month
*
* @return MonthStat
*
* @throws \Exception
*/
public function getMonth($month)
{
$key = (new \DateTime("{$this->year}-$month-01 00:00:00"))->format('Y-m');
if (!isset($this->stats[$key])) {
$this->stats[$key] = new MonthStat($key);
}
return $this->stats[$key];
}
/**
* @return MonthStat[]
*/
public function getStats()
{
return $this->stats;
}
/**
* @return int
*/
public function getSum()
{
$sum = 0;
foreach ($this->stats as $stat) {
$sum += $stat->getSum();
}
return $sum;
}
public function getCount(): int
{
return count($this->stats);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Mautic\StatsBundle\Aggregate;
use Mautic\StatsBundle\Aggregate\Collection\StatCollection;
use Mautic\StatsBundle\Event\AggregateStatRequestEvent;
use Mautic\StatsBundle\Event\Options\FetchOptions;
use Mautic\StatsBundle\StatEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class Collector
{
public function __construct(
private EventDispatcherInterface $eventDispatcher,
) {
}
/**
* @param string $statName
*
* @return StatCollection
*/
public function fetchStats($statName, \DateTime $fromDateTime, \DateTime $toDateTime, ?FetchOptions $fetchOptions = null)
{
if (null === $fetchOptions) {
$fetchOptions = new FetchOptions();
}
$event = new AggregateStatRequestEvent($statName, $fromDateTime, $toDateTime, $fetchOptions);
$this->eventDispatcher->dispatch($event, StatEvents::AGGREGATE_STAT_REQUEST);
return $event->getStatCollection();
}
}

View File

@@ -0,0 +1,210 @@
<?php
namespace Mautic\StatsBundle\Aggregate\Helper;
use Mautic\StatsBundle\Aggregate\Collection\DAO\StatDAO;
class CalculatorHelper
{
/**
* @throws \Exception
*/
public static function getYearLabel($year, $labelFormat): string
{
return (new \DateTime(self::getYearDateString($year)))->format($labelFormat);
}
public static function getYearDateString($year): string
{
return "$year-01-01 00:00:00";
}
/**
* @param string $lastYear
* @param string $thisYear
* @param string $labelFormat
*
* @throws \Exception
*/
public static function fillInMissingYears(StatDAO $statDAO, $lastYear, $thisYear, $labelFormat): void
{
if (!$lastYear) {
return;
}
$lastYear = new \DateTime(self::getYearDateString($lastYear));
$thisYear = new \DateTime(self::getYearDateString($thisYear));
if (!isset($statDAO->getStats()[$lastYear->format($labelFormat)])) {
$statDAO->addStat($lastYear->format($labelFormat), 0);
}
while ($lastYear < $thisYear) {
$lastYear->modify('+1 year');
$statDAO->addStat($lastYear->format($labelFormat), 0);
}
}
/**
* @throws \Exception
*/
public static function getMonthLabel($month, $labelFormat): string
{
return (new \DateTime(self::getMonthDateString($month)))->format($labelFormat);
}
public static function getMonthDateString($month): string
{
return "$month-01 00:00:00";
}
/**
* @param string $lastMonth
* @param string $thisMonth
* @param string $labelFormat
*
* @throws \Exception
*/
public static function fillInMissingMonths(StatDAO $statDAO, $lastMonth, $thisMonth, $labelFormat): void
{
if (!$lastMonth) {
return;
}
$lastMonth = new \DateTime(self::getMonthDateString($lastMonth));
$thisMonth = new \DateTime(self::getMonthDateString($thisMonth));
if (!isset($statDAO->getStats()[$lastMonth->format($labelFormat)])) {
$statDAO->addStat($lastMonth->format($labelFormat), 0);
}
while ($lastMonth < $thisMonth) {
$lastMonth->modify('+1 month');
$statDAO->addStat($lastMonth->format($labelFormat), 0);
}
}
/**
* @throws \Exception
*/
public static function getDayLabel($day, $labelFormat): string
{
return (new \DateTime(self::getDayDateString($day)))->format($labelFormat);
}
public static function getDayDateString($day): string
{
return "$day 00:00:00";
}
/**
* @param string $date
*
* @throws \Exception
*/
public static function getWeekFromDayString($date): string
{
return (new \DateTime($date))->format('Y-W');
}
/**
* @param string $date
* @param string $labelFormat
*
* @throws \Exception
*/
public static function getWeekLabel($date, $labelFormat = 'Y-W'): string
{
return (new \DateTime(self::getWeekDateString($date)))->format($labelFormat);
}
public static function getWeekDateString($date): string
{
if (!preg_match('/^([0-9]{4})-([0-9]{2})$/', $date, $matches)) {
throw new \InvalidArgumentException('Invalid argument, Y-W format is required.');
}
$year = $matches[1];
$week = $matches[2];
return date('Y-m-d 00:00:00', strtotime($year.'W'.$week.'1'));
}
/**
* @param string $yesterday
* @param string $today
* @param string $labelFormat
*
* @throws \Exception
*/
public static function fillInMissingWeeks(StatDAO $statDAO, $yesterday, $today, $labelFormat): void
{
if (!$yesterday) {
return;
}
$yesterday = new \DateTime(self::getWeekDateString($yesterday));
$today = new \DateTime(self::getWeekDateString($today));
while ($yesterday < $today) {
$statDAO->addStat($yesterday->format($labelFormat), 0);
$yesterday->modify('+1 week');
}
}
/**
* @param string $yesterday
* @param string $today
* @param string $labelFormat
*
* @throws \Exception
*/
public static function fillInMissingDays(StatDAO $statDAO, $yesterday, $today, $labelFormat): void
{
if (!$yesterday) {
return;
}
$yesterday = (new \DateTime(self::getDayDateString($yesterday)))->modify('-1 day');
$today = new \DateTime(self::getDayDateString($today));
while ($yesterday < $today) {
$yesterday->modify('+1 day');
$statDAO->addStat($yesterday->format($labelFormat), 0);
}
}
/**
* @throws \Exception
*/
public static function getHourLabel($hour, $labelFormat): string
{
return (new \DateTime(self::getHourDateString($hour)))->format($labelFormat);
}
public static function getHourDateString($hour): string
{
return "$hour:00:00";
}
/**
* @param string $lastHour
* @param string $thisHour
* @param string $labelFormat
*
* @throws \Exception
*/
public static function fillInMissingHours(StatDAO $statDAO, $lastHour, $thisHour, $labelFormat): void
{
if (!$lastHour) {
return;
}
$lastHour = new \DateTime(self::getHourDateString($lastHour));
$thisHour = new \DateTime(self::getHourDateString($thisHour));
while ($lastHour < $thisHour) {
$lastHour->modify('+1 hour');
$statDAO->addStat($lastHour->format($labelFormat), 0);
}
}
}