mirror of
https://github.com/shlinkio/shlink.git
synced 2024-12-27 01:11:39 -06:00
Implemented how to reprocess the locations of all existing visits
This commit is contained in:
parent
fcce18b059
commit
fb8ab0b5fe
@ -18,6 +18,7 @@ use Shlinkio\Shlink\Core\Visit\VisitLocatorInterface;
|
||||
use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException;
|
||||
use Shlinkio\Shlink\IpGeolocation\Model\Location;
|
||||
use Shlinkio\Shlink\IpGeolocation\Resolver\IpLocationResolverInterface;
|
||||
use Symfony\Component\Console\Exception\RuntimeException;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
@ -60,30 +61,66 @@ class LocateVisitsCommand extends AbstractLockedCommand implements VisitGeolocat
|
||||
'retry',
|
||||
'r',
|
||||
InputOption::VALUE_NONE,
|
||||
'Will retry visits that were located with an empty location, in case it was a temporal issue.',
|
||||
'Will retry the location of visits that were located with a not-found location, in case it was due to '
|
||||
. 'a temporal issue.',
|
||||
)
|
||||
->addOption(
|
||||
'all',
|
||||
'a',
|
||||
InputOption::VALUE_NONE,
|
||||
'Will locate all visits, ignoring if they have already been located.',
|
||||
'When provided together with --retry, will locate all existing visits, regardless the fact that they '
|
||||
. 'have already been located.',
|
||||
);
|
||||
}
|
||||
|
||||
protected function interact(InputInterface $input, OutputInterface $output): void
|
||||
{
|
||||
$this->io = new SymfonyStyle($input, $output);
|
||||
$retry = $input->getOption('retry');
|
||||
$all = $input->getOption('all');
|
||||
|
||||
if ($all && !$retry) {
|
||||
$this->io->writeln(
|
||||
'<comment>The <fg=yellow;options=bold>--all</> flag has no effect on its own. You have to provide it '
|
||||
. 'together with <fg=yellow;options=bold>--retry</>.</comment>',
|
||||
);
|
||||
}
|
||||
|
||||
if ($all && $retry && ! $this->warnAndVerifyContinue()) {
|
||||
throw new RuntimeException('Execution aborted');
|
||||
}
|
||||
}
|
||||
|
||||
private function warnAndVerifyContinue(): bool
|
||||
{
|
||||
$this->io->warning([
|
||||
'You are about to process the location of all existing visits your short URLs received.',
|
||||
'Since shlink saves visitors IP addresses anonymized, you could end up losing precision on some of '
|
||||
. 'your visits.',
|
||||
'Also, if you have a large amount of visits, this can be a very time consuming process. '
|
||||
. 'Continue at your own risk.',
|
||||
]);
|
||||
return $this->io->confirm('Do you want to proceed?', false);
|
||||
}
|
||||
|
||||
protected function lockedExecute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->io = new SymfonyStyle($input, $output);
|
||||
$retry = $input->getOption('retry');
|
||||
$all = $retry && $input->getOption('all');
|
||||
|
||||
try {
|
||||
$this->checkDbUpdate();
|
||||
|
||||
$this->visitLocator->locateUnlocatedVisits($this);
|
||||
if ($retry) {
|
||||
$this->visitLocator->locateVisitsWithEmptyLocation($this);
|
||||
if ($all) {
|
||||
$this->visitLocator->locateAllVisits($this);
|
||||
} else {
|
||||
$this->visitLocator->locateUnlocatedVisits($this);
|
||||
if ($retry) {
|
||||
$this->visitLocator->locateVisitsWithEmptyLocation($this);
|
||||
}
|
||||
}
|
||||
|
||||
$this->io->success('Finished processing all IPs');
|
||||
$this->io->success('Finished locating visits');
|
||||
return ExitCodes::EXIT_SUCCESS;
|
||||
} catch (Throwable $e) {
|
||||
$this->io->error($e->getMessage());
|
||||
|
@ -40,6 +40,15 @@ class VisitRepository extends EntityRepository implements VisitRepositoryInterfa
|
||||
return $this->findVisitsForQuery($qb, $blockSize);
|
||||
}
|
||||
|
||||
public function findAllVisits(int $blockSize = self::DEFAULT_BLOCK_SIZE): iterable
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
$qb->select('v')
|
||||
->from(Visit::class, 'v');
|
||||
|
||||
return $this->findVisitsForQuery($qb, $blockSize);
|
||||
}
|
||||
|
||||
private function findVisitsForQuery(QueryBuilder $qb, int $blockSize): iterable
|
||||
{
|
||||
$originalQueryBuilder = $qb->setMaxResults($blockSize)
|
||||
|
@ -15,12 +15,17 @@ interface VisitRepositoryInterface extends ObjectRepository
|
||||
/**
|
||||
* @return iterable|Visit[]
|
||||
*/
|
||||
public function findUnlocatedVisits(int $defaultBlockSize = self::DEFAULT_BLOCK_SIZE): iterable;
|
||||
public function findUnlocatedVisits(int $blockSize = self::DEFAULT_BLOCK_SIZE): iterable;
|
||||
|
||||
/**
|
||||
* @return iterable|Visit[]
|
||||
*/
|
||||
public function findVisitsWithEmptyLocation(int $defaultBlockSize = self::DEFAULT_BLOCK_SIZE): iterable;
|
||||
public function findVisitsWithEmptyLocation(int $blockSize = self::DEFAULT_BLOCK_SIZE): iterable;
|
||||
|
||||
/**
|
||||
* @return iterable|Visit[]
|
||||
*/
|
||||
public function findAllVisits(int $blockSize = self::DEFAULT_BLOCK_SIZE): iterable;
|
||||
|
||||
/**
|
||||
* @return Visit[]
|
||||
|
@ -35,6 +35,11 @@ class VisitLocator implements VisitLocatorInterface
|
||||
$this->locateVisits($this->repo->findVisitsWithEmptyLocation(), $helper);
|
||||
}
|
||||
|
||||
public function locateAllVisits(VisitGeolocationHelperInterface $helper): void
|
||||
{
|
||||
$this->locateVisits($this->repo->findAllVisits(), $helper);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param iterable|Visit[] $results
|
||||
*/
|
||||
|
@ -9,4 +9,6 @@ interface VisitLocatorInterface
|
||||
public function locateUnlocatedVisits(VisitGeolocationHelperInterface $helper): void;
|
||||
|
||||
public function locateVisitsWithEmptyLocation(VisitGeolocationHelperInterface $helper): void;
|
||||
|
||||
public function locateAllVisits(VisitGeolocationHelperInterface $helper): void;
|
||||
}
|
||||
|
@ -68,11 +68,13 @@ class VisitRepositoryTest extends DatabaseTestCase
|
||||
|
||||
$withEmptyLocation = $this->repo->findVisitsWithEmptyLocation($blockSize);
|
||||
$unlocated = $this->repo->findUnlocatedVisits($blockSize);
|
||||
$all = $this->repo->findAllVisits($blockSize);
|
||||
|
||||
// Important! assertCount will not work here, as this iterable object loads data dynamically and counts to
|
||||
// 0 if not iterated
|
||||
// Important! assertCount will not work here, as this iterable object loads data dynamically and the count
|
||||
// is 0 if not iterated
|
||||
$this->assertEquals(2, $countIterable($unlocated));
|
||||
$this->assertEquals(4, $countIterable($withEmptyLocation));
|
||||
$this->assertEquals(6, $countIterable($all));
|
||||
}
|
||||
|
||||
public function provideBlockSize(): iterable
|
||||
|
Loading…
Reference in New Issue
Block a user