mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
[Addresses] create a service to collate addresses with the address reference
This commit is contained in:
parent
3f66e1a862
commit
1552b3c9d7
@ -0,0 +1,147 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Chill\MainBundle\Service\AddressGeographicalUnit;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
|
final readonly class CollateAddressWithReferenceOrPostalCode
|
||||||
|
{
|
||||||
|
private const LOG_PREFIX = '[collate addresses] ';
|
||||||
|
/**
|
||||||
|
* For the address having an "invented" postal code, find the postal code "reference" with the same code,
|
||||||
|
* and the most similar name. When two reference code match, we add
|
||||||
|
*
|
||||||
|
* This query intentionally includes also address with reference, as the reference may be wrong.
|
||||||
|
*/
|
||||||
|
private const FORCE_ORIGINAL_POSTAL_CODE = <<<'SQL'
|
||||||
|
WITH recollate AS (
|
||||||
|
SELECT * FROM (
|
||||||
|
SELECT cma.id AS address_id, cmpc.id, cmpc.label, cmpc.code, cmpc_reference.id AS cmpc_reference_id, cmpc_reference.label, cmpc_reference.code,
|
||||||
|
RANK() OVER (PARTITION BY cma.id ORDER BY SIMILARITY(cmpc.label, cmpc_reference.label) DESC, cmpc_reference.id ASC) AS ranked
|
||||||
|
FROM
|
||||||
|
chill_main_address cma JOIN chill_main_postal_code cmpc on cma.postcode_id = cmpc.id,
|
||||||
|
chill_main_postal_code cmpc_reference
|
||||||
|
WHERE
|
||||||
|
-- use only postal code which are reference
|
||||||
|
cmpc_reference.id != cmpc.id AND cmpc_reference.origin = 0
|
||||||
|
-- only where the reference is null or the cmpc is created manually
|
||||||
|
--AND cma.addressreference_id IS NULL
|
||||||
|
AND cmpc.origin != 0
|
||||||
|
-- only when postal code match
|
||||||
|
AND TRIM(REPLACE(LOWER(cmpc.code), ' ', '')) = LOWER(cmpc_reference.code)
|
||||||
|
AND cma.id > :since_id -- to set the first id
|
||||||
|
) sq
|
||||||
|
WHERE ranked = 1)
|
||||||
|
UPDATE chill_main_address SET postcode_id = cmpc_reference_id FROM recollate WHERE recollate.address_id = chill_main_address.id;
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* associate the address with the most similar address reference.
|
||||||
|
*
|
||||||
|
* This query intentionally ignores the existing addressreference_id, to let fixing the address match the
|
||||||
|
* most similar address reference.
|
||||||
|
*/
|
||||||
|
private const FORCE_MOST_SIMILAR_ADDRESS_REFERENCE = <<<'SQL'
|
||||||
|
WITH recollate AS (
|
||||||
|
SELECT * FROM (
|
||||||
|
SELECT cma.id AS address_id, cma.streetnumber, cma.street, cmpc.code, cmpc.label, cmar.id AS address_reference_id, cmar.streetnumber, cmar.street, cmpc_reference.code, cmpc_reference.label,
|
||||||
|
similarity(cma.street, cmar.street),
|
||||||
|
RANK() OVER (PARTITION BY cma.id ORDER BY SIMILARITY (cma.street, cmar.street) DESC, SIMILARITY (cma.streetnumber, cmar.streetnumber), cmar.id ASC) AS ranked
|
||||||
|
FROM
|
||||||
|
chill_main_address cma
|
||||||
|
JOIN chill_main_postal_code cmpc on cma.postcode_id = cmpc.id,
|
||||||
|
chill_main_address_reference cmar JOIN chill_main_postal_code cmpc_reference ON cmar.postcode_id = cmpc_reference.id
|
||||||
|
WHERE
|
||||||
|
-- only where the reference is null
|
||||||
|
-- cma.addressreference_id IS NULL
|
||||||
|
cma.addressreference_id != cmar.id
|
||||||
|
-- only if cmpc is a reference (must be matched before executing this query)
|
||||||
|
AND cma.postcode_id = cmar.postcode_id
|
||||||
|
-- join cmpc to cma
|
||||||
|
AND SIMILARITY(LOWER(cma.street), LOWER(cmar.street)) > 0.6 AND LOWER(cma.streetnumber) = LOWER(cmar.streetnumber)
|
||||||
|
-- only addresses which match the address reference - let the user decide if the reference has changed
|
||||||
|
AND cma.refstatus = 'match'
|
||||||
|
-- only the most recent
|
||||||
|
AND cma.id > :since_id
|
||||||
|
) AS sq
|
||||||
|
WHERE ranked = 1
|
||||||
|
)
|
||||||
|
UPDATE chill_main_address SET addressreference_id = recollate.address_reference_id FROM recollate WHERE chill_main_address.id = recollate.address_id;
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
private const UPDATE_POINT = <<<'SQL'
|
||||||
|
WITH address_geom AS (
|
||||||
|
SELECT cma.id AS address_id, COALESCE(cmar.point, cmpc.center) AS point
|
||||||
|
FROM chill_main_address cma
|
||||||
|
LEFT JOIN chill_main_address_reference cmar ON cma.addressreference_id = cmar.id
|
||||||
|
LEFT JOIN chill_main_postal_code cmpc ON cma.postcode_id = cmpc.id
|
||||||
|
WHERE cma.id > :since_id
|
||||||
|
)
|
||||||
|
UPDATE chill_main_address SET point = address_geom.point FROM address_geom WHERE address_geom.address_id = chill_main_address.id
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
private const MAX_ADDRESS_ID = <<<'SQL'
|
||||||
|
SELECT MAX(id) AS max_id FROM chill_main_address;
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private Connection $connection,
|
||||||
|
private LoggerInterface $logger,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Throwable
|
||||||
|
*/
|
||||||
|
public function __invoke(int $sinceId = 0): int
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
[
|
||||||
|
$postCodeSetReferenceFromMostSimilar,
|
||||||
|
$addressReferenceMatch,
|
||||||
|
$pointUpdates,
|
||||||
|
$lastId,
|
||||||
|
] = $this->connection->transactional(function () use ($sinceId) {
|
||||||
|
$postCodeSetReferenceFromMostSimilar = $this->connection->executeQuery(self::FORCE_ORIGINAL_POSTAL_CODE, ['since_id' => $sinceId]);
|
||||||
|
$addressReferenceMatch = $this->connection->executeQuery(self::FORCE_MOST_SIMILAR_ADDRESS_REFERENCE, ['since_id' => $sinceId]);
|
||||||
|
$pointUpdates = $this->connection->executeQuery(self::UPDATE_POINT, ['since_id' => $sinceId]);
|
||||||
|
$lastId = $this->connection->fetchOne(self::MAX_ADDRESS_ID);
|
||||||
|
|
||||||
|
return [
|
||||||
|
$postCodeSetReferenceFromMostSimilar,
|
||||||
|
$addressReferenceMatch,
|
||||||
|
$pointUpdates,
|
||||||
|
$lastId,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->logger->error(self::LOG_PREFIX . "error while re-collating addresses", [
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->logger->info(self::LOG_PREFIX . "Collate the addresses with reference", [
|
||||||
|
'set_postcode_from_most_similar' => $postCodeSetReferenceFromMostSimilar,
|
||||||
|
'address_reference_match' => $addressReferenceMatch,
|
||||||
|
'point_update' => $pointUpdates,
|
||||||
|
'since_id' => $sinceId,
|
||||||
|
'last_id' => $lastId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $lastId;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Chill is a software for social workers
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view
|
||||||
|
* the LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Services\AddressGeographicalUnit;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Service\AddressGeographicalUnit\CollateAddressWithReferenceOrPostalCode;
|
||||||
|
use Doctrine\DBAL\Connection;
|
||||||
|
use Psr\Log\NullLogger;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
* @coversNothing
|
||||||
|
*/
|
||||||
|
class CollateAddressWithReferenceOrPostalCodeTest extends KernelTestCase
|
||||||
|
{
|
||||||
|
private Connection $connection;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
self::bootKernel();
|
||||||
|
$this->connection = self::$container->get(Connection::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRun(): void
|
||||||
|
{
|
||||||
|
$collator = new CollateAddressWithReferenceOrPostalCode(
|
||||||
|
$this->connection,
|
||||||
|
new NullLogger()
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = $collator(0);
|
||||||
|
|
||||||
|
self::assertGreaterThan(0, $result);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user