Add service and command to import French addresses from BAN

Introduce a service to handle the import of French addresses from the Base Adresse Nationale (BAN) dataset. Add a new console command `chill:main:address-ref-from-ban` to trigger the import by department numbers, with an option to send a report email for unmatched addresses.
This commit is contained in:
2025-01-10 22:52:08 +01:00
parent d87cf925e2
commit b4a1e824ac
3 changed files with 160 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
<?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\Import;
use League\Csv\Reader;
use League\Csv\Statement;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class AddressReferenceFromBAN
{
public function __construct(
private readonly HttpClientInterface $client,
private readonly AddressReferenceBaseImporter $baseImporter,
private readonly AddressToReferenceMatcher $addressToReferenceMatcher,
) {}
public function import(string $departementNo, ?string $sendAddressReportToEmail = null): void
{
if (!is_numeric($departementNo)) {
throw new \UnexpectedValueException('Could not parse this department number');
}
$url = sprintf('https://adresse.data.gouv.fr/data/ban/adresses/latest/csv/adresses-%s.csv.gz', $departementNo);
$response = $this->client->request('GET', $url);
if (200 !== $response->getStatusCode()) {
throw new \Exception('Could not download CSV: '.$response->getStatusCode());
}
$path = sys_get_temp_dir().'/'.$departementNo.'.csv.gz';
$file = fopen($path, 'w');
if (false === $file) {
throw new \Exception('Could not create temporary file');
}
foreach ($this->client->stream($response) as $chunk) {
fwrite($file, $chunk->getContent());
}
fclose($file);
// re-open it to read it
$csvDecompressed = gzopen($path, 'r');
$csv = Reader::createFromStream($csvDecompressed);
$csv->setDelimiter(';')->setHeaderOffset(0);
$stmt = Statement::create()
->process($csv, [
'id',
'id_fantoir',
'numero',
'rep',
'nom_voie',
'code_postal',
'code_insee',
'nom_commune',
'code_insee_ancienne_commune',
'nom_ancienne_commune',
'x',
'y',
'lon',
'lat',
'type_position',
'alias',
'nom_ld',
'libelle_acheminement',
'nom_afnor',
'source_position',
'source_nom_voie',
'certification_commune',
'cad_parcelles',
]);
foreach ($stmt as $record) {
$this->baseImporter->importAddress(
$record['id'],
$record['code_insee'],
$record['code_postal'],
$record['nom_voie'],
$record['numero'].' '.$record['rep'],
'BAN.'.$departementNo,
(float) $record['lat'],
(float) $record['lon'],
4326
);
}
$this->baseImporter->finalize(sendAddressReportToEmail: $sendAddressReportToEmail);
$this->addressToReferenceMatcher->checkAddressesMatchingReferences();
fclose($csvDecompressed);
unlink($path);
}
}