Scalr\Stats\CostAnalytics\Usage::getCostCenterPeriodData PHP Method

getCostCenterPeriodData() public method

Gets analytics data for the cost center
public getCostCenterPeriodData ( string $ccId, string $mode, string $startDate, string $endDate ) : array
$ccId string The identifier of the cost center (UUID)
$mode string Mode (week, month, quarter, year, custom)
$startDate string Start date in UTC (Y-m-d)
$endDate string End date in UTC (Y-m-d)
return array Returns analytics data for the specified cost center
    public function getCostCenterPeriodData($ccId, $mode, $startDate, $endDate)
    {
        $analytics = $this->getContainer()->analytics;
        $utcTz = new DateTimeZone('UTC');
        $iterator = ChartPeriodIterator::create($mode, new DateTime($startDate, $utcTz), new DateTime($endDate, $utcTz), 'UTC');
        $timelineEvents = $analytics->events->count($iterator->getInterval(), $iterator->getStart(), $iterator->getEnd(), ['ccId' => $ccId]);
        //Interval which is used in the database query for grouping
        $queryInterval = preg_replace('/^1 /', '', $iterator->getInterval());
        //Current period data
        $collectionSet = (new AggregationCollectionSet(['byPlatformDetailed' => new AggregationCollection(['period', 'platform', 'projectId'], ['cost' => 'sum']), 'byProjectDetailed' => new AggregationCollection(['period', 'projectId', 'platform'], ['cost' => 'sum']), 'byPlatform' => new AggregationCollection(['platform', 'projectId'], ['cost' => 'sum']), 'byProject' => new AggregationCollection(['projectId', 'platform'], ['cost' => 'sum'])]))->load($this->get(['ccId' => $ccId], $iterator->getStart(), $iterator->getEnd(), [$queryInterval, TagEntity::TAG_ID_PLATFORM, TagEntity::TAG_ID_PROJECT], true))->calculatePercentage();
        //Previous period data
        $collectionSetPrev = (new AggregationCollectionSet(['byPlatformDetailed' => new AggregationCollection(['period', 'platform', 'projectId'], ['cost' => 'sum']), 'byProjectDetailed' => new AggregationCollection(['period', 'projectId', 'platform'], ['cost' => 'sum']), 'byPlatform' => new AggregationCollection(['platform', 'projectId'], ['cost' => 'sum']), 'byProject' => new AggregationCollection(['projectId', 'platform'], ['cost' => 'sum'])]))->load($this->get(['ccId' => $ccId], $iterator->getPreviousStart(), $iterator->getPreviousEnd(), [$queryInterval, TagEntity::TAG_ID_PLATFORM, TagEntity::TAG_ID_PROJECT], true))->calculatePercentage();
        $quarterIterator = $this->getCurrentQuarterIterator();
        $queryQuarterInterval = preg_replace('/^1 /', '', $quarterIterator->getInterval());
        $rawQuarterUsage = $this->get(['ccId' => $ccId], $quarterIterator->getStart(), $quarterIterator->getEnd(), [TagEntity::TAG_ID_PLATFORM, TagEntity::TAG_ID_PROJECT], true);
        $itemsRollingAvg = $this->getRollingAvg(['ccId' => $ccId], $queryQuarterInterval, $quarterIterator->getEnd(), null, $rawQuarterUsage, ['projectId' => 'projects', 'platform' => 'clouds']);
        //Gets the list of the projects which is assigned to cost center at current moment to optimize
        //retrieving its names with one query
        $projects = new ArrayCollection();
        foreach (ProjectEntity::findByCcId($ccId) as $i) {
            $projects[$i->projectId] = $i;
        }
        //Function retrieves the name of the project by specified identifier
        $fnGetProjectName = function ($projectId) use($projects) {
            if (empty($projectId)) {
                $projectName = 'Unassigned resources';
            } else {
                if (!isset($projects[$projectId])) {
                    //Trying to find the name of the project in the tag values history
                    if (null === ($pe = AccountTagEntity::findOne([['tagId' => TagEntity::TAG_ID_PROJECT], ['valueId' => $projectId]]))) {
                        $projectName = $projectId;
                    } else {
                        $projectName = $pe->valueName;
                        unset($pe);
                    }
                } else {
                    $projectName = $projects[$projectId]->name;
                }
            }
            return $projectName;
        };
        $cloudsData = [];
        $projectsData = [];
        $timeline = [];
        $prevPointKey = null;
        foreach ($iterator as $chartPoint) {
            /* @var $chartPoint \Scalr\Stats\CostAnalytics\ChartPointInfo */
            $i = $chartPoint->i;
            $timeline[] = array('datetime' => $chartPoint->dt->format('Y-m-d H:00'), 'label' => $chartPoint->label, 'onchart' => $chartPoint->show, 'cost' => round(isset($collectionSet['byPlatformDetailed']['data'][$chartPoint->key]['cost']) ? $collectionSet['byPlatformDetailed']['data'][$chartPoint->key]['cost'] : 0, 2), 'events' => isset($timelineEvents[$chartPoint->key]) ? $timelineEvents[$chartPoint->key] : null);
            //Period - Platform - Projects subtotals
            if (!isset($collectionSet['byPlatformDetailed']['data'][$chartPoint->key]['data'])) {
                foreach ($cloudsData as $platform => $v) {
                    if (!$iterator->isFuture()) {
                        //Previous period details
                        if (isset($collectionSetPrev['byPlatformDetailed']['data'][$chartPoint->previousPeriodKey]['data'][$platform])) {
                            $pp = $collectionSetPrev['byPlatformDetailed']['data'][$chartPoint->previousPeriodKey]['data'][$platform];
                        } else {
                            $pp = null;
                        }
                        //Previous point details
                        if (isset($collectionSet['byPlatformDetailed']['data'][$prevPointKey]['data'][$platform])) {
                            $ppt = $collectionSet['byPlatformDetailed']['data'][$prevPointKey]['data'][$platform];
                        } else {
                            $ppt = null;
                        }
                        $r = $this->getPointDataArray(null, $pp, $ppt);
                        //Projects data is empty
                        $r['projects'] = [];
                        $cloudsData[$platform]['data'][] = $r;
                    } else {
                        $cloudsData[$platform]['data'][] = null;
                    }
                }
            } else {
                //Initializes with empty values to prevent data shifts on charts.
                if (!isset($collectionSet['byPlatformDetailed']['data'][$chartPoint->key]['data'])) {
                    $collectionSet['byPlatformDetailed']['data'][$chartPoint->key]['data'] = [];
                }
                $combined =& $collectionSet['byPlatformDetailed']['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($collectionSetPrev['byPlatformDetailed']['data'][$chartPoint->previousPeriodKey]['data'][$platform])) {
                        $pp = $collectionSetPrev['byPlatformDetailed']['data'][$chartPoint->previousPeriodKey]['data'][$platform];
                    } else {
                        $pp = null;
                    }
                    //Previous point details
                    if (isset($collectionSet['byPlatformDetailed']['data'][$prevPointKey]['data'][$platform])) {
                        $ppt = $collectionSet['byPlatformDetailed']['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);
                        // projects data
                        $cloudProjectData = [];
                        if (!empty($v['data'])) {
                            foreach ($v['data'] as $projectId => $pv) {
                                if (isset($pp['data'][$projectId])) {
                                    $ppp = $pp['data'][$projectId];
                                } else {
                                    $ppp = null;
                                }
                                if (isset($ppt['data'][$projectId])) {
                                    $pppt = $ppt['data'][$projectId];
                                } else {
                                    $pppt = null;
                                }
                                $cloudProjectData[] = $this->getDetailedPointDataArray($projectId, $fnGetProjectName($projectId), $pv, $ppp, $pppt);
                            }
                        }
                        $r['projects'] = $cloudProjectData;
                        $cloudsData[$platform]['name'] = $platform;
                        $cloudsData[$platform]['data'][] = $r;
                    } else {
                        $cloudsData[$platform]['data'][] = null;
                    }
                }
            }
            //Period - Project - Platform subtotal
            if (!isset($collectionSet['byProjectDetailed']['data'][$chartPoint->key]['data'])) {
                foreach ($projectsData as $projectId => $v) {
                    if (!$iterator->isFuture()) {
                        //Previous period details
                        if (isset($collectionSetPrev['byProjectDetailed']['data'][$chartPoint->previousPeriodKey]['data'][$projectId])) {
                            $pp = $collectionSetPrev['byProjectDetailed']['data'][$chartPoint->previousPeriodKey]['data'][$projectId];
                        } else {
                            $pp = null;
                        }
                        //Previous point details
                        if (isset($collectionSet['byProjectDetailed']['data'][$prevPointKey]['data'][$projectId])) {
                            $ppt = $collectionSet['byProjectDetailed']['data'][$prevPointKey]['data'][$projectId];
                        } else {
                            $ppt = null;
                        }
                        $r = $this->getPointDataArray(null, $pp, $ppt);
                        //Projects data is empty
                        $r['clouds'] = [];
                        $projectsData[$projectId]['data'][] = $r;
                    } else {
                        $projectsData[$projectId]['data'][] = null;
                    }
                }
            } else {
                //Initializes with empty values to prevent data shifts on charts.
                if (!isset($collectionSet['byProjectDetailed']['data'][$chartPoint->key]['data'])) {
                    $collectionSet['byProjectDetailed']['data'][$chartPoint->key]['data'] = [];
                }
                $combined =& $collectionSet['byProjectDetailed']['data'][$chartPoint->key]['data'];
                if (!empty($projectsData)) {
                    foreach ($projectsData as $projectId => $t) {
                        if (!array_key_exists($projectId, $combined)) {
                            $combined[$projectId] = [];
                        }
                    }
                }
                foreach ($combined as $projectId => $v) {
                    //Previous period details
                    if (isset($collectionSetPrev['byProjectDetailed']['data'][$chartPoint->previousPeriodKey]['data'][$projectId])) {
                        $pp = $collectionSetPrev['byProjectDetailed']['data'][$chartPoint->previousPeriodKey]['data'][$projectId];
                    } else {
                        $pp = null;
                    }
                    //Previous point details
                    if (isset($collectionSet['byProjectDetailed']['data'][$prevPointKey]['data'][$projectId])) {
                        $ppt = $collectionSet['byProjectDetailed']['data'][$prevPointKey]['data'][$projectId];
                    } else {
                        $ppt = null;
                    }
                    if (!isset($projectsData[$projectId]) && $i > 0) {
                        $projectsData[$projectId]['name'] = $fnGetProjectName($projectId);
                        //initializes project legend for the not filled period
                        $projectsData[$projectId]['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) {
                                if (isset($pp['data'][$platform])) {
                                    $ppp = $pp['data'][$platform];
                                } else {
                                    $ppp = null;
                                }
                                if (isset($ppt['data'][$platform])) {
                                    $pppt = $ppt['data'][$platform];
                                } else {
                                    $pppt = null;
                                }
                                $cloudPlatformData[] = $this->getDetailedPointDataArray($platform, $platform, $pv, $ppp, $pppt);
                            }
                        }
                        $r['clouds'] = $cloudPlatformData;
                        $projectsData[$projectId]['name'] = $fnGetProjectName($projectId);
                        $projectsData[$projectId]['data'][] = $r;
                    } else {
                        $projectsData[$projectId]['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 ($projectsData as $projectId => $v) {
            if (($j = count($v['data'])) < $cntpoints) {
                while ($j < $cntpoints) {
                    $projectsData[$projectId]['data'][] = null;
                    $j++;
                }
            }
        }
        //Spending trends uses daily usage precalculated data
        $trends = $this->calculateSpendingTrends(['ccId' => $ccId], $timeline, $queryInterval, $iterator->getEnd());
        if ($iterator->getWholePreviousPeriodEnd() != $iterator->getPreviousEnd()) {
            $rawPrevUsageWhole = $this->get(['ccId' => $ccId], $iterator->getPreviousStart(), $iterator->getWholePreviousPeriodEnd(), [TagEntity::TAG_ID_PLATFORM, TagEntity::TAG_ID_PROJECT], true);
            //Previous whole period usage subtotals by platform
            $prevUsageWhole = (new AggregationCollection(['platform'], ['cost' => 'sum']))->load($rawPrevUsageWhole);
            //Previous whole period usage subtotals by project
            $prevUsageWhole2 = (new AggregationCollection(['projectId'], ['cost' => 'sum']))->load($rawPrevUsageWhole);
        } else {
            $prevUsageWhole = $collectionSetPrev['byPlatform'];
            $prevUsageWhole2 = $collectionSetPrev['byProject'];
        }
        //Build cloud platforms total
        $cloudsTotal = [];
        $it = $collectionSet['byPlatform']->getIterator();
        foreach ($it as $platform => $p) {
            $pp = isset($collectionSetPrev['byPlatform']['data'][$platform]) ? $collectionSetPrev['byPlatform']['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()) {
                $clProjects = [];
                foreach ($it->getChildren() as $projectId => $c) {
                    $cp = isset($collectionSetPrev['byPlatform']['data'][$platform]['data'][$projectId]) ? $collectionSetPrev['byPlatform']['data'][$platform]['data'][$projectId] : null;
                    $clProjects[] = $this->getTotalDataArray($projectId, $fnGetProjectName($projectId), $c, $cp, null, $projectsData, $iterator, true);
                }
                $cl['projects'] = $clProjects;
            } else {
                $cl['projects'] = [];
            }
            $cloudsTotal[] = $cl;
        }
        //Build projects total
        $projectsTotal = [];
        $it = $collectionSet['byProject']->getIterator();
        //For each assigned project wich is not archived we should display
        //zero dollar spend even if there are not any spend for
        //the selected period.
        $projectsWithoutSpend = [];
        foreach ($projects as $projectEntity) {
            /* @var $projectEntity \Scalr\Stats\CostAnalytics\Entity\ProjectEntity */
            if ($projectEntity->archived) {
                continue;
            }
            if (!isset($collectionSet['byProject']['data'][$projectEntity->projectId])) {
                $projectsWithoutSpend[$projectEntity->projectId] = ['cost' => 0, 'cost_percentage' => 0, 'id' => $projectEntity->projectId];
            }
        }
        //Passing projects with spend and then assigned projects without spend for the selected period
        foreach ([$it, $projectsWithoutSpend] as $internalIterator) {
            foreach ($internalIterator as $projectId => $p) {
                $pp = isset($collectionSetPrev['byProject']['data'][$projectId]) ? $collectionSetPrev['byProject']['data'][$projectId] : null;
                $pw = isset($prevUsageWhole2['data'][$projectId]) ? $prevUsageWhole2['data'][$projectId] : null;
                $cl = $this->getTotalDataArray($projectId, $fnGetProjectName($projectId), $p, $pp, $pw, $projectsData, $iterator);
                if ($internalIterator instanceof ArrayIterator && $internalIterator->hasChildren()) {
                    $clPlatforms = [];
                    foreach ($internalIterator->getChildren() as $platform => $c) {
                        $cp = isset($collectionSetPrev['byProject']['data'][$projectId]['data'][$platform]) ? $collectionSetPrev['byProject']['data'][$projectId]['data'][$platform] : null;
                        $clPlatforms[] = $this->getTotalDataArray($platform, $platform, $c, $cp, null, $cloudsData, $iterator, true);
                    }
                    $cl['clouds'] = $clPlatforms;
                } else {
                    $cl['clouds'] = [];
                }
                $projectsTotal[] = $cl;
            }
        }
        $data = ['reportVersion' => '0.1.0', 'totals' => ['cost' => round($collectionSet['byPlatform']['cost'], 2), 'prevCost' => round($collectionSetPrev['byPlatform']['cost'], 2), 'growth' => round($collectionSet['byPlatform']['cost'] - $collectionSetPrev['byPlatform']['cost'], 2), 'growthPct' => $collectionSetPrev['byPlatform']['cost'] == 0 ? null : round(abs(($collectionSet['byPlatform']['cost'] - $collectionSetPrev['byPlatform']['cost']) / $collectionSetPrev['byPlatform']['cost'] * 100), 0), 'clouds' => $cloudsTotal, 'projects' => $projectsTotal, 'trends' => $trends, 'forecastCost' => null], 'timeline' => $timeline, 'clouds' => $cloudsData, 'projects' => $projectsData, '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()) {
            $data['totals']['forecastCost'] = self::calculateForecast($data['totals']['cost'], $iterator->getStart(), $iterator->getEnd(), $prevUsageWhole['cost'], ($data['totals']['growth'] >= 0 ? 1 : -1) * $data['totals']['growthPct'], isset($itemsRollingAvg['rollingAverageDaily']) ? $itemsRollingAvg['rollingAverageDaily'] : null);
        }
        $budgetRequest = ['ccId' => $ccId, '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;
    }