/**
  * @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.");
     }
 }
Example #2
0
 /**
  * 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;
 }
Example #3
0
 /**
  * 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)));
     }
 }
Example #4
0
 /**
  * Gets total data array
  *
  * @param   string              $id                  The identifier of the subject
  * @param   string              $name                The name of the subject
  * @param   array               $currentPeriod
  * @param   array               $previousPeriod
  * @param   array               $previousWholePeriod
  * @param   array               $detailed            Details by each point on chart
  * @param   ChartPeriodIterator $iterator            optional Iterator is needed when it returns long form of array
  * @param   bool                $bshort              optional Whether it should return short form of array
  * @return  array               Returns data array
  */
 public function getTotalDataArray($id, $name, $currentPeriod, $previousPeriod, $previousWholePeriod, $detailed, ChartPeriodIterator $iterator = null, $bshort = false)
 {
     $cl = ['id' => $id, 'name' => $name, 'cost' => isset($currentPeriod['cost']) ? round($currentPeriod['cost'], 2) : 0, 'costPct' => isset($currentPeriod['cost_percentage']) ? $currentPeriod['cost_percentage'] : 0, 'prevCost' => isset($previousPeriod['cost']) ? round($previousPeriod['cost'], 2) : 0, 'prevCostPct' => isset($previousPeriod['cost_percentage']) ? $previousPeriod['cost_percentage'] : 0];
     $cl['growth'] = $cl['cost'] - $cl['prevCost'];
     // growth
     $cl['growthPct'] = $cl['prevCost'] == 0 ? null : round(abs($cl['growth'] / $cl['prevCost'] * 100), 0);
     //growth percentage
     //short form of the data array
     if ($bshort) {
         return $cl;
     }
     // forecasted spend for period
     $cl['forecastCost'] = self::calculateForecast($cl['cost'], $iterator->getStart(), $iterator->getEnd(), $previousWholePeriod['cost'], ($cl['growth'] > 0 ? 1 : -1) * $cl['growthPct']);
     $mediandata = [];
     if (!empty($detailed[$id]['data'])) {
         $dt = $iterator->getStart();
         //FIXME rewrite searching a point
         foreach ($detailed[$id]['data'] as $i => $v) {
             if ($dt > $iterator->today) {
                 break;
             }
             $mediandata[] = !isset($v['cost']) ? 0 : $v['cost'];
             if ($iterator->getInterval() == '1 week') {
                 $dt->modify('next sunday');
             } else {
                 $dt->add($iterator->getIterationInterval());
             }
         }
     }
     $cl['median'] = empty($detailed[$id]['data']) ? 0 : round((double) Scalr_Util_Arrays::median($mediandata), 2);
     $cl['averageCost'] = count($mediandata) ? round(array_sum($mediandata) / count($mediandata), 2) : 0;
     // percentage difference between current and previous period
     $cl['curPrevPctGrowth'] = $cl['costPct'] - $cl['prevCostPct'];
     return $cl;
 }
Example #5
0
 /**
  * Constructor
  *
  * @param   ChartPeriodIterator $iterator  The iterator
  */
 public function __construct(ChartPeriodIterator $iterator)
 {
     $this->mode = $iterator->getMode();
     $this->dt = $iterator->getIterationTimestamp();
     $this->interval = $iterator->getInterval();
     $this->i = $iterator->getIterationNumber();
     $this->start = $iterator->getStart();
     $this->end = $iterator->getEnd();
     $this->isLastPoint = $iterator->isLastPoint();
 }
Example #6
0
 /**
  * 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];
 }