Skip to content
Extraits de code Groupes Projets
ProcessedReport.php 34,5 ko
Newer Older
  • Learn to ignore specific revisions
  •  * Piwik - free/libre analytics platform
    
     *
     * @link http://piwik.org
     * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
     *
     */
    namespace Piwik\Plugins\API;
    
    use Exception;
    
    use Piwik\Archive\DataTableFactory;
    
    use Piwik\Cache\PluginAwareStaticCache;
    
    use Piwik\DataTable;
    
    use Piwik\DataTable\Simple;
    use Piwik\Date;
    
    use Piwik\MetricsFormatter;
    
    use Piwik\Period;
    
    mattab's avatar
    mattab a validé
    use Piwik\Piwik;
    
    use Piwik\Site;
    
    use Piwik\Timer;
    use Piwik\Url;
    
    {
        /**
         * Loads reports metadata, then return the requested one,
         * matching optional API parameters.
         */
        public function getMetadata($idSite, $apiModule, $apiAction, $apiParameters = array(), $language = false,
                                    $period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false)
        {
            $reportsMetadata = $this->getReportMetadata($idSite, $period, $date, $hideMetricsDoc, $showSubtableReports);
    
            foreach ($reportsMetadata as $report) {
    
                // See ArchiveProcessor/Aggregator.php - unique visitors are not processed for period != day
    
                // todo: should use SettingsPiwik::isUniqueVisitorsEnabled instead
    
                if (($period && $period != 'day') && !($apiModule == 'VisitsSummary' && $apiAction == 'get')) {
                    unset($report['metrics']['nb_uniq_visitors']);
                }
                if ($report['module'] == $apiModule
                    && $report['action'] == $apiAction
                ) {
                    // No custom parameters
                    if (empty($apiParameters)
                        && empty($report['parameters'])
                    ) {
                        return array($report);
                    }
                    if (empty($report['parameters'])) {
                        continue;
                    }
                    $diff = array_diff($report['parameters'], $apiParameters);
                    if (empty($diff)) {
                        return array($report);
                    }
                }
            }
            return false;
        }
    
    
        /**
         * Verfies whether the given report exists for the given site.
         *
         * @param int $idSite
    
         * @param string $apiMethodUniqueId  For example 'MultiSites_getAll'
    
        public function isValidReportForSite($idSite, $apiMethodUniqueId)
    
            $report = $this->getReportMetadataByUniqueId($idSite, $apiMethodUniqueId);
    
    
            return !empty($report);
        }
    
        /**
         * Verfies whether the given metric belongs to the given report.
         *
         * @param int $idSite
         * @param string $metric     For example 'nb_visits'
    
         * @param string $apiMethodUniqueId  For example 'MultiSites_getAll'
    
        public function isValidMetricForReport($metric, $idSite, $apiMethodUniqueId)
    
            $translation = $this->translateMetric($metric, $idSite, $apiMethodUniqueId);
    
        public function getReportMetadataByUniqueId($idSite, $apiMethodUniqueId)
    
            $metadata = $this->getReportMetadata(array($idSite));
    
            foreach ($metadata as $report) {
                if ($report['uniqueId'] == $apiMethodUniqueId) {
                    return $report;
                }
    
            }
        }
    
        /**
         * Translates the given metric in case the report exists and in case the metric acutally belongs to the report.
         *
         * @param string $metric     For example 'nb_visits'
         * @param int    $idSite
    
         * @param string $apiMethodUniqueId  For example 'MultiSites_getAll'
    
        public function translateMetric($metric, $idSite, $apiMethodUniqueId)
    
            $report = $this->getReportMetadataByUniqueId($idSite, $apiMethodUniqueId);
    
    
            if (empty($report)) {
                return;
            }
    
            $properties = array('metrics', 'processedMetrics', 'processedMetricsGoal');
    
            foreach ($properties as $prop) {
                if (!empty($report[$prop]) && is_array($report[$prop]) && array_key_exists($metric, $report[$prop])) {
                    return $report[$prop][$metric];
                }
            }
        }
    
    
        /**
         * Triggers a hook to ask plugins for available Reports.
         * Returns metadata information about each report (category, name, dimension, metrics, etc.)
         *
         * @param string $idSites Comma separated list of website Ids
    
    sgiehl's avatar
    sgiehl a validé
         * @param bool|string $period
    
    sgiehl's avatar
    sgiehl a validé
         * @param bool $hideMetricsDoc
         * @param bool $showSubtableReports
    
         * @return array
         */
        public function getReportMetadata($idSites, $period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false)
        {
    
            $idSites = Site::getIdSitesFromIdSitesString($idSites);
    
            if (!empty($idSites)) {
                Piwik::checkUserHasViewAccess($idSites);
            }
    
    
            // as they cache key contains a lot of information there would be an even better cache result by caching parts of
            // this huge method separately but that makes it also more complicated. leaving it like this for now.
            $key   = $this->buildReportMetadataCacheKey($idSites, $period, $date, $hideMetricsDoc, $showSubtableReports);
    
            $cache = new PluginAwareStaticCache($key);
    
            if ($cache->has()) {
                return $cache->get();
            }
    
    
            $parameters = array('idSites' => $idSites, 'period' => $period, 'date' => $date);
    
            $availableReports = array();
    
            foreach (Report::getAllReports() as $report) {
    
                $report->configureReportMetadata($availableReports, $parameters);
    
             * Triggered when gathering metadata for all available reports.
    
             * Plugins that define new reports should use this event to make them available in via
             * the metadata API. By doing so, the report will become available in scheduled reports
             * as well as in the Piwik Mobile App. In fact, any third party app that uses the metadata
             * API will automatically have access to the new report.
    
             * @param string &$availableReports The list of available reports. Append to this list
             *                                  to make a report available.
    
             *                                  Every element of this array must contain the following
             *                                  information:
    
             *                                  - **category**: A translated string describing the report's category.
             *                                  - **name**: The translated display title of the report.
             *                                  - **module**: The plugin of the report.
             *                                  - **action**: The API method that serves the report.
    
             *                                  The following information is optional:
    
             *                                  - **dimension**: The report's [dimension](/guides/all-about-analytics-data#dimensions) if any.
             *                                  - **metrics**: An array mapping metric names with their display names.
             *                                  - **metricsDocumentation**: An array mapping metric names with their
             *                                                              translated documentation.
             *                                  - **processedMetrics**: The array of metrics in the report that are
             *                                                          calculated using existing metrics. Can be set to
             *                                                          `false` if the report contains no processed
             *                                                          metrics.
             *                                  - **order**: The order of the report in the list of reports
             *                                               with the same category.
    
             * @param array $parameters Contains the values of the sites and period we are
    
             *                          getting reports for. Some reports depend on this data.
    
             *                          For example, Goals reports depend on the site IDs being
    
             *                          requested. Contains the following information:
    
             *                          - **idSites**: The array of site IDs we are getting reports for.
             *                          - **period**: The period type, eg, `'day'`, `'week'`, `'month'`,
             *                                        `'year'`, `'range'`.
             *                          - **date**: A string date within the period or a date range, eg,
             *                                      `'2013-01-01'` or `'2012-01-01,2013-01-01'`.
    
             * TODO: put dimensions section in all about analytics data
    
             * @deprecated since 2.5.0 Use Report Classes instead.
             * @ignore
    
            Piwik::postEvent('API.getReportMetadata', array(&$availableReports, $parameters));
    
            // TODO we can remove this one once we remove API.getReportMetadata event (except hideMetricsDoc)
    
            foreach ($availableReports as &$availableReport) {
    
    Thomas Steur's avatar
    Thomas Steur a validé
                // can be removed once we remove hook API.getReportMetadata
    
                if (!isset($availableReport['metrics'])) {
    
                    $availableReport['metrics'] = Metrics::getDefaultMetrics();
    
    Thomas Steur's avatar
    Thomas Steur a validé
                // can be removed once we remove hook API.getReportMetadata
    
                if (!isset($availableReport['processedMetrics'])) {
    
                    $availableReport['processedMetrics'] = Metrics::getDefaultProcessedMetrics();
    
                }
    
                if ($hideMetricsDoc) // remove metric documentation if it's not wanted
                {
                    unset($availableReport['metricsDocumentation']);
                } else if (!isset($availableReport['metricsDocumentation'])) {
                    // set metric documentation to default if it's not set
    
    Thomas Steur's avatar
    Thomas Steur a validé
                    // can be removed once we remove hook API.getReportMetadata
    
                    $availableReport['metricsDocumentation'] = Metrics::getDefaultMetricsDocumentation();
    
             * Triggered after all available reports are collected.
    
             * This event can be used to modify the report metadata of reports in other plugins. You
             * could, for example, add custom metrics to every report or remove reports from the list
             * of available reports.
    
             * @param array &$availableReports List of all report metadata. Read the {@hook API.getReportMetadata}
             *                                 docs to see what this array contains.
    
             * @param array $parameters Contains the values of the sites and period we are
             *                          getting reports for. Some report depend on this data.
             *                          For example, Goals reports depend on the site IDs being
             *                          request. Contains the following information:
    
             *                          - **idSites**: The array of site IDs we are getting reports for.
             *                          - **period**: The period type, eg, `'day'`, `'week'`, `'month'`,
             *                                        `'year'`, `'range'`.
             *                          - **date**: A string date within the period or a date range, eg,
             *                                      `'2013-01-01'` or `'2012-01-01,2013-01-01'`.
    
            Piwik::postEvent('API.getReportMetadata.end', array(&$availableReports, $parameters));
    
            // Sort results to ensure consistent order
            usort($availableReports, array('self', 'sortReports'));
    
    
            // Add the magic API.get report metadata aggregating all plugins API.get API calls automatically
            $this->addApiGetMetdata($availableReports);
    
    
            $knownMetrics = array_merge(Metrics::getDefaultMetrics(), Metrics::getDefaultProcessedMetrics());
    
            $columnsToKeep   = $this->getColumnsToKeep();
            $columnsToRemove = $this->getColumnsToRemove();
    
    
            foreach ($availableReports as &$availableReport) {
                // Ensure all metrics have a translation
                $metrics = $availableReport['metrics'];
                $cleanedMetrics = array();
    
                // TODO we can remove this once we remove the getReportMetadata event, leaving it here for backwards compatibility
    
                foreach ($metrics as $metricId => $metricTranslation) {
                    // When simply the column name was given, ie 'metric' => array( 'nb_visits' )
                    // $metricTranslation is in this case nb_visits. We look for a known translation.
                    if (is_numeric($metricId)
                        && isset($knownMetrics[$metricTranslation])
                    ) {
                        $metricId = $metricTranslation;
                        $metricTranslation = $knownMetrics[$metricTranslation];
                    }
                    $cleanedMetrics[$metricId] = $metricTranslation;
                }
                $availableReport['metrics'] = $cleanedMetrics;
    
                // if hide/show columns specified, hide/show metrics & docs
    
                $availableReport['metrics'] = $this->hideShowMetricsWithParams($availableReport['metrics'], $columnsToRemove, $columnsToKeep);
    
                if (isset($availableReport['processedMetrics'])) {
    
                    $availableReport['processedMetrics'] = $this->hideShowMetricsWithParams($availableReport['processedMetrics'], $columnsToRemove, $columnsToKeep);
    
                }
                if (isset($availableReport['metricsDocumentation'])) {
                    $availableReport['metricsDocumentation'] =
    
                        $this->hideShowMetricsWithParams($availableReport['metricsDocumentation'], $columnsToRemove, $columnsToKeep);
    
                }
    
                // Remove array elements that are false (to clean up API output)
                foreach ($availableReport as $attributeName => $attributeValue) {
                    if (empty($attributeValue)) {
                        unset($availableReport[$attributeName]);
                    }
                }
                // when there are per goal metrics, don't display conversion_rate since it can differ from per goal sum
    
                // TODO we should remove this once we remove the getReportMetadata event, leaving it here for backwards compatibility
    
                if (isset($availableReport['metricsGoal'])) {
                    unset($availableReport['processedMetrics']['conversion_rate']);
                    unset($availableReport['metricsGoal']['conversion_rate']);
                }
    
                // Processing a uniqueId for each report,
                // can be used by UIs as a key to match a given report
                $uniqueId = $availableReport['module'] . '_' . $availableReport['action'];
                if (!empty($availableReport['parameters'])) {
                    foreach ($availableReport['parameters'] as $key => $value) {
                        $uniqueId .= '_' . $key . '--' . $value;
                    }
                }
                $availableReport['uniqueId'] = $uniqueId;
    
                // Order is used to order reports internally, but not meant to be used outside
                unset($availableReport['order']);
            }
    
            // remove subtable reports
            if (!$showSubtableReports) {
                foreach ($availableReports as $idx => $report) {
                    if (isset($report['isSubtableReport']) && $report['isSubtableReport']) {
                        unset($availableReports[$idx]);
                    }
                }
            }
    
    
            $actualReports = array_values($availableReports);
            $cache->set($actualReports);
    
            return $actualReports; // make sure array has contiguous key values
    
        /**
         * API metadata are sorted by category/name,
         * with a little tweak to replicate the standard Piwik category ordering
         *
         * @param array $a
         * @param array $b
         * @return int
         */
        private static function sortReports($a, $b)
        {
            static $order = null;
            if (is_null($order)) {
                $order = array();
                foreach (Report::$orderOfReports as $category) {
                    $order[] = Piwik::translate($category);
                }
            }
            return ($category = strcmp(array_search($a['category'], $order), array_search($b['category'], $order))) == 0
                ? (@$a['order'] < @$b['order'] ? -1 : 1)
                : $category;
        }
    
    
        /**
         * Add the metadata for the API.get report
         * In other plugins, this would hook on 'API.getReportMetadata'
         */
        private function addApiGetMetdata(&$availableReports)
        {
            $metadata = array(
    
                'category'             => Piwik::translate('General_API'),
                'name'                 => Piwik::translate('General_MainMetrics'),
    
                'module'               => 'API',
                'action'               => 'get',
                'metrics'              => array(),
                'processedMetrics'     => array(),
                'metricsDocumentation' => array(),
                'order'                => 1
            );
    
            $indexesToMerge = array('metrics', 'processedMetrics', 'metricsDocumentation');
    
            foreach ($availableReports as $report) {
                if ($report['action'] == 'get') {
                    foreach ($indexesToMerge as $index) {
                        if (isset($report[$index])
                            && is_array($report[$index])
                        ) {
                            $metadata[$index] = array_merge($metadata[$index], $report[$index]);
                        }
                    }
                }
            }
    
            $availableReports[] = $metadata;
        }
    
        public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false,
                                           $apiParameters = false, $idGoal = false, $language = false,
                                           $showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false)
        {
    
            if (empty($apiParameters)) {
                $apiParameters = array();
            }
            if (!empty($idGoal)
                && empty($apiParameters['idGoal'])
            ) {
                $apiParameters['idGoal'] = $idGoal;
            }
            // Is this report found in the Metadata available reports?
            $reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language,
                $period, $date, $hideMetricsDoc, $showSubtableReports = true);
            if (empty($reportMetadata)) {
                throw new Exception("Requested report $apiModule.$apiAction for Website id=$idSite not found in the list of available reports. \n");
            }
    
            $reportMetadata = reset($reportMetadata);
    
            // Generate Api call URL passing custom parameters
            $parameters = array_merge($apiParameters, array(
                                                           'method'     => $apiModule . '.' . $apiAction,
                                                           'idSite'     => $idSite,
                                                           'period'     => $period,
                                                           'date'       => $date,
                                                           'format'     => 'original',
                                                           'serialize'  => '0',
                                                           'language'   => $language,
    
    
            if (isset($reportMetadata['processedMetrics'])) {
    
                $deleteRowsWithNoVisit = '1';
                if (!empty($reportMetadata['constantRowsCount'])) {
                    $deleteRowsWithNoVisit = '0';
                }
                $parameters['filter_add_columns_when_show_all_columns'] = $deleteRowsWithNoVisit;
    
            if (!empty($segment)) $parameters['segment'] = $segment;
    
    
            $url = Url::getQueryStringFromParameters($parameters);
            $request = new Request($url);
    
                $dataTable = $request->process();
            } catch (Exception $e) {
                throw new Exception("API returned an error: " . $e->getMessage() . " at " . basename($e->getFile()) . ":" . $e->getLine() . "\n");
            }
    
    
            list($newReport, $columns, $rowsMetadata, $totals) = $this->handleTableReport($idSite, $dataTable, $reportMetadata, $showRawMetrics);
    
            foreach ($columns as &$name) {
    
            $website = new Site($idSite);
    
            $period = Period\Factory::build($period, $date);
    
            $period = $period->getLocalizedLongString();
    
            $return = array(
                'website'        => $website->getName(),
                'prettyDate'     => $period,
                'metadata'       => $reportMetadata,
                'columns'        => $columns,
                'reportData'     => $newReport,
                'reportMetadata' => $rowsMetadata,
    
                'reportTotal'    => $totals
    
            );
            if ($showTimer) {
                $return['timerMillis'] = $timer->getTimeMs(0);
            }
            return $return;
        }
    
        /**
         * Enhance a $dataTable using metadata :
         *
         * - remove metrics based on $reportMetadata['metrics']
         * - add 0 valued metrics if $dataTable doesn't provide all $reportMetadata['metrics']
         * - format metric values to a 'human readable' format
    
         * - extract row metadata to a separate Simple|Set : $rowsMetadata
    
         * - translate metric names to a separate array : $columns
         *
         * @param int $idSite enables monetary value formatting based on site currency
    
    sgiehl's avatar
    sgiehl a validé
         * @param \Piwik\DataTable\Map|\Piwik\DataTable\Simple $dataTable
    
    sgiehl's avatar
    sgiehl a validé
         * @param bool $showRawMetrics
    
         * @return array Simple|Set $newReport with human readable format & array $columns list of translated column names & Simple|Set $rowsMetadata
    
    sgiehl's avatar
    sgiehl a validé
         */
    
        private function handleTableReport($idSite, $dataTable, &$reportMetadata, $showRawMetrics = false)
        {
            $hasDimension = isset($reportMetadata['dimension']);
            $columns = $reportMetadata['metrics'];
    
            if ($hasDimension) {
                $columns = array_merge(
                    array('label' => $reportMetadata['dimension']),
                    $columns
                );
    
    
                if (isset($reportMetadata['processedMetrics']) && is_array($reportMetadata['processedMetrics'])) {
    
                    $processedMetricsAdded = Metrics::getDefaultProcessedMetrics();
    
                    foreach ($reportMetadata['processedMetrics'] as $processedMetricId => $processedMetricTranslation) {
    
                        // this processed metric can be displayed for this report
    
                        if ($processedMetricTranslation && $processedMetricId !== $processedMetricTranslation) {
    
                            $columns[$processedMetricId] = $processedMetricTranslation;
    
                        } elseif (isset($processedMetricsAdded[$processedMetricId])) {
    
                            // for instance in case 'nb_visits' => 'nb_visits' we will translate it
    
                            $columns[$processedMetricId] = $processedMetricsAdded[$processedMetricId];
    
                        }
                    }
                }
    
                // Display the global Goal metrics
                if (isset($reportMetadata['metricsGoal'])) {
                    $metricsGoalDisplay = array('revenue');
                    // Add processed metrics to be displayed for this report
                    foreach ($metricsGoalDisplay as $goalMetricId) {
                        if (isset($reportMetadata['metricsGoal'][$goalMetricId])) {
                            $columns[$goalMetricId] = $reportMetadata['metricsGoal'][$goalMetricId];
                        }
                    }
                }
            }
    
            $columns = $this->hideShowMetrics($columns);
    
            // $dataTable is an instance of Set when multiple periods requested
            if ($dataTable instanceof DataTable\Map) {
                // Need a new Set to store the 'human readable' values
                $newReport = new DataTable\Map();
    
                // Need a new Set to store report metadata
                $rowsMetadata = new DataTable\Map();
    
                foreach ($dataTable->getDataTables() as $simpleDataTable) {
    
                    $this->removeEmptyColumns($columns, $reportMetadata, $simpleDataTable);
    
                    list($enhancedSimpleDataTable, $rowMetadata) = $this->handleSimpleDataTable($idSite, $simpleDataTable, $columns, $hasDimension, $showRawMetrics);
    
                    $enhancedSimpleDataTable->setAllTableMetadata($simpleDataTable->getAllTableMetadata());
    
                    $period = $simpleDataTable->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getLocalizedLongString();
    
                    $newReport->addTable($enhancedSimpleDataTable, $period);
                    $rowsMetadata->addTable($rowMetadata, $period);
    
                    $totals = $this->aggregateReportTotalValues($simpleDataTable, $totals);
    
                }
            } else {
                $this->removeEmptyColumns($columns, $reportMetadata, $dataTable);
                list($newReport, $rowsMetadata) = $this->handleSimpleDataTable($idSite, $dataTable, $columns, $hasDimension, $showRawMetrics);
    
    
                $totals = $this->aggregateReportTotalValues($dataTable, $totals);
    
            );
        }
    
        /**
         * Removes metrics from the list of columns and the report meta data if they are marked empty
         * in the data table meta data.
         */
        private function removeEmptyColumns(&$columns, &$reportMetadata, $dataTable)
        {
    
            $emptyColumns = $dataTable->getMetadata(DataTable::EMPTY_COLUMNS_METADATA_NAME);
    
            $columnsToRemove = $this->getColumnsToRemove();
            $columnsToKeep   = $this->getColumnsToKeep();
    
            $columns = $this->hideShowMetricsWithParams($columns, $columnsToRemove, $columnsToKeep, $emptyColumns);
    
                $reportMetadata['metrics'] = $this->hideShowMetricsWithParams($reportMetadata['metrics'], $columnsToRemove, $columnsToKeep, $emptyColumns);
    
            }
    
            if (isset($reportMetadata['metricsDocumentation'])) {
    
                $reportMetadata['metricsDocumentation'] = $this->hideShowMetricsWithParams($reportMetadata['metricsDocumentation'], $columnsToRemove, $columnsToKeep, $emptyColumns);
    
            }
        }
    
        /**
         * Removes column names from an array based on the values in the hideColumns,
         * showColumns query parameters. This is a hack that provides the ColumnDelete
         * filter functionality in processed reports.
         *
         * @param array $columns List of metrics shown in a processed report.
         * @param array $emptyColumns Empty columns from the data table meta data.
         * @return array Filtered list of metrics.
         */
        private function hideShowMetrics($columns, $emptyColumns = array())
        {
            if (!is_array($columns)) {
                return $columns;
            }
    
            // remove columns if hideColumns query parameters exist
    
            $columnsToRemove = $this->getColumnsToRemove();
    
            // remove columns if showColumns query parameters exist
            $columnsToKeep = $this->getColumnsToKeep();
    
            return $this->hideShowMetricsWithParams($columns, $columnsToRemove, $columnsToKeep, $emptyColumns);
        }
    
        private function hideShowMetricsWithParams($columns, $columnsToRemove, $columnsToKeep, $emptyColumns = array())
        {
            if (!is_array($columns)) {
                return $columns;
            }
    
    
                foreach ($columnsToRemove as $name) {
                    // if a column to remove is in the column list, remove it
                    if (isset($columns[$name])) {
                        unset($columns[$name]);
                    }
                }
            }
    
    
                foreach ($columns as $name => $ignore) {
                    // if the current column should not be kept, remove it
                    $idx = array_search($name, $columnsToKeep);
                    if ($idx === false) // if $name is not in $columnsToKeep
                    {
                        unset($columns[$name]);
                    }
                }
            }
    
            // remove empty columns
            if (is_array($emptyColumns)) {
                foreach ($emptyColumns as $column) {
                    if (isset($columns[$column])) {
                        unset($columns[$column]);
                    }
                }
            }
    
            return $columns;
        }
    
        /**
         * Enhance $simpleDataTable using metadata :
         *
         * - remove metrics based on $reportMetadata['metrics']
         * - add 0 valued metrics if $simpleDataTable doesn't provide all $reportMetadata['metrics']
         * - format metric values to a 'human readable' format
    
         * - extract row metadata to a separate Simple $rowsMetadata
    
         *
         * @param int $idSite enables monetary value formatting based on site currency
    
         * @param array $metadataColumns
         * @param boolean $hasDimension
         * @param bool $returnRawMetrics If set to true, the original metrics will be returned
         *
    
         * @return array DataTable $enhancedDataTable filtered metrics with human readable format & Simple $rowsMetadata
    
         */
        private function handleSimpleDataTable($idSite, $simpleDataTable, $metadataColumns, $hasDimension, $returnRawMetrics = false)
        {
            // new DataTable to store metadata
    
    
            // new DataTable to store 'human readable' values
            if ($hasDimension) {
    
                $enhancedDataTable = new DataTable();
    
            }
    
            foreach ($simpleDataTable->getRows() as $row) {
                $rowMetrics = $row->getColumns();
    
                foreach ($metadataColumns as $id => $name) {
                    if (!isset($rowMetrics[$id])) {
    
                        $row->setColumn($id, 0);
                        $rowMetrics[$id] = 0;
    
                $enhancedDataTable->addRow($enhancedRow);
    
                foreach ($rowMetrics as $columnName => $columnValue) {
                    // filter metrics according to metadata definition
                    if (isset($metadataColumns[$columnName])) {
                        // generate 'human readable' metric values
    
    
                        // if we handle MultiSites.getAll we do not always have the same idSite but different ones for
    
                        // each site, see https://github.com/piwik/piwik/issues/5006
    
                        $idSiteForRow = $idSite;
                        if ($row->getMetadata('idsite') && is_numeric($row->getMetadata('idsite'))) {
                            $idSiteForRow = (int) $row->getMetadata('idsite');
                        }
    
                        $prettyValue = MetricsFormatter::getPrettyValue($idSiteForRow, $columnName, $columnValue, $htmlAllowed = false);
    
                        $enhancedRow->addColumn($columnName, $prettyValue);
                    } // For example the Maps Widget requires the raw metrics to do advanced datavis
                    elseif ($returnRawMetrics) {
    
                        if (!isset($columnValue)) {
                            $columnValue = 0;
                        }
    
                        $enhancedRow->addColumn($columnName, $columnValue);
                    }
                }
    
                // If report has a dimension, extract metadata into a distinct DataTable
                if ($hasDimension) {
                    $rowMetadata = $row->getMetadata();
                    $idSubDataTable = $row->getIdSubDataTable();
    
                    // Create a row metadata only if there are metadata to insert
                    if (count($rowMetadata) > 0 || !is_null($idSubDataTable)) {
    
                        $rowsMetadata->addRow($metadataRow);
    
                        foreach ($rowMetadata as $metadataKey => $metadataValue) {
                            $metadataRow->addColumn($metadataKey, $metadataValue);
                        }
    
                        if (!is_null($idSubDataTable)) {
                            $metadataRow->addColumn('idsubdatatable', $idSubDataTable);
                        }
                    }
                }
            }
    
            return array(
                $enhancedDataTable,
                $rowsMetadata
            );
        }
    
        private function aggregateReportTotalValues($simpleDataTable, $totals)
    
        {
            $metadataTotals = $simpleDataTable->getMetadata('totals');
    
            if (empty($metadataTotals)) {
    
                return $totals;
            }
    
            $simpleTotals = $this->hideShowMetrics($metadataTotals);
    
            foreach ($simpleTotals as $metric => $value) {
                if (!array_key_exists($metric, $totals)) {
                    $totals[$metric] = $value;
                } else {
                    $totals[$metric] += $value;
                }
            }
    
            return $totals;
        }
    
            $columnsToRemove = Common::getRequestVar('hideColumns', '');
    
            if ($columnsToRemove != '') {
                return explode(',', $columnsToRemove);
            }
    
            return null;
    
            $columnsToKeep = Common::getRequestVar('showColumns', '');
    
            if ($columnsToKeep != '') {
                $columnsToKeep = explode(',', $columnsToKeep);
                $columnsToKeep[] = 'label';
    
                return $columnsToKeep;
            }
    
            return null;
        }
    
        private function buildReportMetadataCacheKey($idSites, $period, $date, $hideMetricsDoc, $showSubtableReports)
        {
            if (isset($_GET) && isset($_POST) && is_array($_GET) && is_array($_POST)) {
                $request = $_GET + $_POST;
            } elseif (isset($_GET) && is_array($_GET)) {
                $request = $_GET;
            } elseif (isset($_POST) && is_array($_POST)) {
                $request = $_POST;
            } else {
                $request = array();
            }
    
            $key = '';
            foreach ($request as $k => $v) {
    
                if (is_array($v)) {
    
                    $key .= $k . $this->getImplodedArray($v) . ',';
    
                } else {
                    $key .= $k . $v . ',';
                }
    
            }
    
            $key .= implode(',', $idSites) . ($period === false ? 0 : $period) . ($date === false ? 0 : $date);
            $key .= (int)$hideMetricsDoc . (int)$showSubtableReports . Piwik::getCurrentUserLogin();
            return 'reportMetadata' . md5($key);
    
    
        /**
         * @param $v
         * @return string
         */
        private function getImplodedArray($v)
        {
            return implode(', ', array_map(function ($entry) {
                if (is_array($entry)) {
                    return implode(":", $entry);
                }
                return $entry;
            }, $v));
        }