1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
<?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.");
}
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;
}
}
|