/** * @test * @dataProvider providerConstructor */ public function testConstructor($mode, $start, $end, $fixture) { $iterator = new ChartPeriodIterator($mode, $start, $end); $this->assertEquals($fixture['interval'], $iterator->getInterval()); $this->assertEquals($fixture['startDate'], $iterator->getStart()->format('Y-m-d'), 'Start date does not match.'); $this->assertEquals($fixture['endDate'], $iterator->getEnd()->format('Y-m-d'), 'End date does not match'); $this->assertEquals($fixture['prevStartDate'], $iterator->getPreviousStart()->format('Y-m-d'), 'Previous period Start date does not match.'); $this->assertEquals($fixture['prevEndDate'], $iterator->getPreviousEnd()->format('Y-m-d'), 'Previous period End date does not match.'); foreach ($iterator as $chartPoint) { /* @var $chartPoint ChartPointInfo */ $this->assertTrue(isset($fixture['keys'][$chartPoint->i]), sprintf("Fixture with number %d is not expected.", $chartPoint->i)); $this->assertEquals($fixture['keys'][$chartPoint->i], $chartPoint->key, "Keys does not match."); } }
/** * Gets project properties and parameters * * @param ProjectEntity $projectEntity Project entity * @param string $calculate optional Whether response should be adjusted with cost usage data * @return array Returns cost centre properties and parameters */ private function getProjectData(ProjectEntity $projectEntity, $calculate = false) { $ret = array('ccId' => $projectEntity->ccId, 'ccName' => $projectEntity->getCostCenter() !== null ? $projectEntity->getCostCenter()->name : null, 'projectId' => $projectEntity->projectId, 'name' => $projectEntity->name, 'billingCode' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_BILLING_CODE), 'description' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_DESCRIPTION), 'leadEmail' => $projectEntity->getProperty(ProjectPropertyEntity::NAME_LEAD_EMAIL), 'created' => $projectEntity->created->format('Y-m-d'), 'createdByEmail' => $projectEntity->createdByEmail, 'archived' => $projectEntity->archived, 'farmsCount' => count($projectEntity->getFarmsList())); if ($calculate) { $iterator = new ChartPeriodIterator('month', gmdate('Y-m-01'), null, 'UTC'); $usage = $this->getContainer()->analytics->usage->get(['projectId' => $ret['projectId']], $iterator->getStart(), $iterator->getEnd()); //It calculates usage for previous period same days $prevusage = $this->getContainer()->analytics->usage->get(['projectId' => $ret['projectId']], $iterator->getPreviousStart(), $iterator->getPreviousEnd()); //Calclulates usage for previous whole period if ($iterator->getPreviousEnd() != $iterator->getWholePreviousPeriodEnd()) { $prevWholePeriodUsage = $this->getContainer()->analytics->usage->get(['projectId' => $ret['projectId']], $iterator->getPreviousStart(), $iterator->getWholePreviousPeriodEnd()); } else { $prevWholePeriodUsage = $prevusage; } $ret = $this->getWrappedUsageData(['ccId' => $ret['ccId'], 'projectId' => $ret['projectId'], 'iterator' => $iterator, 'usage' => $usage['cost'], 'prevusage' => $prevusage['cost'], 'prevusagewhole' => $prevWholePeriodUsage['cost']]) + $ret; } return $ret; }
/** * Constructor * * @param ChartPeriodIterator $iterator The iterator * @throws \InvalidArgumentException */ public function __construct(ChartPeriodIterator $iterator) { $this->mode = $iterator->getMode(); $this->dt = $iterator->getIterationTimestamp(); $this->interval = $iterator->getInterval(); $this->i = $iterator->getIterationNumber(); $prevStart = $iterator->getPreviousStart(); $previousPeriodDt = clone $this->dt; $previousPeriodDt->sub($iterator->getPreviousPeriodInterval()); $this->start = $iterator->getStart(); $this->end = $iterator->getEnd(); $this->isLastPoint = $iterator->isLastPoint(); if ($this->mode == 'year' || $this->mode == 'custom' && $this->interval == '1 month') { $this->show = $this->label = $this->dt->format('M'); $this->key = $this->dt->format('Y-m'); $this->previousPeriodKey = $previousPeriodDt->format('Y-m'); } elseif ($this->mode == 'quarter' || $this->mode == 'custom' && $this->interval == '1 week') { $ddt = clone $this->dt; $ddt->modify('next saturday'); if ($ddt > $this->end) { $ddt = $this->end; } $this->label = $this->dt->format('M j') . ' - ' . $ddt->format('M j'); $this->key = \Scalr_Util_DateTime::yearweek($this->dt->format('Y-m-d')); $this->previousPeriodKey = \Scalr_Util_DateTime::yearweek($previousPeriodDt->format('Y-m-d')); $this->show = $this->i % 3 == 0 ? $this->dt->format('M j') : ($this->isLastPoint && $this->i % 3 > 1 ? $ddt->format('M j') : ''); } elseif ($this->mode == 'week') { $this->label = $this->dt->format('l, M j'); $this->show = $this->dt->format('M j'); $this->key = $this->dt->format('Y-m-d'); $this->previousPeriodKey = $previousPeriodDt->format('Y-m-d'); } elseif ($this->mode == 'month' || $this->mode == 'custom' && $this->interval == '1 day') { $this->label = $this->dt->format('M j'); $this->key = $this->dt->format('Y-m-d'); $this->previousPeriodKey = $previousPeriodDt->format('Y-m-d'); $this->show = $this->i % 4 == 0 || $this->isLastPoint && $this->i % 4 > 2 ? $this->dt->format('M j') : ''; } elseif ($this->mode == 'custom') { switch ($this->interval) { case '1 hour': $h = $this->dt->format('H'); $this->label = $this->dt->format('l, M j, g A'); $this->show = $h == 0 ? $this->dt->format('M j') : ($h % 3 == 0 ? $this->dt->format('g a') : ''); $this->key = $this->dt->format("Y-m-d H:00:00"); $this->previousPeriodKey = $previousPeriodDt->format('Y-m-d H:00:00'); break; case '1 quarter': //Quarter breakdown is not supported yet $quarters = new Quarters(SettingEntity::getQuarters()); $currentPeriod = $quarters->getPeriodForDate($this->start); $prevPeriod = $quarters->getPeriodForDate($prevStart); $this->show = $this->label = $currentPeriod->year . ' Q' . $currentPeriod->quarter; $this->key = $currentPeriod->year . '-' . $currentPeriod->quarter; $this->previousPeriodKey = $prevPeriod->year . '-' . $prevPeriod->quarter; break; case '1 year': $this->show = $this->label = $this->dt->format('Y'); $this->key = $this->label; $this->previousPeriodKey = $previousPeriodDt->format('Y'); break; default: throw new \InvalidArgumentException(sprintf('Unsupported interval for custom mode %s.', $this->interval)); break; } } else { throw new \InvalidArgumentException(sprintf('Invalid mode %s', strip_tags($this->mode))); } }
/** * Gets detailed top 5 usage by farms for specified project on date * * @param string|null $projectId The identifier of the project * @param string $platform The cloud platform * @param string $mode The mode * @param string $date The UTC date within period ('Y-m-d H:00') * @param string $start The start date of the period in UTC ('Y-m-d') * @param string $end The end date of the period in UTC ('Y-m-d') * @param string $ccId optional The identifier of the cost center (It is used only when project is null) * @return array Returns detailed top 5 usage by farms for specified project on date * @throws AnalyticsException * @throws OutOfBoundsException */ public function getProjectFarmsTopUsageOnDate($projectId, $platform, $mode, $date, $start, $end, $ccId = null) { $projectId = empty($projectId) ? null : $projectId; $iterator = new ChartPeriodIterator($mode, $start, $end ?: null, 'UTC'); //Interval which is used in the database query for grouping $queryInterval = preg_replace('/^1 /', '', $iterator->getInterval()); if ($projectId !== null) { $project = ProjectEntity::findPk($projectId); if ($project === null) { if (empty($ccId)) { throw new AnalyticsException(sprintf("Project %s does not exist. Please provide ccId.", $projectId)); } } else { $ccId = $project->ccId; } } //Requests data for the specified period $rawUsage = $this->get(['projectId' => $projectId], $iterator->getStart(), $iterator->getEnd(), [$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); //We do not need to calculate the percentage here $usg = (new AggregationCollection(['period', 'platform', 'farmId' => ['envId']], ['cost' => 'sum']))->load($rawUsage); $prevUsg = (new AggregationCollection(['period', 'platform', 'farmId'], ['cost' => 'sum']))->load($rawPrevUsage)->calculatePercentage(); //Previous chart point $prevcp = null; //Finds the key for current label foreach ($iterator as $chartPoint) { //FIXME rewrite search a point if ($chartPoint->dt->format('Y-m-d H:00') !== $date) { $prevcp = $chartPoint; continue; } $cp = $chartPoint; break; } if (!isset($cp)) { throw new OutOfRangeException(sprintf('Requested date (%s) is out of the range. Last point date is %s', $date, isset($prevcp->dt) ? $prevcp->dt->format('Y-m-d H:00') : 'undefined')); } $result = []; //Maximum number of the farms without grouping $max = 5; $sEverything = self::EVERYTHING_ELSE; if (!empty($usg['data'][$cp->key]['data'][$platform]['data'])) { $usgFarms = new AggregationCollection(['farmId' => ['envId']], ['cost' => 'sum']); $ptr = $usg['data'][$cp->key]['data'][$platform]['data']; uasort($ptr, function ($a, $b) { if ($a['cost'] == $b['cost']) { return 0; } return $a['cost'] > $b['cost'] ? -1 : 1; }); //Aggregates farms if its number more then max + 1 if (count($ptr) > $max + 1) { $this->otherFarmsQuantity = count($ptr) - $max; $new = []; $i = 0; foreach ($ptr as $farmId => $v) { $v['cost_percentage'] = round($usg['data'][$cp->key]['data'][$platform]['cost'] == 0 ? 0 : $v['cost'] * 100 / $usg['data'][$cp->key]['data'][$platform]['cost'], 0); if ($i < $max) { $new[$farmId] = $v; } elseif (!isset($new[self::EVERYTHING_ELSE])) { $v['id'] = self::EVERYTHING_ELSE; $new[self::EVERYTHING_ELSE] = $v; } else { $new[self::EVERYTHING_ELSE]['cost'] += $v['cost']; } $i++; } $new[self::EVERYTHING_ELSE]['cost_percentage'] = round($usg['data'][$cp->key]['data'][$platform]['cost'] == 0 ? 0 : $new[self::EVERYTHING_ELSE]['cost'] * 100 / $usg['data'][$cp->key]['data'][$platform]['cost'], 0); $usgFarms->setArray(['data' => $new]); } else { $usgFarms->setArray($usg['data'][$cp->key]['data'][$platform])->calculatePercentage(); } //Forms result data array foreach ($usgFarms->getIterator() as $farmId => $pv) { $record = $this->getDetailedPointDataArray($farmId, $this->fetchFarmName($farmId), $pv, isset($prevUsg['data'][$cp->previousPeriodKey]['data'][$platform]['data'][$farmId]) ? $prevUsg['data'][$cp->previousPeriodKey]['data'][$platform]['data'][$farmId] : null, isset($usg['data'][$cp->key]['data'][$platform]['data'][$farmId]) ? $usg['data'][$cp->key]['data'][$platform]['data'][$farmId] : null); if ($farmId && $farmId != self::EVERYTHING_ELSE && !empty($pv['envId'])) { $record['environment'] = ['id' => (int) $pv['envId'], 'name' => AccountTagEntity::fetchName($pv['envId'], TagEntity::TAG_ID_ENVIRONMENT)]; } $result[] = $record; } } return ['data' => $result]; }