Download plugins/Actions/RecordBuilders/ActionReports.php from Leon4gr45/matomo: direct link, hf CLI and curl.
- Browser
- Download file 39.4 kB
-
https://huggingface.co/spaces/Leon4gr45/matomo/resolve/main/plugins/Actions/RecordBuilders/ActionReports.php
- Command line
-
hf download hf://spaces/Leon4gr45/matomo/plugins/Actions/RecordBuilders/ActionReports.php
-
curl -L -o ActionReports.php https://huggingface.co/spaces/Leon4gr45/matomo/resolve/main/plugins/Actions/RecordBuilders/ActionReports.php
39.4 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\Actions\RecordBuilders; | |
| use Piwik\API\Request; | |
| use Piwik\ArchiveProcessor; | |
| use Piwik\ArchiveProcessor\Record; | |
| use Piwik\Cache; | |
| use Piwik\Config\GeneralConfig; | |
| use Piwik\DataAccess\LogAggregator; | |
| use Piwik\DataTable; | |
| use Piwik\DataTable\Row; | |
| use Piwik\Metrics as PiwikMetrics; | |
| use Piwik\Plugins\Actions\Archiver; | |
| use Piwik\Plugins\Actions\ArchivingHelper; | |
| use Piwik\Plugins\Actions\Metrics; | |
| use Piwik\RankingQuery; | |
| use Piwik\Tracker\Action; | |
| use Piwik\Tracker\GoalManager; | |
| class ActionReports extends ArchiveProcessor\RecordBuilder | |
| { | |
| public function __construct() | |
| { | |
| ArchivingHelper::reloadConfig(); | |
| parent::__construct( | |
| ArchivingHelper::$maximumRowsInDataTableLevelZero, | |
| ArchivingHelper::$maximumRowsInSubDataTable, | |
| ArchivingHelper::$columnToSortByBeforeTruncation, | |
| null, | |
| Metrics::$columnsToRenameAfterAggregation | |
| ); | |
| } | |
| public function getRecordMetadata(ArchiveProcessor $archiveProcessor): array | |
| { | |
| $pageUrlsRecord = Record::make(Record::TYPE_BLOB, Archiver::PAGE_URLS_RECORD_NAME) | |
| ->setBlobColumnAggregationOps(Metrics::getColumnsAggregationOperation()); | |
| $pageTitlesRecord = Record::make(Record::TYPE_BLOB, Archiver::PAGE_TITLES_RECORD_NAME) | |
| ->setBlobColumnAggregationOps(Metrics::getColumnsAggregationOperation()); | |
| if ($this->isFlatArchivingEnabled()) { | |
| $this->setHierarchyBuiltFromFlatRecord( | |
| $pageUrlsRecord, | |
| Archiver::PAGE_URLS_FLAT_RECORD_NAME, | |
| [$this, 'flatRowToUrlHierarchyPath'], | |
| [$this, 'reduceLegacyUrlHierarchyIntoFlatTable'] | |
| ); | |
| $this->setHierarchyBuiltFromFlatRecord( | |
| $pageTitlesRecord, | |
| Archiver::PAGE_TITLES_FLAT_RECORD_NAME, | |
| [$this, 'flatRowToTitleHierarchyPath'], | |
| [$this, 'reduceLegacyTitleHierarchyIntoFlatTable'] | |
| ); | |
| } | |
| $records = [ | |
| Record::make(Record::TYPE_BLOB, Archiver::SITE_SEARCH_RECORD_NAME) | |
| ->setMaxRowsInTable(ArchivingHelper::$maximumRowsInDataTableSiteSearch), | |
| $pageUrlsRecord, | |
| $pageTitlesRecord, | |
| Record::make(Record::TYPE_BLOB, Archiver::DOWNLOADS_RECORD_NAME), | |
| Record::make(Record::TYPE_BLOB, Archiver::OUTLINKS_RECORD_NAME), | |
| Record::make(Record::TYPE_NUMERIC, Archiver::METRIC_SEARCHES_RECORD_NAME), | |
| Record::make(Record::TYPE_NUMERIC, Archiver::METRIC_KEYWORDS_RECORD_NAME) | |
| ->setIsCountOfBlobRecordRows(Archiver::SITE_SEARCH_RECORD_NAME), | |
| Record::make(Record::TYPE_NUMERIC, Archiver::METRIC_OUTLINKS_RECORD_NAME), | |
| Record::make(Record::TYPE_NUMERIC, Archiver::METRIC_UNIQ_OUTLINKS_RECORD_NAME), | |
| Record::make(Record::TYPE_NUMERIC, Archiver::METRIC_PAGEVIEWS_RECORD_NAME), | |
| Record::make(Record::TYPE_NUMERIC, Archiver::METRIC_UNIQ_PAGEVIEWS_RECORD_NAME), | |
| Record::make(Record::TYPE_NUMERIC, Archiver::METRIC_SUM_TIME_RECORD_NAME), | |
| Record::make(Record::TYPE_NUMERIC, Archiver::METRIC_HITS_TIMED_RECORD_NAME), | |
| Record::make(Record::TYPE_NUMERIC, Archiver::METRIC_DOWNLOADS_RECORD_NAME), | |
| Record::make(Record::TYPE_NUMERIC, Archiver::METRIC_UNIQ_DOWNLOADS_RECORD_NAME), | |
| ]; | |
| if ($this->isFlatArchivingEnabled()) { | |
| $records[] = Record::make(Record::TYPE_BLOB, Archiver::PAGE_URLS_FLAT_RECORD_NAME) | |
| ->setBlobColumnAggregationOps(Metrics::getColumnsAggregationOperation()) | |
| ->setMaxRowsInTable(ArchivingHelper::$maximumRowsInDataTableFlat); | |
| $records[] = Record::make(Record::TYPE_BLOB, Archiver::PAGE_TITLES_FLAT_RECORD_NAME) | |
| ->setBlobColumnAggregationOps(Metrics::getColumnsAggregationOperation()) | |
| ->setMaxRowsInTable(ArchivingHelper::$maximumRowsInDataTableFlat); | |
| } | |
| return $records; | |
| } | |
| public function flatRowToUrlHierarchyPath(Row $flatRow): ?array | |
| { | |
| return $this->flatRowToHierarchyPath($flatRow, Action::TYPE_PAGE_URL); | |
| } | |
| public function flatRowToTitleHierarchyPath(Row $flatRow): ?array | |
| { | |
| return $this->flatRowToHierarchyPath($flatRow, Action::TYPE_PAGE_TITLE); | |
| } | |
| public function reduceLegacyUrlHierarchyIntoFlatTable(DataTable $legacyHierarchy, DataTable $flatTable, ArchiveProcessor $archiveProcessor, Record $record): void | |
| { | |
| ArchivingHelper::mergeHierarchicalActionsTableIntoBestEffortFlatTable( | |
| $legacyHierarchy, | |
| $flatTable, | |
| Action::TYPE_PAGE_URL | |
| ); | |
| } | |
| public function reduceLegacyTitleHierarchyIntoFlatTable(DataTable $legacyHierarchy, DataTable $flatTable, ArchiveProcessor $archiveProcessor, Record $record): void | |
| { | |
| ArchivingHelper::mergeHierarchicalActionsTableIntoBestEffortFlatTable( | |
| $legacyHierarchy, | |
| $flatTable, | |
| Action::TYPE_PAGE_TITLE | |
| ); | |
| } | |
| protected function aggregate(ArchiveProcessor $archiveProcessor): array | |
| { | |
| if (!$this->isFlatArchivingEnabled()) { | |
| return $this->aggregateLegacyHierarchical($archiveProcessor); | |
| } | |
| $rankingQueryLimit = ArchivingHelper::getRankingQueryLimit(); | |
| ArchivingHelper::reloadConfig(); | |
| $tablesByType = $this->makeReportTables(); | |
| $flatPageTablesByType = $this->makeFlatPageTables(); | |
| $tableModesByType = [ | |
| Action::TYPE_PAGE_URL => ArchivingHelper::ACTION_TABLE_MODE_FLAT, | |
| Action::TYPE_PAGE_TITLE => ArchivingHelper::ACTION_TABLE_MODE_FLAT, | |
| ]; | |
| $dayTablesByType = $tablesByType; | |
| $dayTablesByType[Action::TYPE_PAGE_URL] = $flatPageTablesByType[Action::TYPE_PAGE_URL]; | |
| $dayTablesByType[Action::TYPE_PAGE_TITLE] = $flatPageTablesByType[Action::TYPE_PAGE_TITLE]; | |
| $this->archiveDayActions( | |
| $archiveProcessor, | |
| $rankingQueryLimit, | |
| $dayTablesByType, | |
| array_diff(array_keys($dayTablesByType), [Action::TYPE_SITE_SEARCH]), | |
| true, | |
| $tableModesByType | |
| ); | |
| if ($archiveProcessor->getParams()->getSite()->isSiteSearchEnabled()) { | |
| $rankingQueryLimitSiteSearch = max($rankingQueryLimit, ArchivingHelper::$maximumRowsInDataTableSiteSearch); | |
| $this->archiveDayActions( | |
| $archiveProcessor, | |
| $rankingQueryLimitSiteSearch, | |
| $dayTablesByType, | |
| [Action::TYPE_SITE_SEARCH], | |
| false, | |
| $tableModesByType | |
| ); | |
| } | |
| $this->archiveDayEntryActions($archiveProcessor->getLogAggregator(), $dayTablesByType, $rankingQueryLimit, $tableModesByType); | |
| $this->archiveDayExitActions($archiveProcessor->getLogAggregator(), $dayTablesByType, $rankingQueryLimit, $tableModesByType); | |
| $this->archiveDayActionsTime($archiveProcessor->getLogAggregator(), $dayTablesByType, $rankingQueryLimit, $tableModesByType); | |
| $this->archiveDayActionsGoals($archiveProcessor, $rankingQueryLimit); | |
| ArchivingHelper::normalizeFlatGoalsMetricsForHierarchy($flatPageTablesByType); | |
| ArchivingHelper::clearActionsCache(); | |
| $tablesByType[Action::TYPE_PAGE_URL] = $this->buildDayHierarchicalTableFromFlatTable( | |
| $flatPageTablesByType[Action::TYPE_PAGE_URL], | |
| [$this, 'flatRowToUrlHierarchyPath'] | |
| ); | |
| $tablesByType[Action::TYPE_PAGE_TITLE] = $this->buildDayHierarchicalTableFromFlatTable( | |
| $flatPageTablesByType[Action::TYPE_PAGE_TITLE], | |
| [$this, 'flatRowToTitleHierarchyPath'] | |
| ); | |
| $this->finalizeBuiltFromFlatHierarchyTable( | |
| $archiveProcessor, | |
| $tablesByType[Action::TYPE_PAGE_URL], | |
| Archiver::PAGE_URLS_RECORD_NAME | |
| ); | |
| $this->finalizeBuiltFromFlatHierarchyTable( | |
| $archiveProcessor, | |
| $tablesByType[Action::TYPE_PAGE_TITLE], | |
| Archiver::PAGE_TITLES_RECORD_NAME | |
| ); | |
| $dataTable = $tablesByType[Action::TYPE_SITE_SEARCH]; | |
| $this->deleteUnusedColumnsFromKeywordsDataTable($dataTable); | |
| foreach ($tablesByType as $actionType => $dataTable) { | |
| if ( | |
| $actionType === Action::TYPE_PAGE_URL | |
| || $actionType === Action::TYPE_PAGE_TITLE | |
| ) { | |
| continue; | |
| } | |
| ArchivingHelper::deleteInvalidSummedColumnsFromDataTable($dataTable); | |
| } | |
| foreach ($flatPageTablesByType as $flatDataTable) { | |
| // also removes unique visitor columns from the summary row, as those cannot | |
| // be summed and would otherwise be aggregated into non-day periods | |
| ArchivingHelper::deleteInvalidSummedColumnsFromDataTable($flatDataTable); | |
| } | |
| $nbSearches = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)); | |
| $nbKeywords = $dataTable->getRowsCount(); | |
| $dataTable = $tablesByType[Action::TYPE_OUTLINK]; | |
| $nbOutlinks = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)); | |
| $nbUniqOutlinks = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_NB_VISITS)); | |
| $dataTable = $flatPageTablesByType[Action::TYPE_PAGE_URL]; | |
| $nbPageviews = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)); | |
| $nbUniqPageviews = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_NB_VISITS)); | |
| $nbSumTimeGeneration = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_SUM_TIME_GENERATION)); | |
| $nbHitsWithTimeGeneration = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION)); | |
| $dataTable = $tablesByType[Action::TYPE_DOWNLOAD]; | |
| $nbDownloads = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)); | |
| $nbUniqDownloads = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_NB_VISITS)); | |
| return [ | |
| // blob records | |
| Archiver::PAGE_URLS_FLAT_RECORD_NAME => $flatPageTablesByType[Action::TYPE_PAGE_URL], | |
| Archiver::PAGE_TITLES_FLAT_RECORD_NAME => $flatPageTablesByType[Action::TYPE_PAGE_TITLE], | |
| Archiver::PAGE_URLS_RECORD_NAME => $tablesByType[Action::TYPE_PAGE_URL], | |
| Archiver::PAGE_TITLES_RECORD_NAME => $tablesByType[Action::TYPE_PAGE_TITLE], | |
| Archiver::DOWNLOADS_RECORD_NAME => $tablesByType[Action::TYPE_DOWNLOAD], | |
| Archiver::OUTLINKS_RECORD_NAME => $tablesByType[Action::TYPE_OUTLINK], | |
| Archiver::SITE_SEARCH_RECORD_NAME => $tablesByType[Action::TYPE_SITE_SEARCH], | |
| // numeric records | |
| Archiver::METRIC_SEARCHES_RECORD_NAME => $nbSearches, | |
| Archiver::METRIC_KEYWORDS_RECORD_NAME => $nbKeywords, | |
| Archiver::METRIC_OUTLINKS_RECORD_NAME => $nbOutlinks, | |
| Archiver::METRIC_UNIQ_OUTLINKS_RECORD_NAME => $nbUniqOutlinks, | |
| Archiver::METRIC_PAGEVIEWS_RECORD_NAME => $nbPageviews, | |
| Archiver::METRIC_UNIQ_PAGEVIEWS_RECORD_NAME => $nbUniqPageviews, | |
| Archiver::METRIC_SUM_TIME_RECORD_NAME => $nbSumTimeGeneration, | |
| Archiver::METRIC_HITS_TIMED_RECORD_NAME => $nbHitsWithTimeGeneration, | |
| Archiver::METRIC_DOWNLOADS_RECORD_NAME => $nbDownloads, | |
| Archiver::METRIC_UNIQ_DOWNLOADS_RECORD_NAME => $nbUniqDownloads, | |
| ]; | |
| } | |
| protected function beforeInsertBuiltFromFlatHierarchyRecord( | |
| ArchiveProcessor $archiveProcessor, | |
| Record $hierarchicalRecord, | |
| DataTable $hierarchicalTable, | |
| DataTable $flatTable | |
| ): void { | |
| $recordName = $hierarchicalRecord->getName(); | |
| if ( | |
| $recordName !== Archiver::PAGE_URLS_RECORD_NAME | |
| && $recordName !== Archiver::PAGE_TITLES_RECORD_NAME | |
| ) { | |
| return; | |
| } | |
| $this->finalizeBuiltFromFlatHierarchyTable($archiveProcessor, $hierarchicalTable, $recordName); | |
| } | |
| private function flatRowToHierarchyPath(Row $flatRow, int $actionType): ?array | |
| { | |
| $label = $flatRow->getColumn('label'); | |
| if (!is_string($label) || $label === '') { | |
| return null; | |
| } | |
| if ($actionType === Action::TYPE_PAGE_URL) { | |
| if ($label === ArchivingHelper::getUnknownActionName(Action::TYPE_PAGE_URL)) { | |
| return [$label]; | |
| } | |
| return ArchivingHelper::getActionExplodedNames( | |
| ArchivingHelper::removePageUrlLeafMarkerFromFlatLabel($label), | |
| $actionType | |
| ); | |
| } | |
| return ArchivingHelper::getActionExplodedNames($label, $actionType); | |
| } | |
| private function removeFlatPathMetadataFromDataTable(DataTable $dataTable): void | |
| { | |
| $summaryRow = $dataTable->getRowFromId(DataTable::ID_SUMMARY_ROW); | |
| if ($summaryRow instanceof Row) { | |
| $summaryRow->deleteMetadata(ArchivingHelper::ACTION_FLAT_PATH_METADATA_NAME); | |
| } | |
| foreach ($dataTable->getRows() as $row) { | |
| $row->deleteMetadata(ArchivingHelper::ACTION_FLAT_PATH_METADATA_NAME); | |
| $subtable = $row->getSubtable(); | |
| if ($subtable instanceof DataTable) { | |
| $this->removeFlatPathMetadataFromDataTable($subtable); | |
| } | |
| } | |
| } | |
| private function finalizeBuiltFromFlatHierarchyTable( | |
| ArchiveProcessor $archiveProcessor, | |
| DataTable $hierarchicalTable, | |
| string $recordName | |
| ): void { | |
| $hierarchicalTable->setMetadata(DataTable::COLUMN_AGGREGATION_OPS_METADATA_NAME, Metrics::getColumnsAggregationOperation()); | |
| if ($recordName === Archiver::PAGE_URLS_RECORD_NAME) { | |
| $prefix = $archiveProcessor->getParams()->getSite()->getMainUrl(); | |
| $prefix = rtrim($prefix, '/') . '/'; | |
| ArchivingHelper::setFolderPathMetadata($hierarchicalTable, $isUrl = true, $prefix); | |
| } elseif ($recordName === Archiver::PAGE_TITLES_RECORD_NAME) { | |
| ArchivingHelper::setFolderPathMetadata($hierarchicalTable, $isUrl = false); | |
| } | |
| $this->removeFlatPathMetadataFromDataTable($hierarchicalTable); | |
| ArchivingHelper::deleteInvalidSummedColumnsFromDataTable($hierarchicalTable); | |
| } | |
| private function isFlatArchivingEnabled(): bool | |
| { | |
| ArchivingHelper::reloadConfig(); | |
| return ArchivingHelper::$maximumRowsInDataTableFlat > 0; | |
| } | |
| private function setHierarchyBuiltFromFlatRecord( | |
| Record $record, | |
| string $flatRecordName, | |
| callable $flatToHierarchyPathCallback, | |
| callable $legacyHierarchyToFlatReducer | |
| ): void { | |
| $record->setBuiltFromFlatRecord( | |
| $flatRecordName, | |
| $flatToHierarchyPathCallback, | |
| $legacyHierarchyToFlatReducer | |
| )->setMaxRowsInTable(0)->setMaxRowsInSubtable(0); | |
| } | |
| private function buildDayHierarchicalTableFromFlatTable( | |
| DataTable $flatTable, | |
| callable $flatToHierarchyPathCallback | |
| ): DataTable { | |
| return $this->buildHierarchicalTableFromFlatTable( | |
| $flatTable, | |
| Metrics::getColumnsAggregationOperation(), | |
| $flatToHierarchyPathCallback, | |
| $this->getDefaultHierarchyRowColumns() | |
| ); | |
| } | |
| private function getDefaultHierarchyRowColumns(): array | |
| { | |
| return [ | |
| PiwikMetrics::INDEX_NB_VISITS => 0, | |
| PiwikMetrics::INDEX_NB_UNIQ_VISITORS => 0, | |
| PiwikMetrics::INDEX_PAGE_NB_HITS => 0, | |
| PiwikMetrics::INDEX_PAGE_SUM_TIME_SPENT => 0, | |
| ]; | |
| } | |
| private function aggregateLegacyHierarchical(ArchiveProcessor $archiveProcessor): array | |
| { | |
| $rankingQueryLimit = ArchivingHelper::getRankingQueryLimit(); | |
| ArchivingHelper::reloadConfig(); | |
| $tablesByType = $this->makeReportTables(); | |
| $this->archiveDayActions( | |
| $archiveProcessor, | |
| $rankingQueryLimit, | |
| $tablesByType, | |
| array_diff(array_keys($tablesByType), [Action::TYPE_SITE_SEARCH]), | |
| true | |
| ); | |
| if ($archiveProcessor->getParams()->getSite()->isSiteSearchEnabled()) { | |
| $rankingQueryLimitSiteSearch = max($rankingQueryLimit, ArchivingHelper::$maximumRowsInDataTableSiteSearch); | |
| $this->archiveDayActions($archiveProcessor, $rankingQueryLimitSiteSearch, $tablesByType, [Action::TYPE_SITE_SEARCH], false); | |
| } | |
| $this->archiveDayEntryActions($archiveProcessor->getLogAggregator(), $tablesByType, $rankingQueryLimit); | |
| $this->archiveDayExitActions($archiveProcessor->getLogAggregator(), $tablesByType, $rankingQueryLimit); | |
| $this->archiveDayActionsTime($archiveProcessor->getLogAggregator(), $tablesByType, $rankingQueryLimit); | |
| $this->archiveDayActionsGoals($archiveProcessor, $rankingQueryLimit); | |
| ArchivingHelper::clearActionsCache(); | |
| $prefix = $archiveProcessor->getParams()->getSite()->getMainUrl(); | |
| $prefix = rtrim($prefix, '/') . '/'; | |
| ArchivingHelper::setFolderPathMetadata($tablesByType[Action::TYPE_PAGE_URL], $isUrl = true, $prefix); | |
| ArchivingHelper::setFolderPathMetadata($tablesByType[Action::TYPE_PAGE_TITLE], $isUrl = false); | |
| $dataTable = $tablesByType[Action::TYPE_SITE_SEARCH]; | |
| $this->deleteUnusedColumnsFromKeywordsDataTable($dataTable); | |
| foreach ($tablesByType as $dataTable) { | |
| ArchivingHelper::deleteInvalidSummedColumnsFromDataTable($dataTable); | |
| } | |
| $nbSearches = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)); | |
| $nbKeywords = $dataTable->getRowsCount(); | |
| $dataTable = $tablesByType[Action::TYPE_OUTLINK]; | |
| $nbOutlinks = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)); | |
| $nbUniqOutlinks = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_NB_VISITS)); | |
| $dataTable = $tablesByType[Action::TYPE_PAGE_URL]; | |
| $nbPageviews = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)); | |
| $nbUniqPageviews = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_NB_VISITS)); | |
| $nbSumTimeGeneration = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_SUM_TIME_GENERATION)); | |
| $nbHitsWithTimeGeneration = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION)); | |
| $dataTable = $tablesByType[Action::TYPE_DOWNLOAD]; | |
| $nbDownloads = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)); | |
| $nbUniqDownloads = array_sum($dataTable->getColumn(PiwikMetrics::INDEX_NB_VISITS)); | |
| return [ | |
| Archiver::PAGE_URLS_RECORD_NAME => $tablesByType[Action::TYPE_PAGE_URL], | |
| Archiver::PAGE_TITLES_RECORD_NAME => $tablesByType[Action::TYPE_PAGE_TITLE], | |
| Archiver::DOWNLOADS_RECORD_NAME => $tablesByType[Action::TYPE_DOWNLOAD], | |
| Archiver::OUTLINKS_RECORD_NAME => $tablesByType[Action::TYPE_OUTLINK], | |
| Archiver::SITE_SEARCH_RECORD_NAME => $tablesByType[Action::TYPE_SITE_SEARCH], | |
| Archiver::METRIC_SEARCHES_RECORD_NAME => $nbSearches, | |
| Archiver::METRIC_KEYWORDS_RECORD_NAME => $nbKeywords, | |
| Archiver::METRIC_OUTLINKS_RECORD_NAME => $nbOutlinks, | |
| Archiver::METRIC_UNIQ_OUTLINKS_RECORD_NAME => $nbUniqOutlinks, | |
| Archiver::METRIC_PAGEVIEWS_RECORD_NAME => $nbPageviews, | |
| Archiver::METRIC_UNIQ_PAGEVIEWS_RECORD_NAME => $nbUniqPageviews, | |
| Archiver::METRIC_SUM_TIME_RECORD_NAME => $nbSumTimeGeneration, | |
| Archiver::METRIC_HITS_TIMED_RECORD_NAME => $nbHitsWithTimeGeneration, | |
| Archiver::METRIC_DOWNLOADS_RECORD_NAME => $nbDownloads, | |
| Archiver::METRIC_UNIQ_DOWNLOADS_RECORD_NAME => $nbUniqDownloads, | |
| ]; | |
| } | |
| protected function deleteUnusedColumnsFromKeywordsDataTable(DataTable $dataTable): void | |
| { | |
| $columnsToDelete = array( | |
| PiwikMetrics::INDEX_NB_UNIQ_VISITORS, | |
| PiwikMetrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, | |
| PiwikMetrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS, | |
| PiwikMetrics::INDEX_PAGE_ENTRY_NB_ACTIONS, | |
| PiwikMetrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH, | |
| PiwikMetrics::INDEX_PAGE_ENTRY_NB_VISITS, | |
| PiwikMetrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT, | |
| PiwikMetrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS, | |
| ); | |
| $dataTable->deleteColumns($columnsToDelete); | |
| } | |
| private function makeReportTables(): array | |
| { | |
| $result = []; | |
| foreach (Metrics::$actionTypes as $type) { | |
| $dataTable = new DataTable(); | |
| if ($type === Action::TYPE_SITE_SEARCH) { | |
| $maxRows = ArchivingHelper::$maximumRowsInDataTableSiteSearch; | |
| } else { | |
| $maxRows = ArchivingHelper::$maximumRowsInDataTableLevelZero; | |
| } | |
| $dataTable->setMaximumAllowedRows($maxRows); | |
| if ( | |
| $type == Action::TYPE_PAGE_URL | |
| || $type == Action::TYPE_PAGE_TITLE | |
| ) { | |
| // for page urls and page titles, performance metrics exist and have to be aggregated correctly | |
| $dataTable->setMetadata(DataTable::COLUMN_AGGREGATION_OPS_METADATA_NAME, Metrics::getColumnsAggregationOperation()); | |
| } | |
| $result[$type] = $dataTable; | |
| } | |
| return $result; | |
| } | |
| private function makeFlatPageTables(): array | |
| { | |
| $tables = []; | |
| $maxRowsInFlatTable = ArchivingHelper::$maximumRowsInDataTableFlat; | |
| foreach ([Action::TYPE_PAGE_URL, Action::TYPE_PAGE_TITLE] as $type) { | |
| $table = new DataTable(); | |
| $table->setMaximumAllowedRows($maxRowsInFlatTable); | |
| $table->setMetadata(DataTable::COLUMN_AGGREGATION_OPS_METADATA_NAME, Metrics::getColumnsAggregationOperation()); | |
| $tables[$type] = $table; | |
| } | |
| return $tables; | |
| } | |
| protected function archiveDayActions( | |
| ArchiveProcessor $archiveProcessor, | |
| int $rankingQueryLimit, | |
| array $actionsTablesByType, | |
| $actionTypes, | |
| bool $includePageNotDefined, | |
| array $tableModesByType = [] | |
| ): void { | |
| $logAggregator = $archiveProcessor->getLogAggregator(); | |
| $metricsConfig = Metrics::getActionMetrics(); | |
| $select = "log_action.name, | |
| log_action.type, | |
| log_action.idaction, | |
| log_action.url_prefix"; | |
| $select = $this->addMetricsToSelect($select, $metricsConfig); | |
| $from = array( | |
| "log_link_visit_action", | |
| array( | |
| "table" => "log_action", | |
| "joinOn" => "log_link_visit_action.%s = log_action.idaction", | |
| ), | |
| ); | |
| $where = $logAggregator->getWhereStatement('log_link_visit_action', 'server_time'); | |
| $where .= " AND log_link_visit_action.%s IS NOT NULL" | |
| . $this->getWhereClauseActionIsNotEvent(); | |
| $actionTypesWhere = "log_action.type IN (" . implode(", ", $actionTypes) . ")"; | |
| if ($includePageNotDefined) { | |
| $actionTypesWhere = "(" . $actionTypesWhere . " OR log_action.type IS NULL)"; | |
| } | |
| $where .= " AND $actionTypesWhere"; | |
| $groupBy = "log_link_visit_action.%s"; | |
| $orderBy = "`" . PiwikMetrics::INDEX_PAGE_NB_HITS . "` DESC, name ASC"; | |
| $siteSearchEnabled = $archiveProcessor->getParams()->getSite()->isSiteSearchEnabled(); | |
| $rankingQuery = false; | |
| if ($rankingQueryLimit > 0) { | |
| $rankingQuery = new RankingQuery($rankingQueryLimit); | |
| $rankingQuery->addLabelColumn(array('idaction', 'name')); | |
| $rankingQuery->addColumn('url_prefix'); | |
| if ($siteSearchEnabled) { | |
| $rankingQuery->addColumn(PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min'); | |
| $rankingQuery->addColumn(PiwikMetrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, 'sum'); | |
| } | |
| $this->addMetricsToRankingQuery($rankingQuery, $metricsConfig); | |
| $rankingQuery->partitionResultIntoMultipleGroups('type', $actionTypes); | |
| } | |
| // Special Magic to get | |
| // 1) No result Keywords | |
| // 2) For each page view, count number of times the referrer page was a Site Search | |
| if ($siteSearchEnabled) { | |
| $this->updateQuerySelectFromForSiteSearch($select, $from); | |
| } | |
| $this->archiveDayQueryProcess( | |
| $logAggregator, | |
| $actionsTablesByType, | |
| $select, | |
| $from, | |
| $where, | |
| $groupBy, | |
| $orderBy, | |
| "idaction_name", | |
| $rankingQuery, | |
| $metricsConfig, | |
| $tableModesByType | |
| ); | |
| $this->archiveDayQueryProcess( | |
| $logAggregator, | |
| $actionsTablesByType, | |
| $select, | |
| $from, | |
| $where, | |
| $groupBy, | |
| $orderBy, | |
| "idaction_url", | |
| $rankingQuery, | |
| $metricsConfig, | |
| $tableModesByType | |
| ); | |
| } | |
| /** | |
| * Entry actions for Page URLs and Page names | |
| */ | |
| protected function archiveDayEntryActions(LogAggregator $logAggregator, array $actionsTablesByType, int $rankingQueryLimit, array $tableModesByType = []) | |
| { | |
| $rankingQuery = false; | |
| if ($rankingQueryLimit > 0) { | |
| $rankingQuery = new RankingQuery($rankingQueryLimit); | |
| $rankingQuery->addLabelColumn('idaction'); | |
| $rankingQuery->addColumn(PiwikMetrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS); | |
| $rankingQuery->addColumn(array(PiwikMetrics::INDEX_PAGE_ENTRY_NB_VISITS, | |
| PiwikMetrics::INDEX_PAGE_ENTRY_NB_ACTIONS, | |
| PiwikMetrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH, | |
| PiwikMetrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT), 'sum'); | |
| $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($actionsTablesByType)); | |
| $extraSelects = 'log_action.type, log_action.name,'; | |
| $from = array( | |
| "log_visit", | |
| array( | |
| "table" => "log_action", | |
| "joinOn" => "log_visit.%s = log_action.idaction", | |
| ), | |
| ); | |
| $orderBy = "`" . PiwikMetrics::INDEX_PAGE_ENTRY_NB_ACTIONS . "` DESC, log_action.name ASC"; | |
| } else { | |
| $extraSelects = false; | |
| $from = "log_visit"; | |
| $orderBy = false; | |
| } | |
| $select = "log_visit.%s as idaction, $extraSelects | |
| count(distinct log_visit.idvisitor) as `" . PiwikMetrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS . "`, | |
| count(*) as `" . PiwikMetrics::INDEX_PAGE_ENTRY_NB_VISITS . "`, | |
| sum(log_visit.visit_total_actions) as `" . PiwikMetrics::INDEX_PAGE_ENTRY_NB_ACTIONS . "`, | |
| sum(log_visit.visit_total_time) as `" . PiwikMetrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH . "`, | |
| sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `" . PiwikMetrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT . "`"; | |
| $where = $logAggregator->getWhereStatement('log_visit', 'visit_last_action_time'); | |
| $where .= " AND log_visit.%s > 0"; | |
| $groupBy = "log_visit.%s"; | |
| $this->archiveDayQueryProcess( | |
| $logAggregator, | |
| $actionsTablesByType, | |
| $select, | |
| $from, | |
| $where, | |
| $groupBy, | |
| $orderBy, | |
| "visit_entry_idaction_url", | |
| $rankingQuery, | |
| [], | |
| $tableModesByType | |
| ); | |
| $this->archiveDayQueryProcess( | |
| $logAggregator, | |
| $actionsTablesByType, | |
| $select, | |
| $from, | |
| $where, | |
| $groupBy, | |
| $orderBy, | |
| "visit_entry_idaction_name", | |
| $rankingQuery, | |
| [], | |
| $tableModesByType | |
| ); | |
| } | |
| /** | |
| * Exit actions | |
| */ | |
| protected function archiveDayExitActions(LogAggregator $logAggregator, array $actionsTablesByType, int $rankingQueryLimit, array $tableModesByType = []) | |
| { | |
| $rankingQuery = false; | |
| if ($rankingQueryLimit > 0) { | |
| $rankingQuery = new RankingQuery($rankingQueryLimit); | |
| $rankingQuery->addLabelColumn('idaction'); | |
| $rankingQuery->addColumn(PiwikMetrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS); | |
| $rankingQuery->addColumn(PiwikMetrics::INDEX_PAGE_EXIT_NB_VISITS, 'sum'); | |
| $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($actionsTablesByType)); | |
| $extraSelects = 'log_action.type, log_action.name,'; | |
| $from = array( | |
| "log_visit", | |
| array( | |
| "table" => "log_action", | |
| "joinOn" => "log_visit.%s = log_action.idaction", | |
| ), | |
| ); | |
| $orderBy = "`" . PiwikMetrics::INDEX_PAGE_EXIT_NB_VISITS . "` DESC, log_action.name ASC"; | |
| } else { | |
| $extraSelects = false; | |
| $from = "log_visit"; | |
| $orderBy = false; | |
| } | |
| $select = "log_visit.%s as idaction, $extraSelects | |
| count(distinct log_visit.idvisitor) as `" . PiwikMetrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS . "`, | |
| count(*) as `" . PiwikMetrics::INDEX_PAGE_EXIT_NB_VISITS . "`"; | |
| $where = $logAggregator->getWhereStatement('log_visit', 'visit_last_action_time'); | |
| $where .= " AND log_visit.%s > 0"; | |
| $groupBy = "log_visit.%s"; | |
| $this->archiveDayQueryProcess( | |
| $logAggregator, | |
| $actionsTablesByType, | |
| $select, | |
| $from, | |
| $where, | |
| $groupBy, | |
| $orderBy, | |
| "visit_exit_idaction_url", | |
| $rankingQuery, | |
| [], | |
| $tableModesByType | |
| ); | |
| $this->archiveDayQueryProcess( | |
| $logAggregator, | |
| $actionsTablesByType, | |
| $select, | |
| $from, | |
| $where, | |
| $groupBy, | |
| $orderBy, | |
| "visit_exit_idaction_name", | |
| $rankingQuery, | |
| [], | |
| $tableModesByType | |
| ); | |
| return array($rankingQuery, $extraSelects, $from, $orderBy, $select, $where, $groupBy); | |
| } | |
| /** | |
| * Time per action | |
| */ | |
| protected function archiveDayActionsTime(LogAggregator $logAggregator, array $actionsTablesByType, int $rankingQueryLimit, array $tableModesByType = []) | |
| { | |
| $rankingQuery = false; | |
| if ($rankingQueryLimit > 0) { | |
| $rankingQuery = new RankingQuery($rankingQueryLimit); | |
| $rankingQuery->addLabelColumn('idaction'); | |
| $rankingQuery->addColumn(PiwikMetrics::INDEX_PAGE_SUM_TIME_SPENT, 'sum'); | |
| $rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($actionsTablesByType)); | |
| $extraSelects = "log_action.type, log_action.name, count(*) as `" . PiwikMetrics::INDEX_PAGE_NB_HITS . "`,"; | |
| $from = array( | |
| "log_link_visit_action", | |
| array( | |
| "table" => "log_action", | |
| "joinOn" => "log_link_visit_action.%s = log_action.idaction", | |
| ), | |
| ); | |
| $orderBy = "`" . PiwikMetrics::INDEX_PAGE_NB_HITS . "` DESC, log_action.name ASC"; | |
| } else { | |
| $extraSelects = false; | |
| $from = "log_link_visit_action"; | |
| $orderBy = false; | |
| } | |
| $select = "log_link_visit_action.%s as idaction, $extraSelects | |
| sum(log_link_visit_action.time_spent_ref_action) as `" . PiwikMetrics::INDEX_PAGE_SUM_TIME_SPENT . "`"; | |
| $where = $logAggregator->getWhereStatement('log_link_visit_action', 'server_time'); | |
| $where .= " AND log_link_visit_action.time_spent_ref_action > 0 | |
| AND log_link_visit_action.%s > 0" | |
| . $this->getWhereClauseActionIsNotEvent(); | |
| $groupBy = "log_link_visit_action.%s"; | |
| $this->archiveDayQueryProcess( | |
| $logAggregator, | |
| $actionsTablesByType, | |
| $select, | |
| $from, | |
| $where, | |
| $groupBy, | |
| $orderBy, | |
| "idaction_url_ref", | |
| $rankingQuery, | |
| [], | |
| $tableModesByType | |
| ); | |
| $this->archiveDayQueryProcess( | |
| $logAggregator, | |
| $actionsTablesByType, | |
| $select, | |
| $from, | |
| $where, | |
| $groupBy, | |
| $orderBy, | |
| "idaction_name_ref", | |
| $rankingQuery, | |
| [], | |
| $tableModesByType | |
| ); | |
| } | |
| protected function archiveDayQueryProcess( | |
| LogAggregator $logAggregator, | |
| array $actionsTablesByType, | |
| string $select, | |
| $from, | |
| string $where, | |
| string $groupBy, | |
| $orderBy, | |
| string $sprintfField, | |
| ?RankingQuery $rankingQuery = null, | |
| array $metricsConfig = array(), | |
| array $tableModesByType = [] | |
| ): void { | |
| $select = sprintf($select, $sprintfField); | |
| // get query with segmentation | |
| $query = $logAggregator->generateQuery($select, $from, $where, $groupBy, $orderBy); | |
| // replace the rest of the %s | |
| $querySql = str_replace("%s", $sprintfField, $query['sql']); | |
| // apply ranking query | |
| if ($rankingQuery) { | |
| $querySql = $rankingQuery->generateRankingQuery($querySql); | |
| } | |
| // get result | |
| $resultSet = $logAggregator->getDb()->query($querySql, $query['bind']); | |
| ArchivingHelper::updateActionsTableWithRowQuery( | |
| $resultSet, | |
| $sprintfField, | |
| $actionsTablesByType, | |
| $metricsConfig, | |
| $tableModesByType | |
| ); | |
| } | |
| protected function updateQuerySelectFromForSiteSearch(string &$select, array &$from): void | |
| { | |
| $selectFlagNoResultKeywords = ", | |
| MAX(log_link_visit_action.search_count) = 0 AS `" . PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT . "`"; | |
| // we need an extra JOIN to know whether the referrer "idaction_name_ref" was a Site Search request | |
| $from[] = array( | |
| "table" => "log_action", | |
| "tableAlias" => "log_action_name_ref", | |
| "joinOn" => "log_link_visit_action.idaction_name_ref = log_action_name_ref.idaction", | |
| ); | |
| $selectPageIsFollowingSiteSearch = ", | |
| SUM( CASE WHEN log_action_name_ref.type = " . Action::TYPE_SITE_SEARCH . " | |
| THEN 1 ELSE 0 END) | |
| AS `" . PiwikMetrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS . "`"; | |
| $select .= $selectFlagNoResultKeywords | |
| . $selectPageIsFollowingSiteSearch; | |
| } | |
| private function addMetricsToRankingQuery(RankingQuery $rankingQuery, array $metricsConfig): void | |
| { | |
| foreach ($metricsConfig as $metric => $config) { | |
| if (!empty($config['aggregation'])) { | |
| $rankingQuery->addColumn($metric, $config['aggregation']); | |
| } else { | |
| $rankingQuery->addColumn($metric); | |
| } | |
| } | |
| } | |
| private function addMetricsToSelect(string $select, array $metricsConfig): string | |
| { | |
| if (!empty($metricsConfig)) { | |
| foreach ($metricsConfig as $metric => $config) { | |
| $select .= ', ' . $config['query'] . " as `" . $metric . "`"; | |
| } | |
| } | |
| return $select; | |
| } | |
| protected function getWhereClauseActionIsNotEvent(): string | |
| { | |
| return " AND log_link_visit_action.idaction_event_category IS NULL"; | |
| } | |
| /** | |
| * Add goals data for each combination of url / title and pageviews / entries | |
| */ | |
| protected function archiveDayActionsGoals(ArchiveProcessor $archiveProcessor, int $rankingQueryLimit): void | |
| { | |
| $site = $archiveProcessor->getParams()->getSite(); | |
| if ( | |
| !\Piwik\Common::isGoalPluginEnabled() || | |
| GeneralConfig::getConfigValue('disable_archive_actions_goals', $site->getId()) | |
| ) { | |
| return; | |
| } | |
| $goals = $this->getGoalsForSite($site->getId()); | |
| // Add orders and abandoned cart codes if the site is enabled for ecommerce | |
| if ($site->isEcommerceEnabled()) { | |
| $goals[] = GoalManager::IDGOAL_CART; | |
| $goals[] = GoalManager::IDGOAL_ORDER; | |
| } | |
| foreach ($goals as $idGoal) { | |
| $this->archiveDayActionsGoalsPages($archiveProcessor->getLogAggregator(), true, $idGoal); | |
| $this->archiveDayActionsGoalsPages($archiveProcessor->getLogAggregator(), false, $idGoal); | |
| } | |
| $this->archiveDayActionsGoalsPagesEntry($archiveProcessor, $rankingQueryLimit, true); | |
| $this->archiveDayActionsGoalsPagesEntry($archiveProcessor, $rankingQueryLimit, false); | |
| } | |
| /** | |
| * Query goal page view data and update actions data table | |
| * | |
| * @param bool $isUrl If true then query goal data by url, else by name | |
| * @param int $idGoal Goal to archive | |
| * | |
| * @return int|null Count of records processed | |
| * @throws \Exception | |
| */ | |
| protected function archiveDayActionsGoalsPages(LogAggregator $logAggregator, bool $isUrl, int $idGoal): ?int | |
| { | |
| $linkField = ($isUrl ? 'idaction_url' : 'idaction_name'); | |
| $resultSet = $logAggregator->queryConversionsByPageView($linkField, $idGoal); | |
| if (!$resultSet) { | |
| return null; | |
| } | |
| return ArchivingHelper::updateActionsTableWithGoals($resultSet, true); | |
| } | |
| /** | |
| * Get a list of goal ids for a site | |
| * | |
| * @return array | |
| */ | |
| private function getGoalsForSite(string $idSite): array | |
| { | |
| $cache = Cache::getTransientCache(); | |
| $key = 'ActionArchives_allGoalIds_' . $idSite; | |
| if ($cache->contains($key)) { | |
| return $cache->fetch($key); | |
| } | |
| $siteGoals = Request::processRequest('Goals.getGoals', ['idSite' => $idSite, 'filter_limit' => '-1'], $default = []); | |
| $goalIds = array_column($siteGoals, 'idgoal'); | |
| $cache->save($key, $goalIds); | |
| return $goalIds; | |
| } | |
| /** | |
| * Query goal entry page data and update actions data table | |
| * | |
| * @param bool $isUrl If true then query goal data by url, else by name | |
| * | |
| * @return int|null Count of records processed | |
| * @throws \Exception | |
| */ | |
| protected function archiveDayActionsGoalsPagesEntry(ArchiveProcessor $archiveProcessor, int $rankingQueryLimit, bool $isUrl): ?int | |
| { | |
| if (GeneralConfig::getConfigValue('disable_archive_actions_goals', $archiveProcessor->getParams()->getSite()->getId())) { | |
| return null; | |
| } | |
| $linkField = ($isUrl ? 'visit_entry_idaction_url' : 'visit_entry_idaction_name'); | |
| $resultSet = $archiveProcessor->getLogAggregator()->queryConversionsByEntryPageView($linkField, $rankingQueryLimit); | |
| if (!$resultSet) { | |
| return null; | |
| } | |
| return ArchivingHelper::updateActionsTableWithGoals($resultSet, false); | |
| } | |
| } | |