Download plugins/CoreHome/DataTableRowAction/RowEvolution.php from Leon4gr45/matomo: direct link, hf CLI and curl.
- Browser
- Download file 18 kB
-
https://huggingface.co/spaces/Leon4gr45/matomo/resolve/main/plugins/CoreHome/DataTableRowAction/RowEvolution.php
- Command line
-
hf download hf://spaces/Leon4gr45/matomo/plugins/CoreHome/DataTableRowAction/RowEvolution.php
-
curl -L -o RowEvolution.php https://huggingface.co/spaces/Leon4gr45/matomo/resolve/main/plugins/CoreHome/DataTableRowAction/RowEvolution.php
18 kB
| /** | |
| * Matomo - free/libre analytics platform | |
| * | |
| * @link https://matomo.org | |
| * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later | |
| */ | |
| namespace Piwik\Plugins\CoreHome\DataTableRowAction; | |
| use Exception; | |
| use Piwik\API\DataTablePostProcessor; | |
| use Piwik\API\Request; | |
| use Piwik\Common; | |
| use Piwik\DataTable\DataTableInterface; | |
| use Piwik\DataTable\Map; | |
| use Piwik\Date; | |
| use Piwik\Metrics; | |
| use Piwik\NumberFormatter; | |
| use Piwik\Period\Factory as PeriodFactory; | |
| use Piwik\Piwik; | |
| use Piwik\Plugin\ViewDataTable; | |
| use Piwik\Plugins\API\Filter\DataComparisonFilter; | |
| use Piwik\Plugins\CoreVisualizations\Visualizations\Graph\Config as GraphConfig; | |
| use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Config as JqplotGraphConfig; | |
| use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution as EvolutionViz; | |
| use Piwik\ViewDataTable\Factory; | |
| use Piwik\ViewDataTable\Manager as ViewDataTableManager; | |
| /** | |
| * ROW EVOLUTION | |
| * The class handles the popover that shows the evolution of a single row in a data table | |
| */ | |
| class RowEvolution | |
| { | |
| /** | |
| * The current site id | |
| * @var int | |
| */ | |
| protected $idSite; | |
| /** | |
| * The api method to get the data. Format: Plugin.apiAction | |
| * @var string | |
| */ | |
| protected $apiMethod; | |
| /** | |
| * The label of the requested row | |
| * @var string | |
| */ | |
| protected $label; | |
| /** | |
| * The requested period | |
| * @var string | |
| */ | |
| protected $period; | |
| /** | |
| * The requested date | |
| * @var Date | |
| */ | |
| protected $date; | |
| /** | |
| * The request segment | |
| * @var string | |
| */ | |
| protected $segment; | |
| /** | |
| * The metrics that are available for the requested report and period | |
| * @var array<string, array{name: string, min?: number, max?: number, change?: string, logo?: string}> | |
| */ | |
| protected $availableMetrics; | |
| /** | |
| * The name of the dimension of the current report | |
| * @var string | |
| */ | |
| protected $dimension; | |
| /** | |
| * The data | |
| * @var Map | |
| */ | |
| protected $dataTable; | |
| /** | |
| * The label of the current record | |
| * @var string | |
| */ | |
| protected $rowLabel; | |
| /** | |
| * The icon of the current record | |
| * @var string|false | |
| */ | |
| protected $rowIcon; | |
| /** | |
| * The type of graph that has been requested last | |
| * @var string | |
| */ | |
| protected $graphType; | |
| /** | |
| * The metrics for the graph that has been requested last | |
| * @var array<string, array{name: string, min?: number, max?: number, change?: string, logo?: string}> | |
| */ | |
| protected $graphMetrics; | |
| /** | |
| * Whether or not to show all metrics in the evolution graph when to popover opens | |
| * @var bool | |
| */ | |
| protected $initiallyShowAllMetrics = false; | |
| /** | |
| * The constructor | |
| * Initialize some local variables from the request | |
| * @param int $idSite | |
| * @param Date $date ($this->date from controller) | |
| * @param string $graphType | |
| * @throws Exception | |
| */ | |
| public function __construct($idSite, $date, $graphType = 'graphEvolution') | |
| { | |
| $this->apiMethod = Common::getRequestVar('apiMethod', '', 'string'); | |
| if (empty($this->apiMethod)) { | |
| throw new Exception("Parameter apiMethod not set."); | |
| } | |
| $label = DataTablePostProcessor::getLabelFromRequest($_GET); | |
| if (!is_array($label)) { | |
| throw new Exception("Expected label to be an array, got instead: " . $label); | |
| } | |
| $this->label = Common::unsanitizeInputValue($label[0]); | |
| if ($this->label === '') { | |
| throw new Exception("Parameter label not set."); | |
| } | |
| $this->period = Common::getRequestVar('period', '', 'string'); | |
| PeriodFactory::checkPeriodIsEnabled($this->period); | |
| if ($this->period != 'range' && !($date instanceof Date)) { | |
| throw new Exception("Expected date to be an instance of \\Piwik\\Date"); | |
| } | |
| $this->idSite = $idSite; | |
| $this->graphType = $graphType; | |
| if ($this->period != 'range') { | |
| // handle day, week, month and year: display last X periods | |
| //handle cache if exist | |
| $cache = ViewDataTableManager::getViewDataTableParameters( | |
| Piwik::getCurrentUserLogin(), | |
| 'CoreHome.getRowEvolutionGraph' | |
| ); | |
| $lastDay = (isset($cache['evolution_' . $this->period . '_last_n']) ? $cache['evolution_' . $this->period . '_last_n'] : null); | |
| $end = $date->toString(); | |
| [$this->date, $lastN] = EvolutionViz::getDateRangeAndLastN($this->period, $end, $lastDay); | |
| } | |
| $this->segment = \Piwik\API\Request::getRawSegmentFromRequest(); | |
| $this->loadEvolutionReport(); | |
| } | |
| /** | |
| * Render the popover | |
| * @param \Piwik\Plugins\CoreHome\Controller $controller | |
| * @param \Piwik\View $view the popover_rowevolution template | |
| * @return string | |
| */ | |
| public function renderPopover($controller, $view) | |
| { | |
| // render main evolution graph | |
| $this->graphType = 'graphEvolution'; | |
| $this->graphMetrics = $this->availableMetrics; | |
| $view->graph = $this->getRowEvolutionGraphFromController($controller); | |
| // render metrics overview | |
| $view->metrics = $this->getMetricsToggles(); | |
| // available metrics text | |
| $metricsText = Piwik::translate('RowEvolution_AvailableMetrics'); | |
| $popoverTitle = ''; | |
| if ($this->rowLabel) { | |
| $icon = $this->rowIcon ? '<img height="16px" src="' . $this->rowIcon . '" alt="">' : ''; | |
| $rowLabel = str_replace('/', '<wbr>/', str_replace('&', '<wbr>&', $this->rowLabel)); | |
| $metricsText = sprintf(Piwik::translate('RowEvolution_MetricsFor'), $this->dimension . ': ' . $icon . ' ' . $rowLabel); | |
| $popoverTitle = $icon . ' ' . $this->rowLabel; | |
| } | |
| $view->availableMetricsText = $metricsText; | |
| $view->popoverTitle = $popoverTitle; | |
| return $view->render(); | |
| } | |
| /** | |
| * @param string|false|null $column | |
| * @return void | |
| */ | |
| protected function loadEvolutionReport($column = false) | |
| { | |
| [$apiModule, $apiAction] = explode('.', $this->apiMethod); | |
| // getQueryStringFromParameters expects sanitised query parameter values | |
| $parameters = array( | |
| 'method' => 'API.getRowEvolution', | |
| 'label' => $this->label, | |
| 'apiModule' => $apiModule, | |
| 'apiAction' => $apiAction, | |
| 'idSite' => $this->idSite, | |
| 'period' => $this->period, | |
| 'date' => $this->date, | |
| 'format' => 'original', | |
| 'serialize' => '0', | |
| ); | |
| if (!empty($this->segment)) { | |
| $parameters['segment'] = $this->segment; | |
| } | |
| if ($column !== false) { | |
| $parameters['column'] = $column; | |
| } | |
| $unmodifiedSeriesLabels = []; | |
| $isComparing = DataComparisonFilter::isCompareParamsPresent(); | |
| if ($isComparing) { | |
| $compareDates = Common::getRequestVar('compareDates', [], 'array'); | |
| $comparePeriods = Common::getRequestVar('comparePeriods', [], 'array'); | |
| $compareSegments = Common::getRequestVar('compareSegments', [], 'array'); | |
| $totalSeriesCount = (count($compareSegments) + 1) * (count($comparePeriods) + 1); | |
| for ($i = 0; $i < $totalSeriesCount; ++$i) { | |
| $unmodifiedSeriesLabels[] = DataComparisonFilter::getPrettyComparisonLabelFromSeriesIndex($i); | |
| } | |
| $parameters['compare'] = 1; | |
| foreach ($comparePeriods as $index => $period) { | |
| $date = $compareDates[$index]; | |
| if ($period == 'range') { | |
| $comparePeriods[$index] = 'day'; | |
| } else { | |
| [$newDate, $lastN] = EvolutionViz::getDateRangeAndLastN($period, $date); | |
| $compareDates[$index] = $newDate; | |
| } | |
| } | |
| $parameters['compareDates'] = $compareDates; | |
| $parameters['comparePeriods'] = $comparePeriods; | |
| } | |
| $parameters = array_filter($parameters, function ($value) { | |
| return $value !== null && $value !== false; | |
| }); | |
| /** @var array{label: string, reportData: Map, metadata: array{metrics: array, dimension: string, columns: array}} $report */ | |
| $report = Request::processRequest('API.getRowEvolution', $parameters); | |
| // at this point the report data will reference the comparison series labels for the changed compare periods/dates. We don't | |
| // want to show this to users because they will not recognize the changed periods, so we have to replace them. | |
| if ($isComparing) { | |
| $dataTables = $report['reportData']->getDataTables(); | |
| $modifiedSeriesLabels = reset($dataTables)->getMetadata('comparisonSeries'); | |
| $seriesMap = array_combine($modifiedSeriesLabels, $unmodifiedSeriesLabels); | |
| foreach ($report['metadata']['metrics'] as $key => $metricInfo) { | |
| foreach ($seriesMap as $modified => $unmodified) { | |
| $report['metadata']['metrics'][$key]['name'] = str_replace($modified, $unmodified, $report['metadata']['metrics'][$key]['name']); | |
| } | |
| } | |
| } | |
| $this->extractEvolutionReport($report); | |
| } | |
| /** | |
| * @param array $report | |
| * @return void | |
| */ | |
| protected function extractEvolutionReport($report) | |
| { | |
| $this->dataTable = $report['reportData']; | |
| $this->rowLabel = $this->extractPrettyLabel($report); | |
| $this->rowIcon = !empty($report['logo']) ? $report['logo'] : false; | |
| $this->availableMetrics = $report['metadata']['metrics']; | |
| $this->dimension = $report['metadata']['dimension']; | |
| } | |
| /** | |
| * Generic method to get an evolution graph or a sparkline for the row evolution popover. | |
| * Do as much as possible from outside the controller. | |
| * @param string|bool $graphType | |
| * @param array|bool $metrics | |
| * @return ViewDataTable | |
| */ | |
| public function getRowEvolutionGraph($graphType = false, $metrics = false) | |
| { | |
| // set up the view data table | |
| $view = Factory::build( | |
| $graphType ? : $this->graphType, | |
| $this->apiMethod, | |
| $controllerAction = 'CoreHome.getRowEvolutionGraph', | |
| $forceDefault = true | |
| ); | |
| $view->setDataTable($this->dataTable); | |
| if (!empty($this->graphMetrics)) { // In row Evolution popover, this is empty | |
| $view->config->columns_to_display = array_keys($metrics ? : $this->graphMetrics); | |
| } | |
| $view->requestConfig->request_parameters_to_modify['label'] = ''; | |
| $view->config->export_parameters_to_modify['label'] = $this->label; | |
| $view->config->show_flatten_table_export = false; | |
| $view->config->show_goals = false; | |
| $view->config->show_search = false; | |
| $view->config->show_all_views_icons = false; | |
| $view->config->show_related_reports = false; | |
| $view->config->show_footer_message = false; | |
| foreach ($this->availableMetrics as $metric => $metadata) { | |
| $view->config->translations[$metric] = $metadata['name']; | |
| } | |
| if ($view->config instanceof GraphConfig) { | |
| $view->config->show_series_picker = false; | |
| } | |
| if ($view->config instanceof JqplotGraphConfig) { | |
| $view->config->external_series_toggle = 'RowEvolutionSeriesToggle'; | |
| $view->config->external_series_toggle_show_all = $this->initiallyShowAllMetrics; | |
| } | |
| return $view; | |
| } | |
| /** | |
| * Prepare metrics toggles with spark lines | |
| * @return array | |
| */ | |
| protected function getMetricsToggles() | |
| { | |
| $i = 0; | |
| $metrics = array(); | |
| foreach ($this->availableMetrics as $metric => $metricData) { | |
| $unit = Metrics::getUnit($metric, $this->idSite); | |
| $change = isset($metricData['change']) ? $metricData['change'] : false; | |
| [$first, $last] = $this->getFirstAndLastDataPointsForMetric($metric); | |
| $fractionDigits = max($this->getFractionDigits($first), $this->getFractionDigits($last)); | |
| $details = Piwik::translate('RowEvolution_MetricBetweenText', array( | |
| NumberFormatter::getInstance()->format($first, $fractionDigits) . $unit, | |
| NumberFormatter::getInstance()->format($last, $fractionDigits) . $unit, | |
| )); | |
| if ($change !== false) { | |
| $lowerIsBetter = Metrics::isLowerValueBetter($metric); | |
| if (substr($change, 0, 1) == '+') { | |
| $changeClass = $lowerIsBetter ? 'bad' : 'good'; | |
| $changeImage = $lowerIsBetter ? 'arrow_up_red' : 'arrow_up'; | |
| } elseif (substr($change, 0, 1) == '-') { | |
| $changeClass = $lowerIsBetter ? 'good' : 'bad'; | |
| $changeImage = $lowerIsBetter ? 'arrow_down_green' : 'arrow_down'; | |
| } else { | |
| $changeClass = 'neutral'; | |
| $changeImage = false; | |
| } | |
| $change = '<span class="' . $changeClass . '">' | |
| . ($changeImage ? '<img src="plugins/MultiSites/images/' . $changeImage . '.png" /> ' : '') | |
| . $change . '</span>'; | |
| $details .= ', ' . Piwik::translate('RowEvolution_MetricChangeText', $change); | |
| } | |
| // set metric min/max text (used as tooltip for details) | |
| $max = isset($metricData['max']) ? $metricData['max'] : 0; | |
| $min = isset($metricData['min']) ? $metricData['min'] : 0; | |
| $minmax = Piwik::translate('RowEvolution_MetricMinMax', array( | |
| $metricData['name'], | |
| NumberFormatter::getInstance()->formatNumber($min, $fractionDigits, $fractionDigits) . $unit, | |
| NumberFormatter::getInstance()->formatNumber($max, $fractionDigits, $fractionDigits) . $unit, | |
| )); | |
| $newMetric = array( | |
| 'label' => $metricData['name'], | |
| 'details' => $details, | |
| 'minmax' => $minmax, | |
| 'sparkline' => $this->getSparkline($metric), | |
| ); | |
| // Multi Rows, each metric can be for a particular row and display an icon | |
| if (!empty($metricData['logo'])) { | |
| $newMetric['logo'] = $metricData['logo']; | |
| } | |
| // TODO: this check should be determined by metric metadata, not hardcoded here | |
| if ( | |
| $metric == 'nb_users' | |
| && $first == 0 | |
| && $last == 0 | |
| ) { | |
| $newMetric['hide'] = true; | |
| } | |
| $metrics[] = $newMetric; | |
| $i++; | |
| } | |
| return $metrics; | |
| } | |
| /** | |
| * Get the img tag for a sparkline showing a single metric | |
| * | |
| * @param string $metric | |
| * @return string | |
| */ | |
| protected function getSparkline($metric) | |
| { | |
| // sparkline is always echoed, so we need to buffer the output | |
| $view = $this->getRowEvolutionGraph($graphType = 'sparkline', $metrics = array($metric => $metric)); | |
| ob_start(); | |
| $view->render(); | |
| $spark = ob_get_contents(); | |
| ob_end_clean(); | |
| // undo header change by sparkline renderer | |
| Common::sendHeader('Content-type: text/html'); | |
| // base64 encode the image and put it in an img tag | |
| $spark = base64_encode($spark); | |
| return '<img width="100" height="25" src="data:image/png;base64,' . $spark . '" />'; | |
| } | |
| /** | |
| * Use the available metrics for the metrics of the last requested graph. | |
| * @return void | |
| */ | |
| public function useAvailableMetrics() | |
| { | |
| $this->graphMetrics = $this->availableMetrics; | |
| } | |
| /** | |
| * @param string|int $metric | |
| * @return array<int|float> | |
| */ | |
| private function getFirstAndLastDataPointsForMetric($metric) | |
| { | |
| $first = 0; | |
| $firstTable = $this->dataTable->getFirstRow(); | |
| if (!empty($firstTable)) { | |
| $row = $firstTable->getFirstRow(); | |
| if (!empty($row)) { | |
| $first = floatval($row->getColumn($metric)); | |
| } | |
| } | |
| $last = 0; | |
| $lastTable = $this->dataTable->getLastRow(); | |
| if (!empty($lastTable)) { | |
| $row = $lastTable->getFirstRow(); | |
| if (!empty($row)) { | |
| $last = floatval($row->getColumn($metric)); | |
| } | |
| } | |
| return array($first, $last); | |
| } | |
| /** | |
| * @param array $report | |
| * @return string | |
| */ | |
| protected function extractPrettyLabel($report) | |
| { | |
| // By default, use the specified label | |
| $rowLabel = Common::sanitizeInputValue($report['label']); | |
| /** @var \Piwik\DataTable\Map $dataTableMap */ | |
| $dataTableMap = $report['reportData']; | |
| // If the dataTable specifies a label_html, use this instead | |
| if ($dataTableMap instanceof DataTableInterface) { | |
| $labelPretty = $dataTableMap->getColumn('label_html'); | |
| $labelPretty = array_filter($labelPretty, 'strlen'); | |
| $labelPretty = current($labelPretty); | |
| if (!empty($labelPretty)) { | |
| return $labelPretty; | |
| } | |
| } | |
| return $rowLabel; | |
| } | |
| /** | |
| * @param string|int|float $value | |
| */ | |
| private function getFractionDigits($value): int | |
| { | |
| $value = (string) $value; | |
| $fraction = substr(strrchr($value, ".") ?: '', 1); | |
| return strlen($fraction); | |
| } | |
| /** | |
| * @return string|null | |
| */ | |
| protected function getRowEvolutionGraphFromController(\Piwik\Plugins\CoreHome\Controller $controller) | |
| { | |
| return $controller->getRowEvolutionGraph($fetch = true, $rowEvolution = $this); | |
| } | |
| } | |