قرینه از
https://github.com/matomo-org/matomo.git
synced 2025-08-22 23:17:46 +00:00
210 خطوط
6.7 KiB
PHP
210 خطوط
6.7 KiB
PHP
<?php
|
|
|
|
/**
|
|
* 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\UserCountry\Commands;
|
|
|
|
use Piwik\Plugin\ConsoleCommand;
|
|
use Piwik\Plugins\UserCountry\VisitorGeolocator;
|
|
use Piwik\Plugins\UserCountry\LocationProvider;
|
|
use Piwik\DataAccess\RawLogDao;
|
|
use Piwik\Timer;
|
|
|
|
class AttributeHistoricalDataWithLocations extends ConsoleCommand
|
|
{
|
|
public const DATES_RANGE_ARGUMENT = 'dates-range';
|
|
public const PERCENT_STEP_ARGUMENT = 'percent-step';
|
|
public const PERCENT_STEP_ARGUMENT_DEFAULT = 5;
|
|
public const PROVIDER_ARGUMENT = 'provider';
|
|
public const SEGMENT_LIMIT_OPTION = 'segment-limit';
|
|
public const SEGMENT_LIMIT_OPTION_DEFAULT = 1000;
|
|
public const FORCE_OPTION = 'force';
|
|
|
|
/**
|
|
* @var RawLogDao
|
|
*/
|
|
protected $dao;
|
|
|
|
/**
|
|
* @var VisitorGeolocator
|
|
*/
|
|
protected $visitorGeolocator;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
private $processed = 0;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
private $amountOfVisits;
|
|
|
|
/**
|
|
* @var Timer
|
|
*/
|
|
private $timer;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
private $percentStep;
|
|
|
|
/**
|
|
* @var int
|
|
*/
|
|
private $processedPercent = 0;
|
|
|
|
public function __construct(?RawLogDao $dao = null)
|
|
{
|
|
parent::__construct();
|
|
|
|
$this->dao = $dao ?: new RawLogDao();
|
|
}
|
|
|
|
protected function configure()
|
|
{
|
|
$this->setName('usercountry:attribute');
|
|
$this->setDescription("Re-attribute existing raw data (visits & conversions) with geolocated location data, using the specified or configured location provider.");
|
|
$this->addRequiredArgument(self::DATES_RANGE_ARGUMENT, 'Attribute visits in this date range. Eg, 2012-01-01,2013-01-01');
|
|
$this->addOptionalValueOption(
|
|
self::PERCENT_STEP_ARGUMENT,
|
|
null,
|
|
'How often to display the command progress. A status update will be printed after N percent of visits are processed, '
|
|
. 'where N is the value of this option.',
|
|
self::PERCENT_STEP_ARGUMENT_DEFAULT
|
|
);
|
|
$this->addRequiredValueOption(self::PROVIDER_ARGUMENT, null, 'Provider id which should be used to attribute visits. If empty then'
|
|
. ' Piwik will use the currently configured provider. If no provider is configured, the default provider is used.');
|
|
$this->addOptionalValueOption(self::SEGMENT_LIMIT_OPTION, null, 'Number of visits to process at a time.', self::SEGMENT_LIMIT_OPTION_DEFAULT);
|
|
$this->addNoValueOption(self::FORCE_OPTION, null, "Force geolocation, even if the requested provider does not appear to work. It is not "
|
|
. "recommended to use this option.");
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
protected function doExecute(): int
|
|
{
|
|
$input = $this->getInput();
|
|
$output = $this->getOutput();
|
|
[$from, $to] = $this->getDateRangeToAttribute();
|
|
|
|
$this->visitorGeolocator = $this->createGeolocator();
|
|
|
|
$this->percentStep = $this->getPercentStep();
|
|
$this->amountOfVisits = $this->dao->countVisitsWithDatesLimit($from, $to);
|
|
|
|
$output->writeln(
|
|
sprintf(
|
|
'Re-attribution for date range: %s to %s. %d visits to process with provider "%s".',
|
|
$from,
|
|
$to,
|
|
$this->amountOfVisits,
|
|
$this->visitorGeolocator->getProvider()->getId()
|
|
)
|
|
);
|
|
|
|
$this->timer = new Timer();
|
|
|
|
$this->processSpecifiedLogsInChunks($from, $to, $input->getOption(self::SEGMENT_LIMIT_OPTION));
|
|
|
|
$output->writeln("Completed. <comment>" . $this->timer->__toString() . "</comment>");
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
protected function processSpecifiedLogsInChunks($from, $to, $segmentLimit)
|
|
{
|
|
$self = $this;
|
|
$this->visitorGeolocator->reattributeVisitLogs($from, $to, $idSite = null, $segmentLimit, function () use ($self) {
|
|
$self->onVisitProcessed();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
protected function getPercentStep()
|
|
{
|
|
$percentStep = $this->getInput()->getOption(self::PERCENT_STEP_ARGUMENT);
|
|
|
|
if (!is_numeric($percentStep)) {
|
|
return self::PERCENT_STEP_ARGUMENT_DEFAULT;
|
|
}
|
|
|
|
// Percent step should be between maximum percent value and minimum percent value (1-100)
|
|
if ($percentStep > 99 || $percentStep < 1) {
|
|
return 100;
|
|
}
|
|
|
|
return $percentStep;
|
|
}
|
|
|
|
/**
|
|
* Print information about progress.
|
|
*/
|
|
public function onVisitProcessed()
|
|
{
|
|
++$this->processed;
|
|
|
|
$percent = ceil($this->processed / $this->amountOfVisits * 100);
|
|
|
|
if (
|
|
$percent > $this->processedPercent
|
|
&& $percent % $this->percentStep === 0
|
|
) {
|
|
$this->getOutput()->writeln(sprintf('%d%% processed. <comment>%s</comment>', $percent, $this->timer->__toString()));
|
|
|
|
$this->processedPercent = $percent;
|
|
}
|
|
}
|
|
|
|
private function getDateRangeToAttribute()
|
|
{
|
|
$dateRangeString = $this->getInput()->getArgument(self::DATES_RANGE_ARGUMENT);
|
|
|
|
$dates = explode(',', $dateRangeString);
|
|
$dates = array_map(array('Piwik\Date', 'factory'), $dates);
|
|
|
|
if (count($dates) != 2) {
|
|
throw new \InvalidArgumentException('Invalid date range supplied: ' . $dateRangeString);
|
|
}
|
|
|
|
return $dates;
|
|
}
|
|
|
|
private function createGeolocator()
|
|
{
|
|
$input = $this->getInput();
|
|
$output = $this->getOutput();
|
|
$providerId = $input->getOption(self::PROVIDER_ARGUMENT);
|
|
$geolocator = new VisitorGeolocator(LocationProvider::getProviderById($providerId) ?: null);
|
|
|
|
$usedProvider = $geolocator->getProvider();
|
|
if (!$usedProvider->isAvailable()) {
|
|
throw new \InvalidArgumentException("The provider '$providerId' is not currently available, please make sure it is configured correctly.");
|
|
}
|
|
|
|
$isWorkingOrErrorMessage = $usedProvider->isWorking();
|
|
if ($isWorkingOrErrorMessage !== true) {
|
|
$errorMessage = "The provider '$providerId' does not appear to be working correctly. Details: $isWorkingOrErrorMessage";
|
|
|
|
$forceGeolocation = $input->getOption(self::FORCE_OPTION);
|
|
if ($forceGeolocation) {
|
|
$output->writeln("<error>$errorMessage</error>");
|
|
$output->writeln("<comment>Ignoring location provider issue, geolocating anyway due to --force option.</comment>");
|
|
} else {
|
|
throw new \InvalidArgumentException($errorMessage);
|
|
}
|
|
}
|
|
|
|
return $geolocator;
|
|
}
|
|
}
|