コード例 #1
0
ファイル: Usage.php プロジェクト: sacredwebsite/scalr
 /**
  * Gets cost analytics for environment scope
  *
  * @param   Scalr_Environment  $env       Current environment
  * @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')
  * @return  array     Returns cost analytics data for environment scope
  */
 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, 'distributionType', 'usageType', 'usageItem'], 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, 'distributionType', 'usageType', 'usageItem'], 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();
     $usgDetailed = new AggregationCollection(['period', 'farmId', 'distributionType', 'usageType' => ['name', 'displayName'], 'usageItem' => ['envId', 'platform', 'cloudLocation', 'id']], ['cost' => 'sum', 'minUsage' => 'min', 'maxUsage' => 'max', 'usageHours' => 'sum', 'workingHours' => 'sum']);
     $usgPrevDetailed = new AggregationCollection(['period', 'farmId', 'distributionType', 'usageType', 'usageItem'], ['cost' => 'sum', 'minUsage' => 'min', 'maxUsage' => 'max', 'usageHours' => 'sum', 'workingHours' => 'sum']);
     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();
         $usgDetailed->load($rawUsage)->calculatePercentage();
         $usgPrevDetailed->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);
             $usgDetailed->append($d);
         }
         $usgByFarmDetailed->calculatePercentage();
         $usgDetailed->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);
             $usgPrevDetailed->append($d);
         }
         $usgByFarmPrevDetailed->calculatePercentage();
         $usgPrevDetailed->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;
                     $r['distributionTypes'] = $this->getDistributionTypesData($chartPoint, $prevPointKey, $usgDetailed, $usgPrevDetailed, $farmId);
                     $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($rawUsage)->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']);
     //Previous period subtotals by distr types
     $prevUsage3 = (new AggregationCollection(['distributionType'], ['cost' => 'sum']))->load($rawPrevUsage)->calculatePercentage();
     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 = [];
     $it = $usage3->getIterator();
     foreach ($it as $distributionType => $costUsage) {
         $pp = isset($prevUsage3['data'][$distributionType]) ? $prevUsage3['data'][$distributionType] : null;
         $distributionTypeDataTotal = $this->getTotalDataArray($distributionType, $distributionType, $costUsage, $pp, null, [], null, true);
         $distributionTypesTotal[] = $distributionTypeDataTotal;
     }
     $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;
 }