/** * Gets period data for top farms * * @param int $accountId The current client id * @param array $allowedEnvs Array of allowed environments' ids for current user * @param string $mode The mode (week, month, quarter, year) * @param string $startDate The start date of the period in UTC ('Y-m-d') * @param string $endDate The end date of the period in UTC ('Y-m-d') * @param int $farmCount Top farms count * @return array Returns cost analytics data for environment scope */ public function getTopFarmsPeriodData($accountId, array $allowedEnvs, $mode, $startDate, $endDate, $farmCount = 5) { $utcTz = new DateTimeZone('UTC'); $iterator = ChartPeriodIterator::create($mode, new DateTime($startDate, $utcTz), new DateTime($endDate, $utcTz), 'UTC'); $start = $iterator->getStart(); $end = $iterator->getEnd(); //Interval which is used in the database query for grouping $queryInterval = preg_replace('/^1 /', '', $iterator->getInterval()); $criteria = !empty($allowedEnvs) ? ['envId' => $allowedEnvs] : []; //Requests data for the specified period $rawUsage = $this->getFarmData($accountId, $criteria, $start, $end, [$queryInterval, TagEntity::TAG_ID_FARM], true); //Requests data for the previous period $rawPrevUsage = $this->getFarmData($accountId, $criteria, $iterator->getPreviousStart(), $iterator->getPreviousEnd(), [$queryInterval, TagEntity::TAG_ID_FARM], true); //Calculates top five farms for the specified period $topFarms = []; $arr = (new AggregationCollection(['farmId'], ['cost' => 'sum']))->load($rawUsage)->getArrayCopy(); if (!empty($arr['data']) && count($arr['data']) > $farmCount + 1) { 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) { $topFarms[$farmId] = $farmId; if (++$i >= $farmCount) { break; } } } //Subtotals by farms $usage = new AggregationCollection(['farmId'], ['cost' => 'sum']); //Previous period subtotals by farms $prevUsage = new AggregationCollection(['farmId'], ['cost' => 'sum']); if (empty($topFarms)) { //Loads current period foreach ($rawUsage as $item) { $usage->append($item); } //Loads previous period foreach ($rawPrevUsage as $item) { $prevUsage->append($item); } } else { //Loads current period and aggregates top 5 farms foreach ($rawUsage as $item) { if (array_key_exists($item['farmId'], $topFarms)) { $usage->append($item); } } //Loads previous period and aggregates top 5 farms foreach ($rawPrevUsage as $item) { if (array_key_exists($item['farmId'], $topFarms)) { $prevUsage->append($item); } } } //Calculates percentage $usage->calculatePercentage(); if ($iterator->getWholePreviousPeriodEnd() != $iterator->getPreviousEnd()) { $rawPrevUsageWhole = $this->getFarmData($accountId, ['envId' => $allowedEnvs], $iterator->getPreviousStart(), $iterator->getWholePreviousPeriodEnd(), [TagEntity::TAG_ID_FARM], true); //Previous whole period usage subtotals by farm $prevUsageWhole = (new AggregationCollection(['farmId'], ['cost' => 'sum']))->load($rawPrevUsageWhole); } else { $prevUsageWhole = $prevUsage; } //Build farms total $farmsTotal = []; $it = $usage->getIterator(); foreach ($it as $farmId => $p) { $pp = isset($prevUsage['data'][$farmId]) ? $prevUsage['data'][$farmId] : null; $pw = isset($prevUsageWhole['data'][$farmId]) ? $prevUsageWhole['data'][$farmId] : null; $cl = $this->getTotalDataArray($farmId, $this->fetchFarmName($farmId), $p, $pp, $pw, [], null, true); $farmsTotal[] = $cl; } return $farmsTotal; }
/** * Gets analytics data for the specified project and period * * @param string $projectId The identifier of the Project (UUID) * @param string $mode Mode (week, month, quarter, year, custom) * @param string $startDate Start date in UTC (Y-m-d) * @param string $endDate End date in UTC (Y-m-d) * @return array Returns analytics data for the specified project and period */ public function getProjectPeriodData($projectId, $mode, $startDate, $endDate) { $analytics = $this->getContainer()->analytics; /* @var $projectEntity ProjectEntity */ $projectEntity = ProjectEntity::findPk($projectId); $ccId = $projectEntity->ccId; $utcTz = new DateTimeZone('UTC'); $currentDate = new DateTime('now', $utcTz); $iterator = new ChartPeriodIterator($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(), null, $projectId); //Interval which is used in the database query for grouping $queryInterval = preg_replace('/^1 /', '', $iterator->getInterval()); //Requests data for the specified period $rawUsage = $this->get(['projectId' => $projectId], $start, $end, [$queryInterval, TagEntity::TAG_ID_PLATFORM, TagEntity::TAG_ID_FARM], true); //Requests data for the previous period $rawPrevUsage = $this->get(['projectId' => $projectId], $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(); } $cloudsData = []; $farmsData = []; $timeline = []; $prevPointKey = null; foreach ($iterator as $chartPoint) { /* @var $chartPoint \Scalr\Stats\CostAnalytics\ChartPointInfo */ $i = $chartPoint->i; $timeline[] = ['datetime' => $chartPoint->dt->format('Y-m-d H:00'), 'label' => $chartPoint->label, 'onchart' => $chartPoint->show, 'cost' => round(isset($usgByPlatformDetailed['data'][$chartPoint->key]['cost']) ? $usgByPlatformDetailed['data'][$chartPoint->key]['cost'] : 0, 2), 'events' => isset($timelineEvents[$chartPoint->key]) ? $timelineEvents[$chartPoint->key] : null]; //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); //Projects data is empty $r['farms'] = []; $cloudsData[$platform]['data'][] = $r; } else { $cloudsData[$platform]['data'][] = null; } } } else { foreach ($usgByPlatformDetailed['data'][$chartPoint->key]['data'] 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); // Farms data is accessible by clicking on a point $r['farms'] = []; $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); //Projects data is empty $r['clouds'] = []; $farmsData[$farmId]['data'][] = $r; } else { $farmsData[$farmId]['data'][] = null; } } } else { foreach ($usgByFarmDetailed['data'][$chartPoint->key]['data'] 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 project 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' => ['envId'], 'platform'], ['cost' => 'sum']); //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->get(['projectId' => $projectId], $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); if ($it->hasChildren()) { $clFarms = []; foreach ($it->getChildren() as $farmId => $c) { $cp = isset($prevUsage['data'][$platform]['data'][$farmId]) ? $prevUsage['data'][$platform]['data'][$farmId] : null; $clFarms[] = $this->getTotalDataArray($farmId, $this->fetchFarmName($farmId), $c, $cp, null, $farmsData, $iterator, true); } $cl['farms'] = $clFarms; } else { $cl['farms'] = []; } $cloudsTotal[] = $cl; } //Build projects 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 && !empty($p['envId'])) { $cl['environment'] = ['id' => (int) $p['envId'], 'name' => AccountTagEntity::fetchName($p['envId'], TagEntity::TAG_ID_ENVIRONMENT)]; } 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; } $data = ['reportVersion' => '0.1.0', '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, 'trends' => $this->calculateSpendingTrends(['projectId' => $projectId], $timeline, $queryInterval, $iterator->getEnd()), '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($data['totals']['trends']['rollingAverageDaily']) ? $data['totals']['trends']['rollingAverageDaily'] : null); } $budgetRequest = ['projectId' => $projectId, 'usage' => $data['totals']['cost']]; if ($mode != 'custom') { //We need to get budget for the appropriate quarter $budgetRequest['period'] = $iterator->getQuarterPeriod(); } $budget = $this->getBudgetUsedPercentage($budgetRequest); $this->calculateBudgetEstimateOverspend($budget); $data['totals']['budget'] = $budget; return $data; }