public function getContent($params = array()) { $this->request->restrictAccess(Acl::RESOURCE_OWN_FARMS, Acl::PERM_FARMS_CREATE); $projects = []; if ($this->getContainer()->analytics->enabled && $this->getEnvironment()->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID)) { $costCenter = $this->getContainer()->analytics->ccs->get($this->getEnvironment()->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID)); $currentYear = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y'); $quarters = new Quarters(SettingEntity::getQuarters()); $currentQuarter = $quarters->getQuarterForDate(new \DateTime('now', new \DateTimeZone('UTC'))); if ($costCenter instanceof CostCentreEntity) { $projectsIterator = new SharedProjectsFilterIterator($costCenter->getProjects(), $costCenter->ccId, $this->user, $this->getEnvironment()); foreach ($projectsIterator as $item) { $quarterBudget = QuarterlyBudgetEntity::findOne([['year' => $currentYear], ['subjectType' => QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT], ['subjectId' => $item->projectId], ['quarter' => $currentQuarter]]); $projects[] = array('projectId' => $item->projectId, 'name' => $item->name, 'budgetRemain' => !is_null($quarterBudget) && $quarterBudget->budget > 0 ? max(0, round($quarterBudget->budget - $quarterBudget->cumulativespend)) : null); } //$costCentreName = $costCenter->name; $isLocked = $costCenter->getProperty(CostCentrePropertyEntity::NAME_LOCKED); $accountCcs = AccountCostCenterEntity::findOne([['accountId' => $this->environment->clientId], ['ccId' => $costCenter->ccId]]); if ($isLocked || !$accountCcs instanceof AccountCostCenterEntity) { $costCentreLocked = 1; } else { $costCentreLocked = 0; } } else { $costCentreName = ''; $costCentreLocked = 0; } } return ['costCenterLocked' => $costCentreLocked, 'projects' => $projects]; }
protected function run1($stage) { $this->console->out('Initializing quarterly_budget data...'); $quarters = new Quarters(SettingEntity::getQuarters()); $currentYearPeriod = $quarters->getPeriodForDate(); $currentFiscalYear = $currentYearPeriod->year; $prevFiscalYear = $currentFiscalYear - 1; $this->db->Execute("\n UPDATE quarterly_budget b\n SET b.`cumulativespend` = 0.000000\n WHERE b.`year` = ? OR b.`year` = ?\n ", [$currentFiscalYear, $prevFiscalYear]); foreach ([$prevFiscalYear, $currentFiscalYear] as $year) { for ($quarter = 1; $quarter <= 4; $quarter++) { $quarterPeriod = $quarters->getPeriodForQuarter($quarter, $year); $this->db->Execute("\n INSERT INTO quarterly_budget (`year`, `quarter`, `subject_type`, `subject_id`, `cumulativespend`)\n SELECT ?, u.`quarter`, u.`subject_type`, u.`subject_id`, u.`cumulativespend`\n FROM (\n SELECT ? AS `quarter`, ? AS `subject_type`, `cc_id` AS `subject_id`, SUM(`cost`) AS `cumulativespend`\n FROM usage_d\n WHERE `date` BETWEEN ? AND ?\n GROUP BY `cc_id`\n UNION ALL\n SELECT ? AS `quarter`, ? AS `subject_type`, `project_id` AS `subject_id`, SUM(`cost`) AS `cumulativespend`\n FROM usage_d\n WHERE `date` BETWEEN ? AND ?\n GROUP BY `project_id`\n ) AS u\n ON DUPLICATE KEY UPDATE\n `cumulativespend` = u.`cumulativespend`\n ", [$year, $quarter, QuarterlyBudgetEntity::SUBJECT_TYPE_CC, $quarterPeriod->start->format('Y-m-d'), $quarterPeriod->end->format('Y-m-d'), $quarter, QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT, $quarterPeriod->start->format('Y-m-d'), $quarterPeriod->end->format('Y-m-d')]); $this->db->Execute("\n UPDATE quarterly_budget b\n JOIN (\n (SELECT SUM(`cost`) AS `cost`, `date`, `cc_id` AS `subject_id`\n FROM usage_d\n WHERE `date` BETWEEN ? AND ?\n GROUP BY `cc_id`, `date`)\n UNION ALL\n (SELECT SUM(`cost`) AS `cost`, `date`, `project_id` AS `subject_id`\n FROM usage_d\n WHERE `date` BETWEEN ? AND ?\n GROUP BY `project_id`, `date`)\n ORDER BY `date`\n ) AS ud ON b.subject_id = ud.`subject_id` AND b.`year` = ?\n SET b.`spentondate` = IF (\n b.`budget` > 0 AND b.`spentondate` IS NULL AND ud.`cost` >= b.`budget`,\n ud.`date`,\n b.`spentondate`\n )\n WHERE b.`quarter` = ?\n ", [$quarterPeriod->start->format('Y-m-d'), $quarterPeriod->end->format('Y-m-d'), $quarterPeriod->start->format('Y-m-d'), $quarterPeriod->end->format('Y-m-d'), $year, $quarter]); } } }
/** * @test * @dataProvider providerConstructor */ public function testConstructor($days, $fixtures) { $quarters = new Quarters($days); foreach ($fixtures as $i => $v) { $this->assertEquals($v['quarter'], $quarters->getQuarterForDate($v['date']), sprintf('The number of the quarter for the date "%s" is expected to be %d.', $v['date'], $v['quarter'])); $period = $quarters->getPeriodForQuarter($v['quarter'], $v['year']); $this->assertInternalType('object', $period); $this->assertEquals($v['start'], $period->start->format('Y-m-d'), sprintf('Start date is expected to be "%s".', $v['start'])); $this->assertEquals($v['end'], $period->end->format('Y-m-d'), sprintf('End date is expected to be "%s".', $v['end'])); $this->assertEquals($v['year'], $period->year); $this->assertEquals($v['quarter'], $period->quarter); $periodForDate = $quarters->getPeriodForDate(new DateTime($v['date'], new DateTimeZone('UTC'))); $this->assertEquals($v['start'], $periodForDate->start->format('Y-m-d'), sprintf('getPeriodForDate for fixture#%d failed. Start date is expected to be "%s".', $i, $v['start'])); $this->assertEquals($v['end'], $periodForDate->end->format('Y-m-d'), sprintf('getPeriodForDate for fixture#%d failed. End date is expected to be "%s".', $i, $v['end'])); $this->assertEquals($v['year'], $periodForDate->year, sprintf('getPeriodForDate for fixture#%d failed. Year is expected to be "%s".', $i, $v['year'])); $this->assertEquals($v['quarter'], $periodForDate->quarter, sprintf('getPeriodForDate for fixture#%d failed. Quarter is expected to be "%s".', $i, $v['quarter'])); } }
public function xSaveAction() { $this->request->defineParams(array('projectId' => ['type' => 'string'], 'year' => ['type' => 'int'], 'quarters' => ['type' => 'json'], 'selectedQuarter' => ['type' => 'string'])); $year = $this->getParam('year'); $selectedQuarter = $this->getParam('selectedQuarter'); if ($selectedQuarter !== 'year' && ($selectedQuarter < 1 || $selectedQuarter > 4)) { throw new OutOfBoundsException(sprintf("Invalid selectedQuarter number.")); } $quarterReq = []; foreach ($this->getParam('quarters') as $q) { if (!isset($q['quarter'])) { throw new InvalidArgumentException(sprintf("Missing quarter property for quarters data set in the request.")); } if ($q['quarter'] < 1 || $q['quarter'] > 4) { throw new OutOfRangeException(sprintf("Quarter value should be between 1 and 4.")); } if (!isset($q['budget'])) { throw new InvalidArgumentException(sprintf("Missing budget property for quarters data set in the request.")); } $quarterReq[$q['quarter']] = $q; } if ($this->getParam('projectId')) { $subjectType = QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT; $subjectId = $this->getParam('projectId'); $subjectEntity = ProjectEntity::findPk($subjectId); /* @var $subjectEntity ProjectEntity */ if ($subjectEntity->accountId != $this->user->getAccountId() || $subjectEntity->shared != ProjectEntity::SHARED_WITHIN_ACCOUNT) { throw new Scalr_Exception_InsufficientPermissions(); } } else { throw new InvalidArgumentException(sprintf('ProjectId must be provided with the request.')); } if (!preg_match("/^[[:xdigit:]-]{36}\$/", $subjectId)) { throw new InvalidArgumentException(sprintf("Invalid UUID has been passed.")); } if (!preg_match('/^\\d{4}$/', $year)) { throw new InvalidArgumentException(sprintf("Invalid year has been passed.")); } //Fetches the previous state of the entities from database $collection = QuarterlyBudgetEntity::getProjectBudget($year, $subjectId); $quarters = new Quarters(SettingEntity::getQuarters(true)); //Updates|creates entities for ($quarter = 1; $quarter <= 4; ++$quarter) { if (!isset($quarterReq[$quarter])) { continue; } $period = $quarters->getPeriodForQuarter($quarter, $year); //Checks if period has already been closed and forbids update if ($period->end->format('Y-m-d') < gmdate('Y-m-d')) { continue; } $entity = current($collection->filterByQuarter($quarter)); if ($entity instanceof QuarterlyBudgetEntity) { //We should update an entity $entity->budget = abs((double) $quarterReq[$quarter]['budget']); } else { //We should create a new one. $entity = new QuarterlyBudgetEntity($year, $quarter); $entity->subjectType = $subjectType; $entity->subjectId = $subjectId; $entity->budget = abs((double) $quarterReq[$quarter]['budget']); } $entity->save(); } if ($selectedQuarter == 'year') { $selectedPeriod = $quarters->getPeriodForYear($year); } else { $selectedPeriod = $quarters->getPeriodForQuarter($selectedQuarter, $year); } $data = $this->getProjectData(ProjectEntity::findPk($subjectId), $selectedPeriod, true); $budgetInfo = $this->getBudgetInfo($year, $data['ccId'], $data['projectId']); $this->response->data(['data' => $data, 'budgetInfo' => $budgetInfo]); $this->response->success('Budget changes have been saved'); }
/** * {@inheritdoc} * @see \Scalr\System\Zmq\Cron\AbstractTask::enqueue */ public function enqueue() { $logger = $this->getLogger(); if (!\Scalr::getContainer()->analytics->enabled) { $logger->info("CA has not been enabled in config!\n"); } $db = \Scalr::getDb(); $cadb = \Scalr::getContainer()->cadb; $pricing = new stdPricing(); $quarters = new Quarters(SettingEntity::getQuarters()); $logger->info('Started AnalyticsDemo process'); $tzUtc = new DateTimeZone('UTC'); /* @var $projects stdProject[] */ $projects = []; /* @var $ccs stdCc[] */ $ccs = []; /* @var $farms stdFarm[] */ $farms = []; /* @var $environments stdEnv[] */ $environments = []; /* @var $farmRoles stdFarmRole[] */ //$farmRoles = []; //Analytics container $analytics = \Scalr::getContainer()->analytics; $logger->debug('CC & PROJECTS ---'); foreach ($analytics->ccs->all(true) as $cc) { /* @var $cc \Scalr\Stats\CostAnalytics\Entity\CostCentreEntity */ $co = new stdCc(); $co->cc = $cc; $ccs[$cc->ccId] = $co; $logger->debug("Cost center: '%s'", $cc->name); foreach ($cc->getProjects() as $project) { /* @var $project \Scalr\Stats\CostAnalytics\Entity\ProjectEntity */ $project->loadProperties(); $po = new stdProject(); $po->project = $project; $po->cc = $co; $projects[$project->projectId] = $po; $logger->debug("-- Project: '%s'", $project->name); } } //Ordering cost centers $number = 0; foreach ($ccs as $obj) { $obj->number = $number++; } //Ordering projects $number = 0; foreach ($projects as $obj) { $obj->number = $number++; } $logger->debug("FARMS ---"); $pastIterations = static::PAST_HOURS_INIT; //Current time $start = new DateTime('now', $tzUtc); $dt = clone $start; do { $timestamp = $dt->format('Y-m-d H:00:00'); $period = $quarters->getPeriodForDate($dt->format('Y-m-d')); $logger->info("Processing time:%s, year:%d, quarter:%d", $timestamp, $period->year, $period->quarter); //Gets farms for each project foreach ($projects as $po) { foreach ($analytics->projects->getFarmsList($po->project->projectId) as $farmId => $farmName) { if (!isset($farms[$farmId])) { $fo = new stdFarm(); $fo->farm = \DBFarm::LoadByID($farmId); $fo->project = $po; $fo->cc = $po->cc; //$po->farms[] = $fo; $farms[$farmId] = $fo; if (!isset($environments[$fo->farm->EnvID])) { $eo = new stdEnv(); $eo->env = $fo->farm->getEnvironmentObject(); //$eo->farms = [$farmId => $fo]; $environments[$fo->farm->EnvID] = $eo; $fo->env = $eo; } else { //$environments[$fo->farm->EnvID]->farms[$farmId] = $fo; $fo->env = $environments[$fo->farm->EnvID]; } $fo->farmRoles = []; foreach ($fo->farm->GetFarmRoles() as $farmRole) { $fro = new stdFarmRole(); $fro->farmRole = $farmRole; $fro->farm = $fo; $fro->min = $farmRole->GetSetting(\DBFarmRole::SETTING_SCALING_MIN_INSTANCES); $fro->max = $farmRole->GetSetting(\DBFarmRole::SETTING_SCALING_MAX_INSTANCES); $fo->farmRoles[$farmRole->ID] = $fro; //$farmRoles[$farmRole->ID] = $fro; } } else { $fo = $farms[$farmId]; } $logger->debug("Farm:'%s':%d from Env:'%s':%d corresponds to Project:'%s' -> CC:'%s'", $fo->farm->Name, $fo->farm->ID, $fo->farm->getEnvironmentObject()->name, $fo->farm->EnvID, $po->project->name, $po->cc->cc->name); foreach ($fo->farmRoles as $fro) { /* @var $fro stdFarmRole */ $countInstances = rand(max(1, floor($fro->max * 0.7)), min((int) $fro->max, 2)); $cost = $pricing->getPrice($dt, $fro->farmRole->Platform, $fro->farmRole->CloudLocation, $fro->getInstanceType(), $fo->env->getUrl($fro->farmRole->Platform), PriceEntity::OS_LINUX); $type = UsageTypeEntity::NAME_COMPUTE_BOX_USAGE; $costDistType = UsageTypeEntity::COST_DISTR_TYPE_COMPUTE; $usageTypeEntity = UsageTypeEntity::findOne([['costDistrType' => $costDistType], ['name' => $type]]); /* @var $usageTypeEntity UsageTypeEntity */ if ($usageTypeEntity === null) { $usageTypeEntity = new UsageTypeEntity(); $usageTypeEntity->costDistrType = $costDistType; $usageTypeEntity->name = $type; $usageTypeEntity->displayName = 'Compute instances'; $usageTypeEntity->save(); } $item = $fro->getInstanceType(); $usageItemEntity = UsageItemEntity::findOne([['usageType' => $usageTypeEntity->id], ['name' => $item]]); /* @var $usageItemEntity UsageItemEntity */ if ($usageItemEntity === null) { $usageItemEntity = new UsageItemEntity(); $usageItemEntity->usageType = $usageTypeEntity->id; $usageItemEntity->name = $item; $usageItemEntity->save(); } //Hourly usage $rec = new UsageHourlyEntity(); $rec->usageId = \Scalr::GenerateUID(); $rec->accountId = $fro->farm->farm->ClientID; $rec->ccId = $po->cc->cc->ccId; $rec->projectId = $po->project->projectId; $rec->cloudLocation = $fro->farmRole->CloudLocation; $rec->dtime = new DateTime($timestamp, $tzUtc); $rec->envId = $fo->farm->EnvID; $rec->farmId = $fo->farm->ID; $rec->farmRoleId = $fro->farmRole->ID; $rec->usageItem = $usageItemEntity->id; $rec->platform = $fro->farmRole->Platform; $rec->url = $fo->env->getUrl($fro->farmRole->Platform); $rec->os = PriceEntity::OS_LINUX; $rec->num = $countInstances; $rec->cost = $cost * $countInstances; $rec->save(); $logger->log(static::PAST_HOURS_INIT > 0 ? 'DEBUG' : 'INFO', "-- role:'%s':%d platform:%s, min:%d - max:%d, cloudLocation:'%s', usageItem:'%s', " . "cost:%0.4f * %d = %0.3f", $fro->farmRole->Alias, $fro->farmRole->ID, $fro->farmRole->Platform, $fro->min, $fro->max, $fro->farmRole->CloudLocation, $usageItemEntity->id, $cost, $countInstances, $rec->cost); //Update Daily table $cadb->Execute("\n INSERT usage_d\n SET date = ?,\n platform = ?,\n cc_id = UNHEX(?),\n project_id = UNHEX(?),\n farm_id = ?,\n env_id = ?,\n cost = ?\n ON DUPLICATE KEY UPDATE cost = cost + ?\n ", [$rec->dtime->format('Y-m-d'), $rec->platform, $rec->ccId ? str_replace('-', '', $rec->ccId) : '00000000-0000-0000-0000-000000000000', $rec->projectId ? str_replace('-', '', $rec->projectId) : '00000000-0000-0000-0000-000000000000', $rec->farmId ? $rec->farmId : 0, $rec->envId ? $rec->envId : 0, $rec->cost, $rec->cost]); //Updates Quarterly Budget if ($rec->ccId) { $cadb->Execute("\n INSERT quarterly_budget\n SET year = ?,\n subject_type = ?,\n subject_id = UNHEX(?),\n quarter = ?,\n budget = 1000,\n cumulativespend = ?\n ON DUPLICATE KEY UPDATE cumulativespend = cumulativespend + ?\n ", [$period->year, QuarterlyBudgetEntity::SUBJECT_TYPE_CC, str_replace('-', '', $rec->ccId), $period->quarter, $rec->cost, $rec->cost]); } if ($rec->projectId) { $cadb->Execute("\n INSERT quarterly_budget\n SET year = ?,\n subject_type = ?,\n subject_id = UNHEX(?),\n quarter = ?,\n budget = 1000,\n cumulativespend = ?\n ON DUPLICATE KEY UPDATE cumulativespend = cumulativespend + ?\n ", [$period->year, QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT, str_replace('-', '', $rec->projectId), $period->quarter, $rec->cost, $rec->cost]); } } unset($fo); } } $dt->modify('-1 hour'); } while ($pastIterations-- > 0); $dt = clone $start; $start->modify(sprintf("-%d hour", static::PAST_HOURS_INIT)); $start->setTime(0, 0, 0); $date = $dt->format('Y-m-d'); $hours = (int) $dt->format('H'); do { $cadb->Execute("\n INSERT INTO `farm_usage_d` (\n `account_id`,\n `farm_role_id`,\n `usage_item`,\n `cc_id`,\n `project_id`,\n `date`,\n `platform`,\n `cloud_location`,\n `env_id`,\n `farm_id`,\n `role_id`,\n `cost`,\n `min_usage`,\n `max_usage`,\n `usage_hours`,\n `working_hours`)\n SELECT\n `account_id`,\n IFNULL(`farm_role_id`, 0) `farm_role_id`,\n `usage_item`,\n IFNULL(`cc_id`, '') `cc_id`,\n IFNULL(`project_id`, '') `project_id`,\n ? `date`,\n `platform`,\n `cloud_location`,\n IFNULL(`env_id`, 0) `env_id`,\n IFNULL(`farm_id`, 0) `farm_id`,\n IFNULL(`role_id`, 0) `role_id`,\n SUM(`cost`) `cost`,\n (CASE WHEN COUNT(`dtime`) >= ? THEN MIN(`num`) ELSE 0 END) `min_usage`,\n MAX(`num`) `max_usage`,\n SUM(`num`) `usage_hours`,\n COUNT(`dtime`) `working_hours`\n FROM `usage_h` `uh`\n WHERE `uh`.`dtime` BETWEEN ? AND ?\n AND `uh`.`farm_id` > 0\n AND `uh`.`farm_role_id` > 0\n GROUP BY `uh`.`account_id` , `uh`.`farm_role_id` , `uh`.`usage_item`\n ON DUPLICATE KEY UPDATE\n `cost` = VALUES(`cost`),\n `min_usage` = VALUES(`min_usage`),\n `max_usage` = VALUES(`max_usage`),\n `usage_hours` = VALUES(`usage_hours`),\n `working_hours` = VALUES(`working_hours`)\n ", ["{$date} 00:00:00", $hours, "{$date} 00:00:00", "{$date} 23:00:00"]); $dt->modify('-1 day'); $date = $dt->format('Y-m-d'); $hours = 24; } while ($dt >= $start); $logger->info("Finished AnalyticsDemo process"); $logger->info("Memory usage: %0.3f Mb", memory_get_usage() / 1024 / 1024); return new ArrayObject(); }
/** * Saves project or cost center notification * * @param ProjectEntity|CostCentreEntity $subject Project or cost center entity * @param NotificationEntity $notification Current notification object * @throws InvalidArgumentException */ private function saveNotificationData($subject, NotificationEntity $notification) { $baseUrl = rtrim(\Scalr::getContainer()->config('scalr.endpoint.scheme') . "://" . \Scalr::getContainer()->config('scalr.endpoint.host'), '/'); $quarters = new Quarters(SettingEntity::getQuarters()); $date = $quarters->getPeriodForDate('yesterday'); $formatedTitle = 'Q' . $quarters->getQuarterForDate('now') . ' budget (' . (new \DateTime('now', new \DateTimeZone('UTC')))->format('M j, Y') . ')'; if ($subject instanceof ProjectEntity) { $getPeriodicSubjectData = 'getProjectPeriodData'; $subjects = 'projects'; $childItems = 'farms'; $subjectIdName = 'projectId'; $subjectName = 'project'; } else { if ($subject instanceof CostCentreEntity) { $getPeriodicSubjectData = 'getCostCenterPeriodData'; $subjects = 'costcenters'; $childItems = 'projects'; $subjectIdName = 'ccId'; $subjectName = 'cc'; } else { throw new InvalidArgumentException("Invalid subject parameter. It must be either ProjectEntity or CostCentreEntity type."); } } $periodSubjectData = \Scalr::getContainer()->analytics->usage->{$getPeriodicSubjectData}($subject->{$subjectIdName}, 'quarter', $date->start->format('Y-m-d'), $date->end->format('Y-m-d')); $subjectAnalytics = ['budget' => $periodSubjectData['totals']['budget'], 'name' => $subject->name, 'trends' => $periodSubjectData['totals']['trends'], 'forecastCost' => $periodSubjectData['totals']['forecastCost'], 'interval' => $periodSubjectData['interval'], 'date' => $formatedTitle, 'detailsUrl' => $baseUrl . '#/analytics/' . $subjects . '?' . $subjectIdName . '=' . $subject->{$subjectIdName}, 'jsonVersion' => '1.0.0', $childItems => []]; if (!empty($periodSubjectData['totals'][$childItems])) { $subjectAnalytics[$childItems] = $this->getSubjectChildItems($subject, $periodSubjectData['totals'][$childItems], $date); } if ($notification->notificationType === NotificationEntity::NOTIFICATION_TYPE_USAGE) { $reportType = $subjectName . 'Usage'; $budgetThreshold = 'budgetSpentPct'; $emailSubject = $subjectAnalytics['name'] . ' usage notification.'; } else { if ($notification->notificationType === NotificationEntity::NOTIFICATION_TYPE_PROJECTED_OVERSPEND) { $reportType = $subjectName . 'Overspend'; $budgetThreshold = 'estimateOverspendPct'; $emailSubject = $subjectAnalytics['name'] . ' overspend notification.'; } } if ($subjectAnalytics['budget'][$budgetThreshold] >= $notification->threshold) { $subjectAnalytics['reportType'] = $reportType; $entity = ReportPayloadEntity::init([$notification->notificationType, $notification->subjectType, $subject->{$subjectIdName}, $notification->threshold], $subjectAnalytics); if (!ReportPayloadEntity::findPk($entity->uuid)) { $payload = json_decode($entity->payload, true); if (!empty($subjectAnalytics['budget']['estimateDate'])) { $subjectAnalytics['budget']['estimateDate'] = (new DateTime($subjectAnalytics['budget']['estimateDate'], new DateTimeZone('UTC')))->format('M j, Y'); $subjectAnalytics['reportUrl'] = $payload['reportUrl']; } \Scalr::getContainer()->mailer->setSubject($emailSubject)->setContentType('text/html')->sendTemplate(SCALR_TEMPLATES_PATH . '/emails/budget_notification_' . $subjectName . '.html.php', $subjectAnalytics, $notification->emails); $this->getLogger()->info('Notification email has been sent'); $payload['date'] = $entity->created->format('Y-m-d'); $entity->payload = json_encode($payload); $entity->save(); } } }
/** * 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))); } }
/** * {@inheritdoc} * @see Iterator::current() * @return ChartPointInfo */ public function current() { if (!isset($this->c[$this->i])) { $chartPoint = new ChartPointInfo($this); $previousPeriodDt = clone $chartPoint->dt; $previousPeriodDt->sub($this->getPreviousPeriodInterval()); switch ($chartPoint->interval) { case '1 hour': $h = $chartPoint->dt->format('H'); $chartPoint->label = $chartPoint->dt->format('l, M j, g A'); $chartPoint->show = $h == 0 ? $chartPoint->dt->format('M j') : ($h % 3 == 0 ? $chartPoint->dt->format('g a') : ''); $chartPoint->key = $chartPoint->dt->format("Y-m-d H:00:00"); $chartPoint->previousPeriodKey = $previousPeriodDt->format('Y-m-d H:00:00'); break; case '1 day': $chartPoint->label = $chartPoint->dt->format('M j'); $chartPoint->key = $chartPoint->dt->format('Y-m-d'); $chartPoint->previousPeriodKey = $previousPeriodDt->format('Y-m-d'); $chartPoint->show = $chartPoint->i % 4 == 0 || $chartPoint->isLastPoint && $chartPoint->i % 4 > 2 ? $chartPoint->dt->format('M j') : ''; break; case '1 week': $ddt = clone $chartPoint->dt; $ddt->modify('next saturday'); if ($ddt > $chartPoint->end) { $ddt = $chartPoint->end; } $chartPoint->label = $chartPoint->dt->format('M j') . ' - ' . $ddt->format('M j'); $chartPoint->key = \Scalr_Util_DateTime::yearweek($chartPoint->dt->format('Y-m-d')); $chartPoint->previousPeriodKey = \Scalr_Util_DateTime::yearweek($previousPeriodDt->format('Y-m-d')); $chartPoint->show = $chartPoint->i % 3 == 0 ? $chartPoint->dt->format('M j') : ($chartPoint->isLastPoint && $chartPoint->i % 3 > 1 ? $ddt->format('M j') : ''); break; case '1 month': $diffdays = $this->start->diff($this->end, true)->days; if ($diffdays < 366) { $chartPoint->show = $chartPoint->label = $chartPoint->dt->format('M'); } else { $chartPoint->show = $chartPoint->label = $chartPoint->dt->format('M, Y'); } $chartPoint->key = $chartPoint->dt->format('Y-m'); $chartPoint->previousPeriodKey = $previousPeriodDt->format('Y-m'); break; case '1 quarter': //Quarter breakdown is not supported yet $quarters = new Quarters(SettingEntity::getQuarters()); $currentPeriod = $quarters->getPeriodForDate($chartPoint->start); $prevStart = $this->getPreviousStart(); $prevPeriod = $quarters->getPeriodForDate($prevStart); $chartPoint->show = $chartPoint->label = $currentPeriod->year . ' Q' . $currentPeriod->quarter; $chartPoint->key = $currentPeriod->year . '-' . $currentPeriod->quarter; $chartPoint->previousPeriodKey = $prevPeriod->year . '-' . $prevPeriod->quarter; break; case '1 year': $chartPoint->show = $chartPoint->label = $chartPoint->dt->format('Y'); $chartPoint->key = $chartPoint->label; $chartPoint->previousPeriodKey = $previousPeriodDt->format('Y'); break; default: throw new \InvalidArgumentException(sprintf('Unsupported interval for custom mode %s.', $chartPoint->interval)); break; } $this->c[$this->i] = $chartPoint; } return $this->c[$this->i]; }
/** * Gets quarter period object for the current date * * @return QuarterPeriod Returns quarter period object for the current date */ private static function _getCurrentPeriod() { static $period; if ($period === null) { $quarters = new Quarters(SettingEntity::getQuarters()); $period = $quarters->getPeriodForDate(); } return $period; }
/** * Gets project moving average to date * * @param string|null $projectId The identifier of the project * @param string $mode The mode * @param string $date The UTC date within period ('Y-m-d H:00') * @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 string $ccId optional The identifier of the cost center (It is used only when project is null) * @return array Returns project moving average to date * @throws InvalidArgumentException * @throws AnalyticsException */ public function getProjectMovingAverageToDate($projectId, $mode, $date, $startDate, $endDate, $ccId = null) { $projectId = empty($projectId) ? null : $projectId; if (!preg_match('/^[\\d]{4}-[\\d]{2}-[\\d]{2} [\\d]{2}:00$/', $date)) { throw new InvalidArgumentException(sprintf("Invalid date:%s. 'YYYY-MM-DD HH:00' is expected.", strip_tags($date))); } if (!preg_match('/^[[:xdigit:]-]{36}$/', $projectId)) { throw new InvalidArgumentException(sprintf("Invalid identifier of the Project:%s. UUID is expected.", strip_tags($projectId))); } $iterator = ChartPeriodIterator::create($mode, $startDate, $endDate ?: 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; } } $data = $this->getRollingAvg(['projectId' => $projectId], $queryInterval, $date); $data['budgetUseToDate'] = null; $data['budgetUseToDatePct'] = null; //Does not calculate budget use to date for those cases if ($mode != 'custom' && $mode != 'year') { $pointPosition = $iterator->searchPoint($date); if ($pointPosition !== false) { $chartPoint = $iterator->current(); //Gets the end date of the selected interval $end = clone $chartPoint->dt; if ($chartPoint->interval != '1 day') { $end->modify("+" . $chartPoint->interval . " -1 day"); } //Gets quarters config $quarters = new Quarters(SettingEntity::getQuarters()); //Gets start and end of the quarter for the end date of the current interval $period = $quarters->getPeriodForDate($end); $data['year'] = $period->year; $data['quarter'] = $period->quarter; $data['quarterStartDate'] = $period->start->format('Y-m-d'); $data['quarterEndDate'] = $period->end->format('Y-m-d'); //Gets budgeted cost $budget = current(QuarterlyBudgetEntity::getProjectBudget($period->year, $projectId)->filterByQuarter($period->quarter)); //If budget has not been set we should not calculate anything if ($budget instanceof QuarterlyBudgetEntity && round($budget->budget) > 0) { $data['budget'] = round($budget->budget); //Calculates usage from the start date of the quarter to date $usage = $this->get(['projectId' => $projectId], $period->start, $end); $data['budgetUseToDate'] = $usage['cost']; $data['budgetUseToDatePct'] = $data['budget'] == 0 ? null : min(100, round($usage['cost'] / $data['budget'] * 100)); } } } return ['data' => $data]; }
private function getFarmCostData($farmId) { $result = []; $costCenter = $this->getContainer()->analytics->ccs->get($this->getEnvironment()->getPlatformConfigValue(Scalr_Environment::SETTING_CC_ID)); $currentYear = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y'); $quarters = new Quarters(SettingEntity::getQuarters()); $currentQuarter = $quarters->getQuarterForDate(new \DateTime('now', new \DateTimeZone('UTC'))); $projects = []; if ($farmId) { $farm = DBFarm::LoadByID($farmId); $currentProjectId = $farm->GetSetting(Entity\FarmSetting::PROJECT_ID); $currentProject = ProjectEntity::findPk($currentProjectId); /* @var $currentProject ProjectEntity */ if (!empty($currentProject)) { $quarterBudget = QuarterlyBudgetEntity::findOne([['year' => $currentYear], ['subjectType' => QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT], ['subjectId' => $currentProject->projectId], ['quarter' => $currentQuarter]]); $projects[] = ['projectId' => $currentProject->projectId, 'name' => "{$currentProject->name} / {$currentProject->getCostCenter()->name}", 'budgetRemain' => !is_null($quarterBudget) && $quarterBudget->budget > 0 ? max(0, round($quarterBudget->budget - $quarterBudget->cumulativespend)) : null]; } $result['projectId'] = $farm->GetSetting(Entity\FarmSetting::PROJECT_ID); $result['farmCostMetering'] = $result['projectId'] ? $this->getContainer()->analytics->usage->getFarmCostMetering($this->user->getAccountId(), $farmId) : null; } if ($costCenter instanceof CostCentreEntity) { $projectsIterator = new SharedProjectsFilterIterator($costCenter->getProjects(), $costCenter->ccId, $this->user, $this->getEnvironment()); foreach ($projectsIterator as $item) { /* @var $item Scalr\Stats\CostAnalytics\Entity\ProjectEntity */ if (!empty($currentProjectId) && $item->projectId == $currentProjectId) { continue; } $quarterBudget = QuarterlyBudgetEntity::findOne([['year' => $currentYear], ['subjectType' => QuarterlyBudgetEntity::SUBJECT_TYPE_PROJECT], ['subjectId' => $item->projectId], ['quarter' => $currentQuarter]]); $projects[] = array('projectId' => $item->projectId, 'name' => "{$item->name} / {$costCenter->name}", 'budgetRemain' => !is_null($quarterBudget) && $quarterBudget->budget > 0 ? max(0, round($quarterBudget->budget - $quarterBudget->cumulativespend)) : null); } $costCentreName = $costCenter->name; $isLocked = $costCenter->getProperty(CostCentrePropertyEntity::NAME_LOCKED); $accountCcs = AccountCostCenterEntity::findOne([['accountId' => $this->environment->clientId], ['ccId' => $costCenter->ccId]]); if ($isLocked || !$accountCcs instanceof AccountCostCenterEntity) { $costCentreLocked = 1; } else { $costCentreLocked = 0; } } else { $costCentreName = ''; $costCentreLocked = 0; } $supportedClouds = $this->getContainer()->analytics->prices->getSupportedClouds(); $result['analytics'] = array('costCenterName' => $costCentreName, 'costCenterLocked' => $costCentreLocked, 'projects' => $projects, 'unsupportedClouds' => array_values(array_diff($this->environment->getEnabledPlatforms(), $supportedClouds))); return $result; }
/** * Returns iterator for current quarter * * @return Iterator\ChartQuarterlyIterator */ public function getCurrentQuarterIterator() { $quarters = new Quarters(SettingEntity::getQuarters()); $currentQuarter = $quarters->getQuarterForDate(new \DateTime('now', new \DateTimeZone('UTC'))); $currentYear = (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y'); if ($currentQuarter === 1) { $quarter = 4; $year = $currentYear - 1; } else { $quarter = $currentQuarter - 1; $year = $currentYear; } $date = $quarters->getPeriodForQuarter($quarter, $year); $iterator = ChartPeriodIterator::create('quarter', $date->start, $date->end, 'UTC'); return $iterator; }
/** * {@inheritdoc} * @see Iterator::next() */ public function next() { if ($this->interval == '1 week') { $this->dt->modify('next sunday'); } elseif ($this->interval == '1 quarter') { //First day of next quarter $quarters = new Quarters(SettingEntity::getQuarters()); $period = $quarters->getPeriodForDate($this->dt); $this->dt = $period->end; $this->dt->modify('+1 day'); } else { $this->dt->add($this->di); } $this->i++; }
/** * {@inheritdoc} * @see \Scalr\System\Pcntl\ProcessInterface::OnStartForking() */ public function OnStartForking() { if (!\Scalr::getContainer()->analytics->enabled) { die("Terminating the process as Cost analytics is disabled in the config.\n"); } $this->console->out("%s (UTC) Start Analytics Notifications process", gmdate('Y-m-d')); if (SettingEntity::getValue(SettingEntity::ID_NOTIFICATIONS_CCS_ENABLED) || SettingEntity::getValue(SettingEntity::ID_NOTIFICATIONS_PROJECTS_ENABLED)) { $this->console->out('Calculating data for projects notifications'); $quarters = new Quarters(SettingEntity::getQuarters()); $date = $quarters->getPeriodForDate('yesterday'); $formatedTitle = 'Q' . $quarters->getQuarterForDate('now') . ' budget (' . (new \DateTime('now', new \DateTimeZone('UTC')))->format('M j, Y') . ')'; $projects = ProjectEntity::find(); foreach ($projects as $project) { $periodProjectData = \Scalr::getContainer()->analytics->usage->getProjectPeriodData($project->projectId, 'quarter', $date->start->format('Y-m-d'), $date->end->format('Y-m-d')); $projectAnalytics[$project->projectId] = ['budget' => $periodProjectData['totals']['budget'], 'name' => $project->name, 'trends' => $periodProjectData['totals']['trends'], 'forecastCost' => $periodProjectData['totals']['forecastCost'], 'interval' => $periodProjectData['interval'], 'jsonVersion' => '1.0.0', 'farms' => []]; if (!empty($periodProjectData['totals']['farms'])) { foreach ($periodProjectData['totals']['farms'] as $farm) { $projectAnalytics[$project->projectId]['farms'][] = ['id' => $farm['id'], 'name' => $farm['name'], 'median' => $farm['median'] / 7, 'cost' => $farm['cost'], 'costPct' => $farm['costPct']]; } if (count($projectAnalytics[$project->projectId]['farms'] > 1)) { usort($projectAnalytics[$project->projectId]['farms'], array($this, 'sortItems')); if (count($projectAnalytics[$project->projectId]['farms'] > 6)) { array_splice($projectAnalytics[$project->projectId]['farms'], 6, count($projectAnalytics[$project->projectId]['farms'])); } } } $projectAnalytics[$project->projectId]['date'] = $formatedTitle; if ($project->archived) { continue; } if (SettingEntity::getValue(SettingEntity::ID_NOTIFICATIONS_PROJECTS_ENABLED)) { $projectNotifications = NotificationEntity::findBySubjectType(NotificationEntity::SUBJECT_TYPE_PROJECT); foreach ($projectNotifications as $notification) { $this->saveNotificationData('project', $notification, $project->projectId, $projectAnalytics); } } } } if (SettingEntity::getValue(SettingEntity::ID_NOTIFICATIONS_CCS_ENABLED)) { $this->console->out('Calculating data for cost center notifications'); $ccs = CostCentreEntity::find(); foreach ($ccs as $cc) { if ($cc->archived) { continue; } $periodCostCenterData = \Scalr::getContainer()->analytics->usage->getCostCenterPeriodData($cc->ccId, 'quarter', $date->start->format('Y-m-d'), $date->end->format('Y-m-d')); $ccAnalytics[$cc->ccId] = ['budget' => $periodCostCenterData['totals']['budget'], 'name' => $cc->name, 'trends' => $periodCostCenterData['totals']['trends'], 'forecastCost' => $periodCostCenterData['totals']['forecastCost'], 'interval' => $periodCostCenterData['interval'], 'jsonVersion' => '1.0.0', 'projects' => []]; if (!empty($periodCostCenterData['totals']['projects'])) { foreach ($periodCostCenterData['totals']['projects'] as $key => $project) { if (!empty($project['id'])) { $projectBudget = $projectAnalytics[$project['id']]['budget']; $projectBudget['name'] = $project['name']; $projectBudget['id'] = $project['id']; $projectBudget['median'] = $project['median'] / 7; $ccAnalytics[$cc->ccId]['projects'][] = $projectBudget; } else { $otherProjectsKey = $key; } } if (isset($otherProjectsKey)) { $ccAnalytics[$cc->ccId]['projects'][] = ['id' => '', 'budgetSpent' => $periodCostCenterData['totals']['projects'][$otherProjectsKey]['cost'], 'median' => $periodCostCenterData['totals']['projects'][$otherProjectsKey]['median'] / 7, 'name' => $periodCostCenterData['totals']['projects'][$otherProjectsKey]['name'], 'estimateOverspend' => null]; unset($otherProjectsKey); } if (count($ccAnalytics[$cc->ccId]['projects'] > 1)) { usort($ccAnalytics[$cc->ccId]['projects'], array($this, 'sortItems')); if (count($ccAnalytics[$cc->ccId]['projects'] > 6)) { array_splice($ccAnalytics[$cc->ccId]['projects'], 6, count($ccAnalytics[$cc->ccId]['projects'])); } } } $ccAnalytics[$cc->ccId]['date'] = $formatedTitle; $ccsNotifications = NotificationEntity::findBySubjectType(NotificationEntity::SUBJECT_TYPE_CC); foreach ($ccsNotifications as $notification) { $this->saveNotificationData('cc', $notification, $cc->ccId, $ccAnalytics); } } } if (SettingEntity::getValue(SettingEntity::ID_REPORTS_ENABLED)) { $this->console->out('Calculating data for reports'); $reports = ReportEntity::find(); foreach ($reports as $report) { switch ($report->period) { case ReportEntity::PERIOD_DAILY: $period = 'custom'; $start = (new \DateTime('yesterday', new \DateTimeZone('UTC')))->format('Y-m-d'); $end = $start; $startForecast = (new \DateTime('first day of this month', new \DateTimeZone('UTC')))->format('Y-m-d'); $endForecast = (new \DateTime('last day of this month', new \DateTimeZone('UTC')))->format('Y-m-d'); if ($startForecast == (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y-m-d')) { $startForecast = (new \DateTime('first day of last month', new \DateTimeZone('UTC')))->format('Y-m-d'); $endForecast = (new \DateTime('last day of last month', new \DateTimeZone('UTC')))->format('Y-m-d'); } $periodForecast = 'month'; $formatedTitle = (new \DateTime($start, new \DateTimeZone('UTC')))->format('M j'); break; case ReportEntity::PERIOD_MONTHLY: $period = 'month'; $start = (new \DateTime('first day of last month', new \DateTimeZone('UTC')))->format('Y-m-d'); $end = (new \DateTime('last day of last month', new \DateTimeZone('UTC')))->format('Y-m-d'); $quarters = new Quarters(SettingEntity::getQuarters()); $currentQuarter = $quarters->getQuarterForDate(new \DateTime($start, new \DateTimeZone('UTC'))); $currentYear = (new \DateTime($start, new \DateTimeZone('UTC')))->format('Y'); $date = $quarters->getPeriodForQuarter($currentQuarter, $currentYear); $formatedTitle = (new \DateTime($start, new \DateTimeZone('UTC')))->format('M Y'); $startForecast = $date->start->format('Y-m-d'); $endForecast = $date->end->format('Y-m-d'); $periodForecast = 'quarter'; break; case ReportEntity::PERIOD_QUARTELY: $period = 'quarter'; $quarters = new Quarters(SettingEntity::getQuarters()); $currentQuarter = $quarters->getQuarterForDate(new \DateTime('yesterday', new \DateTimeZone('UTC'))); $currentYear = (new \DateTime('yesterday', new \DateTimeZone('UTC')))->format('Y'); if ($currentQuarter === 1) { $quarter = 4; $year = $currentYear - 1; } else { $quarter = $currentQuarter - 1; $year = $currentYear; } $date = $quarters->getPeriodForQuarter($quarter, $year); $start = $date->start->format('Y-m-d'); $end = $date->end->format('Y-m-d'); $formatedTitle = 'Q' . $quarter . ' ' . $year; $startForecast = $currentYear . '-01-01'; $endForecast = $currentYear . '-12-31'; $periodForecast = 'year'; break; case ReportEntity::PERIOD_WEEKLY: $period = 'week'; $end = (new \DateTime('yesterday', new \DateTimeZone('UTC')))->modify('last saturday')->format('Y-m-d'); $start = (new \DateTime($end, new \DateTimeZone('UTC')))->modify('last sunday')->format('Y-m-d'); $formatedTitle = (new \DateTime($start, new \DateTimeZone('UTC')))->format('M j') . ' - ' . (new \DateTime($end, new \DateTimeZone('UTC')))->format('M j'); $startForecast = (new \DateTime('first day of this month', new \DateTimeZone('UTC')))->format('Y-m-d'); $endForecast = (new \DateTime('last day of this month', new \DateTimeZone('UTC')))->format('Y-m-d'); if ($startForecast == (new \DateTime('now', new \DateTimeZone('UTC')))->format('Y-m-d')) { $startForecast = (new \DateTime('first day of last month', new \DateTimeZone('UTC')))->format('Y-m-d'); $endForecast = (new \DateTime('last day of last month', new \DateTimeZone('UTC')))->format('Y-m-d'); } $periodForecast = 'month'; break; } if ($report->subjectType === ReportEntity::SUBJECT_TYPE_CC) { $getPeriodicSubjectData = 'getCostCenterPeriodData'; $subjectEntityName = 'Scalr\\Stats\\CostAnalytics\\Entity\\CostCentre'; $subjectId = 'ccId'; } else { if ($report->subjectType === ReportEntity::SUBJECT_TYPE_PROJECT) { $getPeriodicSubjectData = 'getProjectPeriodData'; $subjectEntityName = 'Scalr\\Stats\\CostAnalytics\\Entity\\Project'; $subjectId = 'projectId'; } else { $periodData = \Scalr::getContainer()->analytics->usage->getDashboardPeriodData($period, $start, $end); $periodDataForecast = \Scalr::getContainer()->analytics->usage->getDashboardPeriodData($periodForecast, $startForecast, $endForecast); $periodData['period'] = $period; $periodData['forecastPeriod'] = $periodForecast; $periodData['totals']['forecastCost'] = $periodDataForecast['totals']['forecastCost']; $periodData['totals']['trends'] = $periodDataForecast['totals']['trends']; $periodData['name'] = 'Cloud Cost Report'; $periodData['jsonVersion'] = '1.0.0'; $periodData['totals']['clouds'] = $this->changeCloudNames($periodData['totals']['clouds']); $periodData['date'] = $formatedTitle; $periodData['totals']['budget']['budget'] = null; if ($period == 'quarter') { $periodData['totals']['budget'] = ['quarter' => $quarter, 'year' => $year, 'quarterStartDate' => $start, 'quarterEndDate' => $end]; } else { if ($period == 'month') { $periodData['totals']['budget'] = ['quarter' => $currentQuarter]; } } unset($periodData['projects'], $periodData['budget']['projects']); if (count($periodData['costcenters'] > 1)) { uasort($periodData['costcenters'], array($this, 'sortItems')); if (count($periodData['costcenters'] > 6)) { array_splice($periodData['costcenters'], 6, count($periodData['costcenters'])); } } if (count($periodData['totals']['clouds'] > 1)) { usort($periodData['totals']['clouds'], array($this, 'sortItems')); } $entity = ReportPayloadEntity::init([$report->subjectType, $report->subjectId, $period], $periodData, $start); if (!ReportPayloadEntity::findPk($entity->uuid)) { $payload = json_decode($entity->payload, true); \Scalr::getContainer()->mailer->setSubject('Summary report.')->setContentType('text/html')->sendTemplate(SCALR_TEMPLATES_PATH . '/emails/report_summary.html.php', $payload, $report->emails); $this->console->out('Summary report email has been sent'); $payload['date'] = $entity->created->format('Y-m-d'); $entity->payload = json_encode($payload); $entity->save(); } } } unset($currentQuarter, $currentYear); if (!empty($report->subjectType) && !empty($report->subjectId)) { $subject = call_user_func($subjectEntityName . 'Entity::findPk', $report->subjectId); if ($subject->archived) { continue; } $this->saveReportData($getPeriodicSubjectData, $subjectEntityName, ['period' => $period, 'start' => $start, 'end' => $end], ['period' => $periodForecast, 'start' => $startForecast, 'end' => $endForecast], $report->subjectId, $report->subjectType, $report->emails, $formatedTitle); } else { if (!empty($report->subjectType)) { $subjects = call_user_func($subjectEntityName . 'Entity::find'); foreach ($subjects as $subject) { if ($subject->archived) { continue; } $this->saveReportData($getPeriodicSubjectData, $subjectEntityName, ['period' => $period, 'start' => $start, 'end' => $end], ['period' => $periodForecast, 'start' => $startForecast, 'end' => $endForecast], $subject->{$subjectId}, $report->subjectType, $report->emails, $formatedTitle); } } } } } $this->console->out('Done'); exit; }
/** * Constructor * * @param string $start The start date of the period 'YYYY-mm-dd' * @param string $end optional End date * @param string $timezone optional Timezone * @throws \InvalidArgumentException */ public function __construct($start, $end = null, $timezone = 'UTC') { $this->mode = 'quarter'; $this->timezone = new DateTimeZone($timezone); $this->today = $this->getTodayDate(); $this->start = new DateTime($start instanceof DateTime ? $start->format('Y-m-d 00:00:00') : $start, $this->timezone); $this->end = !empty($end) ? new DateTime($end instanceof DateTime ? $end->format('Y-m-d 00:00:00') : $end, $this->timezone) : null; $quarters = new Quarters(SettingEntity::getQuarters()); $this->interval = '1 week'; $currentPeriod = $quarters->getPeriodForDate($this->start); $this->start = $currentPeriod->start; $this->end = $currentPeriod->end; $this->wholePeriodPerviousEnd = clone $this->start; $this->wholePeriodPerviousEnd->modify('-1 day'); $prevPeriod = $quarters->getPeriodForDate($this->wholePeriodPerviousEnd); $this->prevStart = $prevPeriod->start; $this->prevInterval = new \DateInterval('P' . sprintf("%d", $this->start->diff($this->end, true)->days) . 'D'); $this->determinePrevEnd(); $endoftheday = new \DateInterval('PT23H59M59S'); $this->end->add($endoftheday); $this->prevEnd->add($endoftheday); $this->wholePeriodPerviousEnd->add($endoftheday); if (!$this->di) { $this->di = \DateInterval::createFromDateString($this->interval); } $this->dt = clone $this->start; }