diff --git a/assets/scripts/vanilla/services/component_search_address.js b/assets/scripts/vanilla/services/component_search_address.js index 62507cbe9..4e29deb26 100644 --- a/assets/scripts/vanilla/services/component_search_address.js +++ b/assets/scripts/vanilla/services/component_search_address.js @@ -74,12 +74,6 @@ export function attacheAutocompleteAddressEvent (inputAdresse) { if (document?.querySelector('#' + idForm + ' [data-autocomplete-insee]')) { document.querySelector('#' + idForm + ' [data-autocomplete-insee]').value = '' } - if (document?.querySelector('#' + idForm + ' [data-autocomplete-geoloclng]')) { - document.querySelector('#' + idForm + ' [data-autocomplete-geoloclng]').value = '' - } - if (document?.querySelector('#' + idForm + ' [data-autocomplete-geoloclat]')) { - document.querySelector('#' + idForm + ' [data-autocomplete-geoloclat]').value = '' - } } }) @@ -88,7 +82,7 @@ export function attacheAutocompleteAddressEvent (inputAdresse) { addressGroup.innerHTML = '' selectionIndex = -1 } - + if (e.key === 'ArrowDown') { if (addressGroup.children.length > 0) { selectionIndex = Math.min(selectionIndex + 1, addressGroup.children.length - 1) @@ -143,12 +137,6 @@ function attachAddressSuggestionEvent (inputAdresse, suggestion, feature) { if (document?.querySelector('#' + idForm + ' [data-autocomplete-insee]')) { document.querySelector('#' + idForm + ' [data-autocomplete-insee]').value = feature.properties.citycode } - if (document?.querySelector('#' + idForm + ' [data-autocomplete-geolocLng]')) { - document.querySelector('#' + idForm + ' [data-autocomplete-geolocLng]').value = feature.geometry.coordinates[0] - } - if (document?.querySelector('#' + idForm + ' [data-autocomplete-geolocLat]')) { - document.querySelector('#' + idForm + ' [data-autocomplete-geolocLat]').value = feature.geometry.coordinates[1] - } const addressGroup = document?.querySelector(inputAdresse.dataset.autocompleteQuerySelector) addressGroup.innerHTML = '' }) diff --git a/assets/scripts/vanilla/services/tabs_manager.js b/assets/scripts/vanilla/services/tabs_manager.js index 56567cb40..0543c7b70 100644 --- a/assets/scripts/vanilla/services/tabs_manager.js +++ b/assets/scripts/vanilla/services/tabs_manager.js @@ -1,9 +1,9 @@ -const currentHash = window.location.hash != '' ? window.location.hash.substring(1) : '' -const btnElements = document.querySelectorAll(`.fr-tabs__tab`); -const tabElements = document.querySelectorAll(`.fr-tabs__panel`); -const buttonElement = document.getElementById('tabpanel-'+currentHash) -const tabElement = document.getElementById('tabpanel-'+currentHash+'-panel'); -if(buttonElement && tabElement){ +const currentHash = window.location.hash !== '' ? window.location.hash.substring(1) : '' +const btnElements = document.querySelectorAll('.fr-tabs__tab') +const tabElements = document.querySelectorAll('.fr-tabs__panel') +const buttonElement = document.getElementById('tabpanel-' + currentHash) +const tabElement = document.getElementById('tabpanel-' + currentHash + '-panel') +if (buttonElement && tabElement) { if (!tabElement.disabled) { btnElements.forEach((btnElement) => { btnElement.setAttribute('aria-selected', 'false') @@ -16,7 +16,7 @@ if(buttonElement && tabElement){ } } btnElements.forEach((btnElement) => { - btnElement.addEventListener('click', (e) => { - window.location.hash = btnElement.id.substring(9) - }) -}) \ No newline at end of file + btnElement.addEventListener('click', (e) => { + window.location.hash = btnElement.id.substring(9) + }) +}) diff --git a/assets/scripts/vue/components/signalement-form/TheSignalementAppForm.vue b/assets/scripts/vue/components/signalement-form/TheSignalementAppForm.vue index c73905c9f..253dbdb05 100644 --- a/assets/scripts/vue/components/signalement-form/TheSignalementAppForm.vue +++ b/assets/scripts/vue/components/signalement-form/TheSignalementAppForm.vue @@ -212,8 +212,6 @@ export default defineComponent({ if (suggestions[0] !== undefined) { formStore.data.adresse_logement_adresse_detail_commune = suggestions[0].properties.city formStore.data.adresse_logement_adresse_detail_insee = suggestions[0].properties.citycode - formStore.data.adresse_logement_adresse_detail_geoloc_lng = suggestions[0].geometry.coordinates[0] - formStore.data.adresse_logement_adresse_detail_geoloc_lat = suggestions[0].geometry.coordinates[1] formStore.data.adresse_logement_adresse = formStore.data.adresse_logement_adresse_detail_numero + ' ' + formStore.data.adresse_logement_adresse_detail_code_postal + ' ' + formStore.data.adresse_logement_adresse_detail_commune formStore.data.adresse_logement_adresse_suggestion = formStore.data.adresse_logement_adresse_detail_numero + ' ' + formStore.data.adresse_logement_adresse_detail_code_postal + ' ' + formStore.data.adresse_logement_adresse_detail_commune diff --git a/assets/scripts/vue/components/signalement-form/components/SignalementFormAddress.vue b/assets/scripts/vue/components/signalement-form/components/SignalementFormAddress.vue index 98b461bf2..cc91f2538 100644 --- a/assets/scripts/vue/components/signalement-form/components/SignalementFormAddress.vue +++ b/assets/scripts/vue/components/signalement-form/components/SignalementFormAddress.vue @@ -222,8 +222,6 @@ export default defineComponent({ this.formStore.data[this.id + '_detail_code_postal'] = this.suggestions[index].properties.postcode this.formStore.data[this.id + '_detail_commune'] = this.suggestions[index].properties.city this.formStore.data[this.id + '_detail_insee'] = this.suggestions[index].properties.citycode - this.formStore.data[this.id + '_detail_geoloc_lng'] = this.suggestions[index].geometry.coordinates[0] - this.formStore.data[this.id + '_detail_geoloc_lat'] = this.suggestions[index].geometry.coordinates[1] this.formStore.data[this.id + '_detail_manual'] = 0 this.suggestions.length = 0 setTimeout(() => { diff --git a/migrations/Version20241125151642.php b/migrations/Version20241125151642.php new file mode 100644 index 000000000..0fb1cd7df --- /dev/null +++ b/migrations/Version20241125151642.php @@ -0,0 +1,26 @@ +addSql('ALTER TABLE signalement ADD ban_id_occupant VARCHAR(50) DEFAULT NULL'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE signalement DROP ban_id_occupant'); + } +} diff --git a/src/Command/InitIdBanCommand.php b/src/Command/InitIdBanCommand.php new file mode 100644 index 000000000..4bea105c8 --- /dev/null +++ b/src/Command/InitIdBanCommand.php @@ -0,0 +1,80 @@ +historyEntryManager->removeEntityListeners(); + + $io = new SymfonyStyle($input, $output); + + $signalementIdsWithouBanId = $this->signalementRepository->findNullBanId(); + $nbSignalementWithoutBanId = \count($signalementIdsWithouBanId); + + $nb = 0; + $progressBar = new ProgressBar($output, $nbSignalementWithoutBanId); + $progressBar->start(); + + $nbBatch = ceil($nbSignalementWithoutBanId / self::BATCH_SIZE); + for ($i = 0; $i < $nbBatch; ++$i) { + $signalementsBatch = array_splice($signalementIdsWithouBanId, 0, self::BATCH_SIZE); + $signalementsIdsBatch = []; + foreach ($signalementsBatch as $signalementBatch) { + $signalementsIdsBatch[] = $signalementBatch['id']; + } + $listSignalementBanIdNull = $this->signalementRepository->findBy(['id' => $signalementsIdsBatch]); + + /** @var Signalement $signalement */ + foreach ($listSignalementBanIdNull as $signalement) { + $this->signalementManager->updateAddressOccupantFromBanData( + signalement: $signalement, + updateGeoloc: false, + ); + if (!empty($signalement->getBanIdOccupant())) { + ++$nb; + } + $progressBar->advance(); + } + $this->entityManager->flush(); + } + + $progressBar->finish(); + $nbSignalementWithoutBanId = $this->signalementRepository->count(['banIdOccupant' => '0']); + $io->success(\sprintf( + '%s BAN IDs have been initialized, but %s signalements remain with no BAN ID', + $nb, + $nbSignalementWithoutBanId + )); + + return Command::SUCCESS; + } +} diff --git a/src/Dto/Request/Signalement/AdresseOccupantRequest.php b/src/Dto/Request/Signalement/AdresseOccupantRequest.php index 69fb75d4c..1eaf6fc67 100644 --- a/src/Dto/Request/Signalement/AdresseOccupantRequest.php +++ b/src/Dto/Request/Signalement/AdresseOccupantRequest.php @@ -25,12 +25,6 @@ public function __construct( private readonly ?string $numAppart = null, #[Assert\Length(max: 255, maxMessage: 'Le champ Autre ne peut pas dépasser {{ limit }} caractères.')] private readonly ?string $autre = null, - #[Assert\Length(max: 50, maxMessage: 'La longitude ne peut pas dépasser {{ limit }} caractères.')] - #[Assert\Regex(pattern: '/^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?)$/', message: 'La longitude doit être un nombre décimal.')] - private readonly ?string $geolocLng = null, - #[Assert\Length(max: 50, maxMessage: 'La latitude ne peut pas dépasser {{ limit }} caractères.')] - #[Assert\Regex(pattern: '/^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?)$/', message: 'La latitude doit être un nombre décimal.')] - private readonly ?string $geolocLat = null, #[Assert\Regex(pattern: '/^[0-9][0-9A-Za-z][0-9]{3}$/', message: 'Le code insee doit être composé de 5 caractères.')] private readonly ?string $insee = null, #[Assert\Choice(choices: ['1'], message: 'Le champ "manual" est incorrect.')] @@ -75,16 +69,6 @@ public function getAutre(): ?string return $this->autre; } - public function getGeolocLng(): ?string - { - return $this->geolocLng; - } - - public function getGeolocLat(): ?string - { - return $this->geolocLat; - } - public function getInsee(): ?string { return $this->insee; diff --git a/src/Dto/Request/Signalement/SignalementDraftRequest.php b/src/Dto/Request/Signalement/SignalementDraftRequest.php index 5364e1a52..6d4bf79a7 100644 --- a/src/Dto/Request/Signalement/SignalementDraftRequest.php +++ b/src/Dto/Request/Signalement/SignalementDraftRequest.php @@ -54,18 +54,6 @@ class SignalementDraftRequest #[Assert\NotBlank(message: 'Merci de saisir un code INSEE.')] #[Assert\Regex(pattern: '/^[0-9][0-9A-Za-z][0-9]{3}$/', message: 'Le code insee doit être composé de 5 caractères.')] private ?string $adresseLogementAdresseDetailInsee = null; - #[Assert\Length(max: 50, maxMessage: 'La latitude ne peut pas dépasser {{ limit }} caractères.')] - #[Assert\Regex( - pattern: '/^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?)$/', - message: 'La latitude doit être un nombre décimal.' - )] - private ?float $adresseLogementAdresseDetailGeolocLat = null; - #[Assert\Length(max: 50, maxMessage: 'La longitude ne peut pas dépasser {{ limit }} caractères.')] - #[Assert\Regex( - pattern: '/^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?)$/', - message: 'La longitude doit être un nombre décimal.' - )] - private ?float $adresseLogementAdresseDetailGeolocLng = null; private ?bool $adresseLogementAdresseDetailManual = null; #[Assert\Length(max: 3, maxMessage: 'L\'escalier ne doit pas dépasser {{ limit }} caractères')] private ?string $adresseLogementComplementAdresseEscalier = null; @@ -564,30 +552,6 @@ public function setAdresseLogementAdresseDetailInsee(?string $adresseLogementAdr return $this; } - public function getAdresseLogementAdresseDetailGeolocLat(): ?float - { - return $this->adresseLogementAdresseDetailGeolocLat; - } - - public function setAdresseLogementAdresseDetailGeolocLat(?float $adresseLogementAdresseDetailGeolocLat): self - { - $this->adresseLogementAdresseDetailGeolocLat = $adresseLogementAdresseDetailGeolocLat; - - return $this; - } - - public function getAdresseLogementAdresseDetailGeolocLng(): ?float - { - return $this->adresseLogementAdresseDetailGeolocLng; - } - - public function setAdresseLogementAdresseDetailGeolocLng(?float $adresseLogementAdresseDetailGeolocLng): self - { - $this->adresseLogementAdresseDetailGeolocLng = $adresseLogementAdresseDetailGeolocLng; - - return $this; - } - public function getAdresseLogementAdresseDetailManual(): ?bool { return $this->adresseLogementAdresseDetailManual; diff --git a/src/Entity/Signalement.php b/src/Entity/Signalement.php index fddeb0316..546b9c56a 100644 --- a/src/Entity/Signalement.php +++ b/src/Entity/Signalement.php @@ -200,6 +200,9 @@ class Signalement implements EntityHistoryInterface, EntityHistoryCollectionInte #[ORM\Column(type: 'string', length: 100, nullable: true)] private $villeOccupant; + #[ORM\Column(type: 'string', length: 50, nullable: true)] + private $banIdOccupant; + #[ORM\Column(type: 'boolean')] private $isCguAccepted; @@ -1051,6 +1054,18 @@ public function getAddressCompleteOccupant(): ?string ); } + public function getBanIdOccupant(): ?string + { + return $this->banIdOccupant; + } + + public function setBanIdOccupant(?string $banIdOccupant): self + { + $this->banIdOccupant = $banIdOccupant; + + return $this; + } + public function getIsCguAccepted(): ?bool { return $this->isCguAccepted; diff --git a/src/Manager/SignalementManager.php b/src/Manager/SignalementManager.php index e52343fc8..de9931e80 100644 --- a/src/Manager/SignalementManager.php +++ b/src/Manager/SignalementManager.php @@ -52,6 +52,8 @@ class SignalementManager extends AbstractManager { + public const float SCORE_IF_BAN_ID_ACCEPTED = 0.9; + public function __construct( protected ManagerRegistry $managerRegistry, private Security $security, @@ -193,6 +195,45 @@ public function updateAddressOccupantFromAddress(Signalement $signalement, Addre } } + public function updateAddressOccupantFromBanData(Signalement $signalement, bool $updateGeoloc = true): void + { + $addressResult = $this->addressService->getAddress($signalement->getAddressCompleteOccupant()); + if ($addressResult->getScore() > self::SCORE_IF_BAN_ID_ACCEPTED) { + $signalement->setBanIdOccupant($addressResult->getBanId()); + if ($updateGeoloc) { + $signalement + ->setInseeOccupant($addressResult->getInseeCode()) + ->setGeoloc([ + 'lat' => $addressResult->getLatitude(), + 'lng' => $addressResult->getLongitude(), + ]); + } + + return; + } elseif ($updateGeoloc && !empty($signalement->getCpOccupant()) && !empty($signalement->getVilleOccupant())) { + $inseeResult = $this->addressService->getAddress($signalement->getCpOccupant().' '.$signalement->getVilleOccupant()); + if (!empty($inseeResult->getCity())) { + $signalement + ->setBanIdOccupant(0) + ->setVilleOccupant($inseeResult->getCity()) + ->setInseeOccupant($inseeResult->getInseeCode()) + ->setGeoloc([ + 'lat' => $inseeResult->getLatitude(), + 'lng' => $inseeResult->getLongitude(), + ]); + + return; + } + } + + $signalement->setBanIdOccupant(0); + if ($updateGeoloc) { + $signalement + ->setInseeOccupant(null) + ->setGeoloc([]); + } + } + public function findAllPartners(Signalement $signalement): array { /** @var PartnerRepository $partnerRepository */ @@ -339,10 +380,6 @@ public function updateFromAdresseOccupantRequest( ->setCpOccupant($adresseOccupantRequest->getCodePostal()) ->setVilleOccupant($adresseOccupantRequest->getVille()) ->setInseeOccupant($adresseOccupantRequest->getInsee()) - ->setGeoloc([ - 'lat' => $adresseOccupantRequest->getGeolocLat(), - 'lng' => $adresseOccupantRequest->getGeolocLng(), - ]) ->setEtageOccupant($adresseOccupantRequest->getEtage()) ->setEscalierOccupant($adresseOccupantRequest->getEscalier()) @@ -350,21 +387,9 @@ public function updateFromAdresseOccupantRequest( ->setAdresseAutreOccupant($adresseOccupantRequest->getAutre()) ->setManualAddressOccupant('1' === $adresseOccupantRequest->getManual()); - if ('1' === $adresseOccupantRequest->getNeedResetInsee()) { - $resetAddress = $this->addressService->getAddress($adresseOccupantRequest->getCodePostal().' '.$adresseOccupantRequest->getVille()); - if (!empty($resetAddress->getCity())) { - $signalement->setVilleOccupant($resetAddress->getCity()); - } - if (!empty($resetAddress->getInseeCode())) { - $signalement->setInseeOccupant($resetAddress->getInseeCode()); - } - if (!empty($resetAddress->getLatitude())) { - $signalement->setGeoloc([ - 'lat' => $resetAddress->getLatitude(), - 'lng' => $resetAddress->getLongitude(), - ]); - } - } + $this->updateAddressOccupantFromBanData( + signalement: $signalement, + ); $this->save($signalement); diff --git a/src/Repository/SignalementRepository.php b/src/Repository/SignalementRepository.php index afb00efd8..236d2e829 100755 --- a/src/Repository/SignalementRepository.php +++ b/src/Repository/SignalementRepository.php @@ -1349,6 +1349,15 @@ public function findAllArchived( return new Paginator($queryBuilder->getQuery(), false); } + public function findNullBanId(): array + { + return $this->createQueryBuilder('s') + ->select('s.id') + ->where('s.banIdOccupant IS NULL') + ->getQuery() + ->getResult(); + } + public function findLogementSocialWithoutBailleurLink(): array { return $this->createQueryBuilder('s') diff --git a/src/Service/DataGouv/AddressService.php b/src/Service/DataGouv/AddressService.php index 458a27cc9..8e6938d75 100755 --- a/src/Service/DataGouv/AddressService.php +++ b/src/Service/DataGouv/AddressService.php @@ -10,6 +10,7 @@ class AddressService { private const string API_URL = 'https://api-adresse.data.gouv.fr/search/?q='; + private const string API_PARAM_LIMIT = '&limit=1'; public function __construct( private readonly HttpClientInterface $httpClient, @@ -20,7 +21,8 @@ public function __construct( public function searchAddress(string $query): ?array { try { - $response = $this->httpClient->request('GET', self::API_URL.urlencode($query)); + $url = self::API_URL.urlencode($query).self::API_PARAM_LIMIT; + $response = $this->httpClient->request('GET', $url); if (Response::HTTP_OK === $response->getStatusCode()) { return $response->toArray(); diff --git a/src/Service/DataGouv/Response/Address.php b/src/Service/DataGouv/Response/Address.php index f36241790..6d0d0ffb6 100644 --- a/src/Service/DataGouv/Response/Address.php +++ b/src/Service/DataGouv/Response/Address.php @@ -9,6 +9,8 @@ class Address private ?string $zipCode = null; private ?string $city = null; private ?string $inseeCode = null; + private ?float $score = 0; + private ?string $banId = null; private ?string $longitude = null; private ?string $latitude = null; @@ -20,7 +22,9 @@ public function __construct(?array $data = null) $this->street = $properties['name'] ?? null; $this->zipCode = $properties['postcode'] ?? null; $this->city = $properties['city'] ?? null; + $this->score = $properties['score'] ?? null; $this->inseeCode = $properties['citycode'] ?? null; + $this->banId = $properties['id'] ?? null; } if (null !== $data && !empty($data['features'][0]['geometry']['coordinates'])) { @@ -50,6 +54,16 @@ public function getInseeCode(): ?string return $this->inseeCode; } + public function getScore(): ?float + { + return $this->score; + } + + public function getBanId(): ?string + { + return $this->banId; + } + public function getLongitude(): ?string { return $this->longitude; diff --git a/src/Service/Signalement/SignalementBuilder.php b/src/Service/Signalement/SignalementBuilder.php index 24607acd1..a29bac1cc 100644 --- a/src/Service/Signalement/SignalementBuilder.php +++ b/src/Service/Signalement/SignalementBuilder.php @@ -19,6 +19,7 @@ use App\Factory\Signalement\SituationFoyerFactory; use App\Factory\Signalement\TypeCompositionLogementFactory; use App\Manager\DesordreCritereManager; +use App\Manager\SignalementManager; use App\Repository\BailleurRepository; use App\Repository\DesordreCritereRepository; use App\Repository\DesordrePrecisionRepository; @@ -54,6 +55,7 @@ public function __construct( private CriticiteCalculator $criticiteCalculator, private SignalementQualificationUpdater $signalementQualificationUpdater, private DesordreCompositionLogementLoader $desordreCompositionLogementLoader, + private SignalementManager $signalementManager, ) { } @@ -317,11 +319,6 @@ private function setAddressData(): void ->setIsLogementSocial($this->isLogementSocial()) ->setAdresseOccupant($this->signalementDraftRequest->getAdresseLogementAdresseDetailNumero()) ->setCpOccupant($this->signalementDraftRequest->getAdresseLogementAdresseDetailCodePostal()) - ->setInseeOccupant($this->signalementDraftRequest->getAdresseLogementAdresseDetailInsee()) - ->setGeoloc([ - 'lat' => $this->signalementDraftRequest->getAdresseLogementAdresseDetailGeolocLat(), - 'lng' => $this->signalementDraftRequest->getAdresseLogementAdresseDetailGeolocLng(), - ]) ->setVilleOccupant($this->signalementDraftRequest->getAdresseLogementAdresseDetailCommune()) ->setEtageOccupant($this->signalementDraftRequest->getAdresseLogementComplementAdresseEtage()) ->setEscalierOccupant($this->signalementDraftRequest->getAdresseLogementComplementAdresseEscalier()) @@ -330,6 +327,10 @@ private function setAddressData(): void ) ->setAdresseAutreOccupant($this->signalementDraftRequest->getAdresseLogementComplementAdresseAutre()) ->setManualAddressOccupant($this->signalementDraftRequest->getAdresseLogementAdresseDetailManual()); + + $this->signalementManager->updateAddressOccupantFromBanData( + signalement: $this->signalement, + ); } private function setOccupantDeclarantData(): void diff --git a/templates/back/signalement/view/edit-modals/edit-address.html.twig b/templates/back/signalement/view/edit-modals/edit-address.html.twig index 499964386..13686d0b1 100755 --- a/templates/back/signalement/view/edit-modals/edit-address.html.twig +++ b/templates/back/signalement/view/edit-modals/edit-address.html.twig @@ -51,8 +51,6 @@ - -