wrapped = $wrapped; $this->isLowerBetter = Metrics::isLowerValueBetter($this->wrapped); $this->pastData = $pastData; $this->currentData = $currentData; if (empty($evolutionMetricName)) { $wrappedName = $this->getWrappedName(); $evolutionMetricName = $wrappedName . '_evolution'; } $this->evolutionMetricTrendName = $evolutionMetricName . '_trend'; $this->evolutionMetricName = $evolutionMetricName; $this->quotientPrecision = $quotientPrecision; } public function getName() { return $this->evolutionMetricName; } public function getTrendName() { return $this->evolutionMetricTrendName; } public function getTranslatedName() { if ($this->wrapped instanceof Metric) { $metricName = $this->wrapped->getTranslatedName(); } else { $defaultMetricTranslations = Metrics::getDefaultMetricTranslations(); $metricName = isset($defaultMetricTranslations[$this->wrapped]) ? $defaultMetricTranslations[$this->wrapped] : $this->wrapped; } return Piwik::translate('CoreHome_EvolutionMetricName', [$metricName]); } public function getTrendValue($computedValue = 0) { if ($this->isLowerBetter) { return ($computedValue < 0 ? 1 : ($computedValue > 0 ? -1 : 0)); } return ($computedValue < 0 ? -1 : ($computedValue > 0 ? 1 : 0)); } public function compute(Row $row) { $columnName = $this->getWrappedName(); $pastRow = $this->getPastRowFromCurrent($row); $currentValue = $this->getMetric($row, $columnName); $pastValue = $pastRow ? $this->getMetric($pastRow, $columnName) : 0; // Reduce past value proportionally to match the percent of the current period which is complete, if applicable $ratio = self::getRatio($this->currentData, $this->pastData, $row); $period = $this->pastData->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX); $row->setMetadata('ratio', $ratio); $row->setMetadata('currencySymbol', $row['label'] !== DataTable::ID_SUMMARY_ROW ? Site::getCurrencySymbolFor($row['label']) : API::getInstance()->getDefaultCurrency()); $row->setMetadata('previous_'.$columnName, $pastValue); $row->setMetadata('periodName', $period->getLabel()); $row->setMetadata('previousRange', $period->getLocalizedShortString()); $pastValue = ($pastValue * $ratio); $dividend = $currentValue - $pastValue; $divisor = $pastValue; if ($dividend == 0) { return 0; } else if ($divisor == 0) { return 1; } else { return Piwik::getQuotientSafe($dividend, $divisor, $this->quotientPrecision + 2); } } public function format($value, Formatter $formatter) { return $formatter->getPrettyPercentFromQuotient($value); } public function getDependentMetrics() { return array($this->getWrappedName()); } public function beforeComputeSubtable(Row $row) { $this->labelPath[] = $row->getColumn('label'); } public function afterComputeSubtable(Row $row) { array_pop($this->labelPath); } protected function getWrappedName() { return $this->wrapped instanceof Metric ? $this->wrapped->getName() : $this->wrapped; } /** * public for Insights use. */ public function getPastRowFromCurrent(Row $row) { $pastData = $this->getPastDataTable(); if (empty($pastData)) { return null; } $label = $row->getColumn('label'); return $label ? $pastData->getRowFromLabel($label) : $pastData->getFirstRow(); } private function getPastDataTable() { $result = $this->pastData; foreach ($this->labelPath as $label) { $row = $result->getRowFromLabel($label); if (empty($row)) { return null; } $subtable = $row->getSubtable(); if (empty($subtable)) { return null; } $result = $subtable; } return $result; } /** * Calculate the ratio of time between a past period and current incomplete period * * eg. if today is Thursday at 12:00pm and the past period is a week then the ratio is 0.5, exactly half of the * current incomplete period has passed * * If the current period end is in the past then the ratio will always be 1, since the current period is complete. * * @param DataTable|null $currentData * @param DataTable|null $pastData * @param Row $row * @return float|int * @throws \Exception */ public static function getRatio(?DataTable $currentData, ?DataTable $pastData, Row $row) { $ratio = 1; if ($currentData != null && $pastData != null) { $p = $pastData->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX); $pStart = $p->getDateStart()->setTime('00:00:00'); $pEnd = $p->getDateEnd()->setTime('23:59:59'); $c = $currentData->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX); $cStart = $c->getDateStart()->setTime('00:00:00'); $cEnd = $c->getDateEnd()->setTime('23:59:59'); $nowTS = Date::getNowTimestamp(); // If we know the date the the datatable data was generated then use that instead of now $archivedDateStr = $row->getMetadata(DataTable::ARCHIVED_DATE_METADATA_NAME); if ($archivedDateStr) { $archivedDate = Date::factory($archivedDateStr); if ($archivedDate) { $nowTS = Date::factory($archivedDate)->getTimestamp(); } } if ($cStart->getTimestamp() <= $nowTS && $cEnd->getTimestamp() >= $nowTS) { $secsInPastPeriod = $pEnd->getTimestamp() - $pStart->getTimestamp(); $secsInCurrentPeriod = $nowTS - $cStart->getTimestamp(); $ratio = $secsInCurrentPeriod / $secsInPastPeriod; } } return round($ratio, 3); } }