Download plugins/CoreAdminHome/Commands/FixDuplicateLogActions.php from Leon4gr45/matomo: direct link, hf CLI and curl.
- Browser
- Download file 7.63 kB
-
https://huggingface.co/spaces/Leon4gr45/matomo/resolve/main/plugins/CoreAdminHome/Commands/FixDuplicateLogActions.php
- Command line
-
hf download hf://spaces/Leon4gr45/matomo/plugins/CoreAdminHome/Commands/FixDuplicateLogActions.php
-
curl -L -o FixDuplicateLogActions.php https://huggingface.co/spaces/Leon4gr45/matomo/resolve/main/plugins/CoreAdminHome/Commands/FixDuplicateLogActions.php
7.63 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\CoreAdminHome\Commands; | |
| use Piwik\Common; | |
| use Piwik\Container\StaticContainer; | |
| use Piwik\DataAccess\Actions; | |
| use Piwik\Archive\ArchiveInvalidator; | |
| use Piwik\Date; | |
| use Piwik\Plugin\ConsoleCommand; | |
| use Piwik\Plugins\CoreAdminHome\Model\DuplicateActionRemover; | |
| use Piwik\Timer; | |
| use Piwik\Log\LoggerInterface; | |
| /** | |
| * Finds duplicate actions rows in log_action and removes them. Fixes references to duplicate | |
| * actions in the log_link_visit_action table, log_conversion table, and log_conversion_item | |
| * table. | |
| * | |
| * Prior to version 2.11, there was a race condition in the tracker where it was possible for | |
| * two or more actions with the same name and type to be inserted simultaneously. This resulted | |
| * in inaccurate data. A Piwik database with this problem can be fixed using this class. | |
| * | |
| * With version 2.11 and above, it is still possible for duplicate actions to be inserted, but | |
| * ONLY if the tracker's PHP process fails suddenly right after inserting an action. This is | |
| * very rare, and even if it does happen, report data will not be affected, but the extra | |
| * actions can be deleted w/ this class. | |
| */ | |
| class FixDuplicateLogActions extends ConsoleCommand | |
| { | |
| /** | |
| * Used to invalidate archives. Only used if $shouldInvalidateArchives is true. | |
| * | |
| * @var ArchiveInvalidator | |
| */ | |
| private $archiveInvalidator; | |
| /** | |
| * DAO used to find duplicate actions in log_action and fix references to them in other tables. | |
| * | |
| * @var DuplicateActionRemover | |
| */ | |
| private $duplicateActionRemover; | |
| /** | |
| * DAO used to remove actions from the log_action table. | |
| * | |
| * @var Actions | |
| */ | |
| private $actionsAccess; | |
| /** | |
| * @var LoggerInterface | |
| */ | |
| private $logger; | |
| public function __construct( | |
| ?ArchiveInvalidator $invalidator = null, | |
| ?DuplicateActionRemover $duplicateActionRemover = null, | |
| ?Actions $actionsAccess = null, | |
| ?LoggerInterface $logger = null | |
| ) { | |
| parent::__construct(); | |
| $this->archiveInvalidator = $invalidator ?: StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); | |
| $this->duplicateActionRemover = $duplicateActionRemover ?: new DuplicateActionRemover(); | |
| $this->actionsAccess = $actionsAccess ?: new Actions(); | |
| $this->logger = $logger ?: StaticContainer::get(LoggerInterface::class); | |
| } | |
| protected function configure() | |
| { | |
| $this->setName('core:fix-duplicate-log-actions'); | |
| $this->addNoValueOption('invalidate-archives', null, "If supplied, archives for logs that use duplicate actions will be invalidated." | |
| . " On the next cron archive run, the reports for those dates will be re-processed."); | |
| $this->setDescription('Removes duplicates in the log action table and fixes references to the duplicates in ' | |
| . 'related tables. NOTE: This action can take a long time to run!'); | |
| } | |
| protected function doExecute(): int | |
| { | |
| $input = $this->getInput(); | |
| $output = $this->getOutput(); | |
| $invalidateArchives = $input->getOption('invalidate-archives'); | |
| $timer = new Timer(); | |
| $duplicateActions = $this->duplicateActionRemover->getDuplicateIdActions(); | |
| if (empty($duplicateActions)) { | |
| $output->writeln("Found no duplicate actions."); | |
| return self::SUCCESS; | |
| } | |
| $output->writeln("<info>Found " . count($duplicateActions) . " actions with duplicates.</info>"); | |
| list($numberRemoved, $allArchivesAffected) = $this->fixDuplicateActionReferences($duplicateActions); | |
| $this->deleteDuplicatesFromLogAction($duplicateActions); | |
| if ($invalidateArchives) { | |
| $this->invalidateArchivesUsingActionDuplicates($allArchivesAffected); | |
| } else { | |
| $this->printAffectedArchives($allArchivesAffected); | |
| } | |
| $logActionTable = Common::prefixTable('log_action'); | |
| $this->writeSuccessMessage(array( | |
| "Found and deleted $numberRemoved duplicate action entries in the $logActionTable table.", | |
| "References in log_link_visit_action, log_conversion and log_conversion_item were corrected.", | |
| $timer->__toString(), | |
| )); | |
| return self::SUCCESS; | |
| } | |
| private function invalidateArchivesUsingActionDuplicates($archivesAffected) | |
| { | |
| $this->getOutput()->write("Invalidating archives affected by duplicates fixed..."); | |
| foreach ($archivesAffected as $archiveInfo) { | |
| $dates = array(Date::factory($archiveInfo['server_time'])); | |
| $this->archiveInvalidator->markArchivesAsInvalidated(array($archiveInfo['idsite']), $dates, $period = false); | |
| } | |
| $this->getOutput()->writeln("Done."); | |
| } | |
| private function printAffectedArchives($allArchivesAffected) | |
| { | |
| $this->getOutput()->writeln("The following archives used duplicate actions and should be invalidated if you want correct reports:"); | |
| foreach ($allArchivesAffected as $archiveInfo) { | |
| $this->getOutput()->writeln("\t[ idSite = {$archiveInfo['idsite']}, date = {$archiveInfo['server_time']} ]"); | |
| } | |
| } | |
| private function fixDuplicateActionReferences($duplicateActions) | |
| { | |
| $dupeCount = count($duplicateActions); | |
| $numberRemoved = 0; | |
| $allArchivesAffected = array(); | |
| foreach ($duplicateActions as $index => $dupeInfo) { | |
| $name = $dupeInfo['name']; | |
| $toIdAction = $dupeInfo['idaction']; | |
| $fromIdActions = $dupeInfo['duplicateIdActions']; | |
| $numberRemoved += count($fromIdActions); | |
| $this->getOutput()->writeln("<info>[$index / $dupeCount]</info> Fixing duplicates for '$name'"); | |
| $this->logger->debug(" idaction = {idaction}, duplicate idactions = {duplicateIdActions}", array( | |
| 'idaction' => $toIdAction, | |
| 'duplicateIdActions' => $fromIdActions, | |
| )); | |
| foreach (DuplicateActionRemover::$tablesWithIdActionColumns as $table) { | |
| $archivesAffected = $this->fixDuplicateActionsInTable($table, $toIdAction, $fromIdActions); | |
| $allArchivesAffected = array_merge($allArchivesAffected, $archivesAffected); | |
| } | |
| } | |
| $allArchivesAffected = array_values(array_unique($allArchivesAffected, SORT_REGULAR)); | |
| return array($numberRemoved, $allArchivesAffected); | |
| } | |
| private function fixDuplicateActionsInTable($table, $toIdAction, $fromIdActions) | |
| { | |
| $timer = new Timer(); | |
| $archivesAffected = $this->duplicateActionRemover->getSitesAndDatesOfRowsUsingDuplicates($table, $fromIdActions); | |
| $this->duplicateActionRemover->fixDuplicateActionsInTable($table, $toIdAction, $fromIdActions); | |
| $this->getOutput()->writeln("\tFixed duplicates in " . Common::prefixTable($table) . ". <comment>" . $timer->__toString() . "</comment>."); | |
| return $archivesAffected; | |
| } | |
| private function deleteDuplicatesFromLogAction($duplicateActions) | |
| { | |
| $logActionTable = Common::prefixTable('log_action'); | |
| $this->getOutput()->writeln("<info>Deleting duplicate actions from $logActionTable...</info>"); | |
| $idActions = []; | |
| foreach ($duplicateActions as $dupeInfo) { | |
| $idActions = array_merge($idActions, $dupeInfo['duplicateIdActions']); | |
| } | |
| $this->actionsAccess->delete($idActions); | |
| } | |
| } | |