public function getEnvironmentPeriodData(Scalr_Environment $env, $mode, $startDate, $endDate)
{
$analytics = $this->getContainer()->analytics;
$utcTz = new DateTimeZone('UTC');
$iterator = ChartPeriodIterator::create($mode, new DateTime($startDate, $utcTz), new DateTime($endDate, $utcTz), 'UTC');
$start = $iterator->getStart();
$end = $iterator->getEnd();
$timelineEvents = $analytics->events->count($iterator->getInterval(), $iterator->getStart(), $iterator->getEnd(), ['envId' => $env->id, 'accountId' => $env->clientId]);
//Interval which is used in the database query for grouping
$queryInterval = preg_replace('/^1 /', '', $iterator->getInterval());
$criteria = ['envId' => $env->id];
//Requests data for the specified period
$rawUsage = $this->getFarmData($env->clientId, $criteria, $start, $end, [$queryInterval, TagEntity::TAG_ID_PLATFORM, TagEntity::TAG_ID_FARM], true);
//Requests data for the previous period
$rawPrevUsage = $this->getFarmData($env->clientId, $criteria, $iterator->getPreviousStart(), $iterator->getPreviousEnd(), [$queryInterval, TagEntity::TAG_ID_PLATFORM, TagEntity::TAG_ID_FARM], true);
$max = 5;
//Calculates top five farms for the specified period
$top5farms = [];
$this->otherFarmsQuantity = 0;
$arr = (new AggregationCollection(['farmId'], ['cost' => 'sum']))->load($rawUsage)->getArrayCopy();
if (!empty($arr['data']) && count($arr['data']) > $max + 1) {
$this->otherFarmsQuantity = count($arr['data']) - $max;
uasort($arr['data'], function ($a, $b) {
if ($a['cost'] == $b['cost']) {
return 0;
}
return $a['cost'] < $b['cost'] ? 1 : -1;
});
$i = 0;
foreach ($arr['data'] as $farmId => $v) {
$top5farms[$farmId] = $farmId;
if (++$i >= 5) {
break;
}
}
}
$usgByPlatformDetailed = (new AggregationCollection(['period', 'platform'], ['cost' => 'sum']))->load($rawUsage)->calculatePercentage();
$usgByPlatformPrevDetailed = (new AggregationCollection(['period', 'platform'], ['cost' => 'sum']))->load($rawPrevUsage)->calculatePercentage();
if (empty($top5farms)) {
$usgByFarmDetailed = (new AggregationCollection(['period', 'farmId', 'platform'], ['cost' => 'sum']))->load($rawUsage)->calculatePercentage();
$usgByFarmPrevDetailed = (new AggregationCollection(['period', 'farmId', 'platform'], ['cost' => 'sum']))->load($rawPrevUsage)->calculatePercentage();
} else {
$usgByFarmDetailed = new AggregationCollection(['period', 'farmId', 'platform'], ['cost' => 'sum']);
foreach ($rawUsage as $d) {
if (!array_key_exists($d['farmId'], $top5farms)) {
$d['farmId'] = self::EVERYTHING_ELSE;
}
$usgByFarmDetailed->append($d);
}
$usgByFarmDetailed->calculatePercentage();
$usgByFarmPrevDetailed = new AggregationCollection(['period', 'farmId', 'platform'], ['cost' => 'sum']);
foreach ($rawPrevUsage as $d) {
if (!array_key_exists($d['farmId'], $top5farms)) {
$d['farmId'] = self::EVERYTHING_ELSE;
}
$usgByFarmPrevDetailed->append($d);
}
$usgByFarmPrevDetailed->calculatePercentage();
}
$quarterIterator = $this->getCurrentQuarterIterator();
$queryQuarterInterval = preg_replace('/^1 /', '', $quarterIterator->getInterval());
$rawQuarterUsage = $this->getFarmData($env->clientId, $criteria, $quarterIterator->getStart(), $quarterIterator->getEnd(), [TagEntity::TAG_ID_PLATFORM], true);
$itemsRollingAvg = $this->getRollingAvg(['envId' => $env->id], $queryQuarterInterval, $quarterIterator->getEnd(), $env->clientId, $rawQuarterUsage, ['platform' => 'clouds']);
$cloudsData = [];
$farmsData = [];
$timeline = [];
$prevPointKey = null;
foreach ($iterator as $chartPoint) {
/* @var $chartPoint \Scalr\Stats\CostAnalytics\ChartPointInfo */
$i = $chartPoint->i;
$currentPeriodTotal = isset($usgByPlatformDetailed['data'][$chartPoint->key]) ? $usgByPlatformDetailed['data'][$chartPoint->key] : null;
$ppTotal = isset($usgByPlatformPrevDetailed['data'][$chartPoint->previousPeriodKey]) ? $usgByPlatformPrevDetailed['data'][$chartPoint->previousPeriodKey] : null;
$pptTotal = isset($usgByPlatformDetailed['data'][$prevPointKey]) ? $usgByPlatformDetailed['data'][$prevPointKey] : null;
$pointDataTotal = $this->getPointDataArray($currentPeriodTotal, $ppTotal, $pptTotal);
$timeline[] = ['datetime' => $chartPoint->dt->format('Y-m-d H:00'), 'label' => $chartPoint->label, 'onchart' => $chartPoint->show, 'events' => isset($timelineEvents[$chartPoint->key]) ? $timelineEvents[$chartPoint->key] : null] + $pointDataTotal;
//Period - Platform - Farms subtotals
if (!isset($usgByPlatformDetailed['data'][$chartPoint->key]['data'])) {
foreach ($cloudsData as $platform => $v) {
if (!$iterator->isFuture()) {
//Previous period details
if (isset($usgByPlatformPrevDetailed['data'][$chartPoint->previousPeriodKey]['data'][$platform])) {
$pp = $usgByPlatformPrevDetailed['data'][$chartPoint->previousPeriodKey]['data'][$platform];
} else {
$pp = null;
}
//Previous point details
if (isset($usgByPlatformDetailed['data'][$prevPointKey]['data'][$platform])) {
$ppt = $usgByPlatformDetailed['data'][$prevPointKey]['data'][$platform];
} else {
$ppt = null;
}
$r = $this->getPointDataArray(null, $pp, $ppt);
$cloudsData[$platform]['data'][] = $r;
} else {
$cloudsData[$platform]['data'][] = null;
}
}
} else {
//Initializes with empty values to prevent data shifts on charts.
if (!isset($usgByPlatformDetailed['data'][$chartPoint->key]['data'])) {
$usgByPlatformDetailed['data'][$chartPoint->key]['data'] = [];
}
$combined =& $usgByPlatformDetailed['data'][$chartPoint->key]['data'];
if (!empty($cloudsData)) {
foreach ($cloudsData as $platform => $t) {
if (!array_key_exists($platform, $combined)) {
$combined[$platform] = [];
}
}
}
foreach ($combined as $platform => $v) {
//Previous period details
if (isset($usgByPlatformPrevDetailed['data'][$chartPoint->previousPeriodKey]['data'][$platform])) {
$pp = $usgByPlatformPrevDetailed['data'][$chartPoint->previousPeriodKey]['data'][$platform];
} else {
$pp = null;
}
//Previous point details
if (isset($usgByPlatformDetailed['data'][$prevPointKey]['data'][$platform])) {
$ppt = $usgByPlatformDetailed['data'][$prevPointKey]['data'][$platform];
} else {
$ppt = null;
}
if (!isset($cloudsData[$platform]) && $i > 0) {
$cloudsData[$platform]['name'] = $platform;
//initializes platfrorm legend for the not filled period
$cloudsData[$platform]['data'] = array_fill(0, $i, null);
}
if (!$iterator->isFuture()) {
$r = $this->getPointDataArray($v, $pp, $ppt);
$cloudsData[$platform]['name'] = $platform;
$cloudsData[$platform]['data'][] = $r;
} else {
$cloudsData[$platform]['data'][] = null;
}
}
}
//Period - Farm - Platform subtotal
if (!isset($usgByFarmDetailed['data'][$chartPoint->key]['data'])) {
foreach ($farmsData as $farmId => $v) {
if (!$iterator->isFuture()) {
//Previous period details
if (isset($usgByFarmPrevDetailed['data'][$chartPoint->previousPeriodKey]['data'][$farmId])) {
$pp = $usgByFarmPrevDetailed['data'][$chartPoint->previousPeriodKey]['data'][$farmId];
} else {
$pp = null;
}
//Previous point details
if (isset($usgByFarmDetailed['data'][$prevPointKey]['data'][$farmId])) {
$ppt = $usgByFarmDetailed['data'][$prevPointKey]['data'][$farmId];
} else {
$ppt = null;
}
$r = $this->getPointDataArray(null, $pp, $ppt);
$r['clouds'] = [];
$r['usageTypes'] = [];
$farmsData[$farmId]['data'][] = $r;
} else {
$farmsData[$farmId]['data'][] = null;
}
}
} else {
//Initializes with empty values to prevent data shifts on charts.
if (!isset($usgByFarmDetailed['data'][$chartPoint->key]['data'])) {
$usgByFarmDetailed['data'][$chartPoint->key]['data'] = [];
}
$combined =& $usgByFarmDetailed['data'][$chartPoint->key]['data'];
if (!empty($farmsData)) {
foreach ($farmsData as $farmId => $t) {
if (!array_key_exists($farmId, $combined)) {
$combined[$farmId] = [];
}
}
}
foreach ($combined as $farmId => $v) {
//Previous period details
if (isset($usgByFarmPrevDetailed['data'][$chartPoint->previousPeriodKey]['data'][$farmId])) {
$pp = $usgByFarmPrevDetailed['data'][$chartPoint->previousPeriodKey]['data'][$farmId];
} else {
$pp = null;
}
//Previous point details
if (isset($usgByFarmDetailed['data'][$prevPointKey]['data'][$farmId])) {
$ppt = $usgByFarmDetailed['data'][$prevPointKey]['data'][$farmId];
} else {
$ppt = null;
}
if (!isset($farmsData[$farmId]) && $i > 0) {
$farmsData[$farmId]['name'] = $this->fetchFarmName($farmId);
//initializes project legend for the not filled period
$farmsData[$farmId]['data'] = array_fill(0, $i, null);
}
if (!$iterator->isFuture()) {
$r = $this->getPointDataArray($v, $pp, $ppt);
// platform data
$cloudPlatformData = [];
if (!empty($v['data'])) {
foreach ($v['data'] as $platform => $pv) {
$cloudPlatformData[] = $this->getDetailedPointDataArray($platform, $platform, $pv, isset($pp['data'][$platform]) ? $pp['data'][$platform] : null, isset($ppt['data'][$platform]) ? $ppt['data'][$platform] : null);
}
}
$r['clouds'] = $cloudPlatformData;
$farmsData[$farmId]['name'] = $this->fetchFarmName($farmId);
$farmsData[$farmId]['data'][] = $r;
} else {
$farmsData[$farmId]['data'][] = null;
}
}
}
$prevPointKey = $chartPoint->key;
}
//complete arrays for cloud data and farm data
$cntpoints = count($timeline);
foreach ($cloudsData as $platform => $v) {
if (($j = count($v['data'])) < $cntpoints) {
while ($j < $cntpoints) {
$cloudsData[$platform]['data'][] = null;
$j++;
}
}
}
foreach ($farmsData as $farmId => $v) {
if (($j = count($v['data'])) < $cntpoints) {
while ($j < $cntpoints) {
$farmsData[$farmId]['data'][] = null;
$j++;
}
}
}
//Subtotals by platforms
$usage = new AggregationCollection(['platform', 'farmId'], ['cost' => 'sum']);
//Subtotals by farms
$usage2 = new AggregationCollection(['farmId' => ['projectId', 'envId'], 'platform'], ['cost' => 'sum']);
//Subtotals by distr types
$usage3 = (new AggregationCollection(['distributionType'], ['cost' => 'sum']))->load($this->getFarmData($env->clientId, $criteria, $start, $end, ['distributionType', 'usageType', 'usageItem'], true))->calculatePercentage();
//Previous period subtotals by platforms
$prevUsage = new AggregationCollection(['platform', 'farmId'], ['cost' => 'sum']);
//Previous period subtotals by farms
$prevUsage2 = new AggregationCollection(['farmId', 'platform'], ['cost' => 'sum']);
if (empty($top5farms)) {
//Loads current period
foreach ($rawUsage as $item) {
$usage->append($item);
$usage2->append($item);
}
//Loads previous period
foreach ($rawPrevUsage as $item) {
$prevUsage->append($item);
$prevUsage2->append($item);
}
} else {
//Loads current period and aggregates top 5 farms
foreach ($rawUsage as $item) {
if (!array_key_exists($item['farmId'], $top5farms)) {
$item['farmId'] = self::EVERYTHING_ELSE;
}
$usage->append($item);
$usage2->append($item);
}
//Loads previous period and aggregates top 5 farms
foreach ($rawPrevUsage as $item) {
if (!array_key_exists($item['farmId'], $top5farms)) {
$item['farmId'] = self::EVERYTHING_ELSE;
}
$prevUsage->append($item);
$prevUsage2->append($item);
}
}
//Calculates percentage
$usage->calculatePercentage();
$usage2->calculatePercentage();
$prevUsage->calculatePercentage();
$prevUsage2->calculatePercentage();
if ($iterator->getWholePreviousPeriodEnd() != $iterator->getPreviousEnd()) {
$rawPrevUsageWhole = $this->getFarmData($env->clientId, ['envId' => $env->id], $iterator->getPreviousStart(), $iterator->getWholePreviousPeriodEnd(), [TagEntity::TAG_ID_PLATFORM, TagEntity::TAG_ID_FARM], true);
//Previous whole period usage subtotals by platform
$prevUsageWhole = (new AggregationCollection(['platform'], ['cost' => 'sum']))->load($rawPrevUsageWhole);
//Previous whole period usage subtotals by farm
$prevUsageWhole2 = (new AggregationCollection(['farmId'], ['cost' => 'sum']))->load($rawPrevUsageWhole);
} else {
$prevUsageWhole = $prevUsage;
$prevUsageWhole2 = $prevUsage2;
}
//Build cloud platforms total
$cloudsTotal = [];
$it = $usage->getIterator();
foreach ($it as $platform => $p) {
$pp = isset($prevUsage['data'][$platform]) ? $prevUsage['data'][$platform] : null;
$pw = isset($prevUsageWhole['data'][$platform]) ? $prevUsageWhole['data'][$platform] : null;
$cl = $this->getTotalDataArray($platform, $platform, $p, $pp, $pw, $cloudsData, $iterator);
$cloudsTotal[] = $cl;
}
//Build farms total
$farmsTotal = [];
$it = $usage2->getIterator();
foreach ($it as $farmId => $p) {
$pp = isset($prevUsage2['data'][$farmId]) ? $prevUsage2['data'][$farmId] : null;
$pw = isset($prevUsageWhole2['data'][$farmId]) ? $prevUsageWhole2['data'][$farmId] : null;
$cl = $this->getTotalDataArray($farmId, $this->fetchFarmName($farmId), $p, $pp, $pw, $farmsData, $iterator);
if ($farmId && $farmId != self::EVERYTHING_ELSE) {
$userId = AccountTagEntity::fetchName($farmId, TagEntity::TAG_ID_FARM_OWNER);
if ($userId) {
$cl['email'] = AccountTagEntity::fetchName($userId, TagEntity::TAG_ID_USER, $env->clientId);
}
if (!empty($p['envId'])) {
$cl['environment'] = ['id' => (int) $p['envId'], 'name' => AccountTagEntity::fetchName($p['envId'], TagEntity::TAG_ID_ENVIRONMENT)];
}
if (!empty($p['projectId']) && $p['projectId'] !== '00000000-0000-0000-0000-000000000000') {
$cl['projectName'] = AccountTagEntity::fetchName($p['projectId'], TagEntity::TAG_ID_PROJECT);
} else {
$cl['projectName'] = 'Unassigned resources';
}
}
if ($it->hasChildren()) {
$clPlatforms = [];
foreach ($it->getChildren() as $platform => $c) {
$cp = isset($prevUsage2['data'][$farmId]['data'][$platform]) ? $prevUsage2['data'][$farmId]['data'][$platform] : null;
$clPlatforms[] = $this->getTotalDataArray($platform, $platform, $c, $cp, null, $cloudsData, $iterator, true);
}
$cl['clouds'] = $clPlatforms;
} else {
$cl['clouds'] = [];
}
$farmsTotal[] = $cl;
}
// Build cost dist types total
$distributionTypesTotal = [];
foreach ($usage3->getIterator() as $distributionType => $costUsage) {
$distributionTypesTotal[] = $this->getTotalDataArray($distributionType, $distributionType, $costUsage, null, null, [], null, true);
}
$data = ['totals' => ['cost' => round($usage['cost'], 2), 'prevCost' => round($prevUsage['cost'], 2), 'growth' => round($usage['cost'] - $prevUsage['cost'], 2), 'growthPct' => $prevUsage['cost'] == 0 ? null : round(abs(($usage['cost'] - $prevUsage['cost']) / $prevUsage['cost'] * 100), 0), 'clouds' => $cloudsTotal, 'farms' => $farmsTotal, 'distributionTypes' => $distributionTypesTotal, 'trends' => $this->calculateSpendingTrends(['envId' => $env->id], $timeline, $queryInterval, $iterator->getEnd(), $env->clientId), 'forecastCost' => null], 'timeline' => $timeline, 'clouds' => $cloudsData, 'farms' => $farmsData, 'interval' => $queryInterval, 'startDate' => $iterator->getStart()->format('Y-m-d'), 'endDate' => $iterator->getEnd()->format('Y-m-d'), 'previousStartDate' => $iterator->getPreviousStart()->format('Y-m-d'), 'previousEndDate' => $iterator->getPreviousEnd()->format('Y-m-d')];
if ($iterator->getTodayDate() < $iterator->getEnd()) {
//Today is in the selected period
$data['totals']['forecastCost'] = self::calculateForecast($data['totals']['cost'], $start, $end, $prevUsageWhole['cost'], ($data['totals']['growth'] >= 0 ? 1 : -1) * $data['totals']['growthPct'], isset($itemsRollingAvg['rollingAverageDaily']) ? $itemsRollingAvg['rollingAverageDaily'] : null);
}
return $data;
}