diff --git a/core/Plugin/Report.php b/core/Plugin/Report.php index 004c5ae3db4..2a70aeb6a8b 100644 --- a/core/Plugin/Report.php +++ b/core/Plugin/Report.php @@ -21,10 +21,6 @@ use Piwik\Plugin\Dimension\ActionDimension; use Piwik\Plugin\Dimension\ConversionDimension; use Piwik\Plugin\Dimension\VisitDimension; -use Piwik\Plugins\CoreHome\Columns\Metrics\BounceRate; -use Piwik\Plugins\CoreHome\Columns\Metrics\ActionsPerVisit; -use Piwik\Plugins\CoreHome\Columns\Metrics\AverageTimeOnSite; -use Piwik\Plugins\CoreHome\Columns\Metrics\ConversionRate; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; use Piwik\ViewDataTable\Factory as ViewDataTableFactory; use Exception; @@ -370,15 +366,26 @@ public function render() } /** - * * Processing a uniqueId for each report, can be used by UIs as a key to match a given report + * * @return string */ public function getId() { - $params = $this->getParameters(); + return self::buildId($this->getModule(), $this->getAction(), $this->getParameters()); + } - $paramsKey = $this->getModule() . '.' . $this->getAction(); + /** + * TODO + * + * @param string $module + * @param string $action + * @param array|null $params + * @return string + */ + public static function buildId(string $module, string $action, ?array $params = null): string + { + $paramsKey = $module . '.' . $action; if (!empty($params)) { foreach ($params as $key => $value) { @@ -436,7 +443,7 @@ public function getRecursiveLabelSeparator() */ public function getMetrics() { - return $this->getMetricTranslations($this->metrics); + return self::getMetricTranslations($this->metrics); } /** @@ -494,7 +501,7 @@ public function getProcessedMetrics() return $this->processedMetrics; } - return $this->getMetricTranslations($this->processedMetrics); + return self::getMetricTranslations($this->processedMetrics); } /** @@ -795,7 +802,10 @@ protected function buildReportMetadata() $report['metrics'] = $this->getMetrics(); $report['metricsDocumentation'] = $this->getMetricsDocumentation(); - $processedMetricMetadata = $this->getProcessedMetricsMetadata(); + $processedMetricMetadata = self::getProcessedMetricsMetadata( + $this->processedMetrics ?: [], + $this->getProcessedMetrics() ?: null + ); $report['processedMetrics'] = $processedMetricMetadata['names']; $report['processedMetricFormulas'] = $processedMetricMetadata['formulas']; $report['temporaryMetricAggregationTypes'] = $processedMetricMetadata['temporaryMetricAggregationTypes']; @@ -927,7 +937,8 @@ public function getParameters() } /** - * Get the translated name of the category the report belongs to. + * Get the ID of the category the report belongs to. + * The ID should also be a valid translation key. * @return string|null * @ignore */ @@ -937,8 +948,9 @@ public function getCategoryId() } /** - * Get the translated name of the subcategory the report belongs to. + * Get the ID of the subcategory the report belongs to. * @return string|null + * The ID should also be a valid translation key. * @ignore */ public function getSubcategoryId() @@ -1094,7 +1106,7 @@ public function fetchSubtable($idSubtable, $paramOverride = array()) return Request::processRequest($module . '.' . $action, $paramOverride); } - private function getMetricTranslations($metricsToTranslate) + private static function getMetricTranslations($metricsToTranslate) { $translations = Metrics::getDefaultMetricTranslations(); $metrics = array(); @@ -1341,20 +1353,20 @@ private function isScopeSameOrSubsetOf(?string $scope, string $supersetScope): b return false; } - private function getProcessedMetricsMetadata(): array + public static function getProcessedMetricsMetadata(?array $processedMetrics, ?array $processedMetricTranslations = null): array { $metadata = [ - 'names' => $this->getProcessedMetrics() ?: [], + 'names' => $processedMetricTranslations ?: self::getMetricTranslations($processedMetrics ?: []), 'formulas' => [], 'temporaryMetricAggregationTypes' => [], 'temporaryMetricSemanticTypes' => [], ]; - if (empty($this->processedMetrics)) { + if (empty($processedMetrics)) { return $metadata; } - foreach ($this->processedMetrics as $processedMetric) { + foreach ($processedMetrics as $processedMetric) { if (!($processedMetric instanceof ProcessedMetric)) { continue; } diff --git a/plugins/API/ProcessedReport.php b/plugins/API/ProcessedReport.php index 40c1efe3f37..7d0d5b8c73a 100644 --- a/plugins/API/ProcessedReport.php +++ b/plugins/API/ProcessedReport.php @@ -17,7 +17,9 @@ use Piwik\Common; use Piwik\Container\StaticContainer; use Piwik\DataTable; +use Piwik\DataTable\DataTableInterface; use Piwik\DataTable\Filter\AddColumnsProcessedMetricsGoal; +use Piwik\DataTable\Map; use Piwik\DataTable\Row; use Piwik\DataTable\Simple; use Piwik\Date; @@ -25,7 +27,10 @@ use Piwik\Metrics\Formatter; use Piwik\Period; use Piwik\Piwik; +use Piwik\Plugin\ProcessedMetric; +use Piwik\Plugin\Report; use Piwik\Plugin\ReportsProvider; +use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecificProcessedMetric; use Piwik\Site; use Piwik\Timer; use Piwik\Url; @@ -179,9 +184,10 @@ public function translateMetric($metric, $idSite, $apiMethodUniqueId) * @param bool|Date $date * @param bool $hideMetricsDoc * @param bool $showSubtableReports + * @param array $dataTables TODO * @return array */ - public function getReportMetadata($idSite, $period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false) + public function getReportMetadata($idSite, $period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false, ?array $dataTables = null) { Piwik::checkUserHasViewAccess($idSite); @@ -200,7 +206,8 @@ public function getReportMetadata($idSite, $period = false, $date = false, $hide $availableReports = array(); foreach ($this->reportsProvider->getAllReports() as $report) { - $report->configureReportMetadata($availableReports, $parameters); + $dataTable = $dataTables[$report->getId()] ?? null; + $report->configureReportMetadata($availableReports, $parameters, $dataTable); } foreach ($availableReports as &$availableReport) { @@ -432,6 +439,8 @@ public function getProcessedReport( list($newReport, $columns, $rowsMetadata, $totals) = $this->handleTableReport($idSite, $dataTable, $reportMetadata, $showRawMetrics, $formatMetrics); + $this->handleExtraProcessedMetricsMetadata($dataTable, $reportMetadata); + if (function_exists('mb_substr')) { foreach ($columns as &$name) { if (substr($name, 0, 1) === mb_substr($name, 0, 1)) { @@ -946,4 +955,38 @@ private function getIdGoalToUseForActionsReports($idGoal, string $requestMethod) } return $idGoal; } + + private function handleExtraProcessedMetricsMetadata(DataTableInterface $reportData, array &$reportMetadata): void + { + $dataTable = $this->getFirstDataTable($reportData); + if (empty($dataTable)) { + return; + } + + $extraProcessedMetrics = $dataTable->getMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME); + if (empty($extraProcessedMetrics)) { + return; + } + + $extraProcessedMetrics = array_filter($extraProcessedMetrics, function ($processedMetric) { + return $processedMetric instanceof ProcessedMetric + && !($processedMetric instanceof GoalSpecificProcessedMetric); + }); + + $extraMetadata = Report::getProcessedMetricsMetadata($extraProcessedMetrics); + + $reportMetadata['processedMetrics'] = array_merge($reportMetadata['processedMetrics'] ?? [], $extraMetadata['names']); + $reportMetadata['processedMetricFormulas'] = array_merge($reportMetadata['processedMetricFormulas'] ?? [], $extraMetadata['formulas']); + $reportMetadata['temporaryMetricAggregationTypes'] = array_merge($reportMetadata['temporaryMetricAggregationTypes'] ?? [], $extraMetadata['temporaryMetricAggregationTypes']); + $reportMetadata['temporaryMetricSemanticTypes'] = array_merge($reportMetadata['temporaryMetricSemanticTypes'] ?? [], $extraMetadata['temporaryMetricSemanticTypes']); + } + + private function getFirstDataTable(DataTableInterface $reportData) + { + $table = $reportData; + while ($table instanceof Map) { + $table = $table->getFirstRow(); + } + return $table; + } } diff --git a/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php b/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php index 846c02379dd..fe5e6854fab 100644 --- a/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php +++ b/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php @@ -53,11 +53,6 @@ public function getMetrics() ); } - public function getProcessedMetrics() - { - return array(); - } - protected function getMetricsDocumentation() { return array( diff --git a/tests/PHPUnit/System/ApiGetReportMetadataTest.php b/tests/PHPUnit/System/ApiGetReportMetadataTest.php index 1b854d226b0..8cf8b6a778c 100644 --- a/tests/PHPUnit/System/ApiGetReportMetadataTest.php +++ b/tests/PHPUnit/System/ApiGetReportMetadataTest.php @@ -80,6 +80,21 @@ public function getApiForTesting() ], ], + [ + 'API.getProcessedReport', + [ + 'idSite' => $idSite, + 'date' => $dateTime, + 'apiModule' => 'UserCountry', + 'apiAction' => 'getCountry', + 'testSuffix' => '_withExtraProcessedMetrics', + 'otherRequestParameters' => [ + 'filter_update_columns_when_show_all_goals' => '1', + 'idGoal' => '0', + ], + ], + ], + // Test w/ showRawMetrics=true [ 'API.getProcessedReport', diff --git a/tests/PHPUnit/System/ApiGetReportMetadataYearTest.php b/tests/PHPUnit/System/ApiGetReportMetadataYearTest.php index 72dee9be5d5..a9324c1d9e8 100644 --- a/tests/PHPUnit/System/ApiGetReportMetadataYearTest.php +++ b/tests/PHPUnit/System/ApiGetReportMetadataYearTest.php @@ -29,6 +29,7 @@ public function getApiForTesting() 'date' => self::$fixture->dateTime, 'periods' => 'year', 'language' => 'fr'); + return [array('API.getProcessedReport', $params)]; return array( array('API.getProcessedReport', $params), array('LanguagesManager.getAvailableLanguageNames', $params), diff --git a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getProcessedReport_day.xml index 0a51612d31f..f23bdfae283 100644 --- a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getProcessedReport_day.xml +++ b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getProcessedReport_day.xml @@ -29,9 +29,14 @@ Avg. Time on Website Bounce Rate Revenue per Visit + Conversion Rate $revenue / ($nb_visits != 0 ? $nb_visits : $nb_conversions) + $nb_visits_converted / $nb_visits + $nb_actions / $nb_visits + $sum_visit_length / $nb_visits + $bounce_count / $nb_visits number @@ -72,6 +77,14 @@ index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=UserCountry&apiAction=getCountry&period=day&date=2009-01-04 index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=UserCountry&apiAction=getCountry&period=day&date=2008-12-06,2009-01-04 UserCountry_getCountry + + sum + sum + + + number + number + diff --git a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getReportMetadata_day.xml b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getReportMetadata_day.xml index 472657fb71c..865e57f2234 100644 --- a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getReportMetadata_day.xml +++ b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getReportMetadata_day.xml @@ -1713,8 +1713,6 @@ sum - - Ecommerce order Revenue per Visit @@ -2128,8 +2126,6 @@ sum - - Ecommerce order Revenue per Visit @@ -2207,6 +2203,12 @@ The number of times this Page was visited after a visitor did a search on your website, and clicked on this page in the search results. The number of times this page was visited. + + Avg. time on page + Bounce Rate + Exit rate + Avg. generation time + $sum_time_spent / $nb_hits $entry_bounce_count / $entry_nb_visits @@ -2297,6 +2299,12 @@ The number of times this Page was visited after a visitor did a search on your website, and clicked on this page in the search results. The number of times this page was visited. + + Avg. time on page + Bounce Rate + Exit rate + Avg. generation time + $sum_time_spent / $nb_hits $entry_bounce_count / $entry_nb_visits diff --git a/tests/PHPUnit/System/expected/test_apiGetReportMetadata_showRawMetrics__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_apiGetReportMetadata_showRawMetrics__API.getProcessedReport_day.xml index 99b62f5c732..55389cd5bf2 100644 --- a/tests/PHPUnit/System/expected/test_apiGetReportMetadata_showRawMetrics__API.getProcessedReport_day.xml +++ b/tests/PHPUnit/System/expected/test_apiGetReportMetadata_showRawMetrics__API.getProcessedReport_day.xml @@ -29,9 +29,14 @@ Avg. Time on Website Bounce Rate Revenue per Visit + Conversion Rate $revenue / ($nb_visits != 0 ? $nb_visits : $nb_conversions) + $nb_visits_converted / $nb_visits + $nb_actions / $nb_visits + $sum_visit_length / $nb_visits + $bounce_count / $nb_visits number @@ -72,6 +77,14 @@ index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=UserCountry&apiAction=getCountry&period=day&date=2009-01-04 index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=UserCountry&apiAction=getCountry&period=day&date=2008-12-06,2009-01-04 UserCountry_getCountry + + sum + sum + + + number + number + diff --git a/tests/PHPUnit/System/expected/test_apiGetReportMetadata_withExtraProcessedMetrics__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_apiGetReportMetadata_withExtraProcessedMetrics__API.getProcessedReport_day.xml new file mode 100644 index 00000000000..54a43cc70ed --- /dev/null +++ b/tests/PHPUnit/System/expected/test_apiGetReportMetadata_withExtraProcessedMetrics__API.getProcessedReport_day.xml @@ -0,0 +1,147 @@ + + + Piwik test + Sunday, January 4, 2009 + + Visitors + Locations + Country + UserCountry + getCountry + Country + Shows which country your visitors connected from when accessing your website. + + Visits + Unique visitors + Actions + + + If a visitor comes to your website for the first time or if they visit a page more than 30 minutes after their last page view, this will be recorded as a new visit. + The number of unduplicated visitors coming to your website. Every user is only counted once, even if they visit the website multiple times a day. + The number of actions performed by your visitors. Actions can be page views, internal site searches, downloads or outlinks. + The average number of actions (page views, site searches, downloads or outlinks) that were performed during the visits. + The average duration of a visit. + The percentage of visits that only had a single pageview. This means, that the visitor left the website directly from the entrance page. + The percentage of visits that triggered a goal conversion. + + + Actions per Visit + Avg. Time on Website + Bounce Rate + Conversion Rate + Revenue per Visit + + + ($goals["idgoal=3"].revenue) / ($nb_visits != 0 ? $nb_visits : $nb_conversions) + $nb_visits_converted / $nb_visits + $nb_actions / $nb_visits + $sum_visit_length / $nb_visits + $bounce_count / $nb_visits + + + number + number + number + number + duration_s + percent + percent + money + + + sum + sum + + + Conversions + Revenue + + + number + money + money + percent + + + sum + sum + + + Ecommerce order Revenue per Visit + Ecommerce order conversion rate + + + $goals["idgoal={idGoal}"].revenue / ($nb_visits == 0 ? $goals["idgoal={idGoal}"].nb_conversions : $nb_visits) + $goals["idgoal={idGoal}"].nb_conversions / $nb_visits + + index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=UserCountry&apiAction=getCountry&period=day&date=2009-01-04 + index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=UserCountry&apiAction=getCountry&period=day&date=2008-12-06,2009-01-04 + UserCountry_getCountry + + sum + sum + + + number + number + + + + + Visits + Unique visitors + Actions + Actions per Visit + Avg. Time on Website + Bounce Rate + Conversion Rate + Revenue per Visit + Revenue + + + + + 1 + 1 + 1 + $42.26 + 100% + 1 + 00:18:06 + 100% + $42 + 100% + 1 + $42.26 + $42.26 + + + + + fr + plugins/Morpheus/icons/dist/flags/fr.png + countryCode==fr + 16 + + + + 1 + 1 + 1 + 0 + 1 + 1086 + 1 + 1 + + + 1 + 1 + 42.26 + + + 1 + 42.26 + 1 + + \ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_apiGetReportMetadata_year__API.getProcessedReport_year.xml b/tests/PHPUnit/System/expected/test_apiGetReportMetadata_year__API.getProcessedReport_year.xml index 4eb0835b059..06e3a127b6c 100644 --- a/tests/PHPUnit/System/expected/test_apiGetReportMetadata_year__API.getProcessedReport_year.xml +++ b/tests/PHPUnit/System/expected/test_apiGetReportMetadata_year__API.getProcessedReport_year.xml @@ -28,9 +28,14 @@ Temps moyen sur le site web Taux de rebond Revenu par visite + Taux de conversion $revenue / ($nb_visits != 0 ? $nb_visits : $nb_conversions) + $nb_visits_converted / $nb_visits + $nb_actions / $nb_visits + $sum_visit_length / $nb_visits + $bounce_count / $nb_visits number @@ -71,6 +76,14 @@ index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=UserCountry&apiAction=getCountry&period=year&date=2009-01-04 index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=UserCountry&apiAction=getCountry&period=year&date=2000-01-01,2009-12-31 UserCountry_getCountry + + sum + sum + + + number + number +