refactor(address): use directly BAN data
This commit is contained in:
parent
0b3a0d5840
commit
20307f1d21
11 changed files with 85 additions and 124 deletions
|
@ -1,2 +1,3 @@
|
||||||
= render Dsfr::ComboboxComponent.new form: @form, url: data_sources_data_source_adresse_path, selected: @champ.value, allows_custom_value: true, input_html_options: { name: :value, id: @champ.input_id, class: 'fr-select', describedby: @champ.describedby_id } do
|
= render Dsfr::ComboboxComponent.new form: @form, url: data_sources_data_source_adresse_path, selected: @champ.value, allows_custom_value: true, input_html_options: { name: :value, id: @champ.input_id, class: 'fr-select', describedby: @champ.describedby_id } do
|
||||||
= @form.hidden_field :external_id, data: { value_slot: 'value' }
|
= @form.hidden_field :external_id, data: { value_slot: 'value' }
|
||||||
|
= @form.hidden_field :feature, data: { value_slot: 'data' }
|
||||||
|
|
|
@ -26,7 +26,7 @@ class DataSources::AdresseController < ApplicationController
|
||||||
{
|
{
|
||||||
label: _1[:properties][:label],
|
label: _1[:properties][:label],
|
||||||
value: _1[:properties][:label],
|
value: _1[:properties][:label],
|
||||||
data: _1[:properties]
|
data: _1
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -401,7 +401,7 @@ module Instructeurs
|
||||||
def champs_private_params
|
def champs_private_params
|
||||||
champs_params = params.require(:dossier).permit(champs_private_attributes: [
|
champs_params = params.require(:dossier).permit(champs_private_attributes: [
|
||||||
:id, :value, :primary_value, :secondary_value, :piece_justificative_file, :value_other, :external_id, :numero_allocataire, :code_postal, :code_departement, value: [],
|
:id, :value, :primary_value, :secondary_value, :piece_justificative_file, :value_other, :external_id, :numero_allocataire, :code_postal, :code_departement, value: [],
|
||||||
champs_attributes: [:id, :_destroy, :value, :primary_value, :secondary_value, :piece_justificative_file, :value_other, :external_id, :numero_allocataire, :code_postal, :code_departement, value: []]
|
champs_attributes: [:id, :_destroy, :value, :primary_value, :secondary_value, :piece_justificative_file, :value_other, :external_id, :numero_allocataire, :code_postal, :code_departement, :feature, value: []]
|
||||||
])
|
])
|
||||||
champs_params[:champs_private_all_attributes] = champs_params.delete(:champs_private_attributes) || {}
|
champs_params[:champs_private_all_attributes] = champs_params.delete(:champs_private_attributes) || {}
|
||||||
champs_params
|
champs_params
|
||||||
|
|
|
@ -488,6 +488,7 @@ module Users
|
||||||
:code_departement,
|
:code_departement,
|
||||||
:accreditation_number,
|
:accreditation_number,
|
||||||
:accreditation_birthdate,
|
:accreditation_birthdate,
|
||||||
|
:feature,
|
||||||
value: []
|
value: []
|
||||||
])
|
])
|
||||||
champs_params[:champs_public_all_attributes] = champs_params.delete(:champs_public_attributes) || {}
|
champs_params[:champs_public_all_attributes] = champs_params.delete(:champs_public_attributes) || {}
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
require 'json_schemer'
|
|
||||||
|
|
||||||
class APIAddress::AddressAdapter
|
|
||||||
class InvalidSchemaError < ::StandardError
|
|
||||||
def initialize(errors)
|
|
||||||
super(errors.map(&:to_json).join("\n"))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(search_term)
|
|
||||||
@search_term = search_term
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_params
|
|
||||||
result = Geocoder.search(@search_term, limit: 1).first
|
|
||||||
if result.present? && result.national_address == @search_term
|
|
||||||
feature = result.data['features'].first
|
|
||||||
if schemer.valid?(feature)
|
|
||||||
{
|
|
||||||
label: result.national_address,
|
|
||||||
type: result.result_type,
|
|
||||||
street_address: result.street_address,
|
|
||||||
street_number: result.street_number,
|
|
||||||
street_name: result.street_name,
|
|
||||||
postal_code: result.postal_code.presence || "",
|
|
||||||
city_name: result.city_name,
|
|
||||||
city_code: result.city_code,
|
|
||||||
department_name: result.department_name,
|
|
||||||
department_code: result.department_code,
|
|
||||||
region_name: result.region_name,
|
|
||||||
region_code: result.region_code,
|
|
||||||
geometry: result.geometry
|
|
||||||
}
|
|
||||||
else
|
|
||||||
errors = schemer.validate(feature).to_a
|
|
||||||
raise InvalidSchemaError.new(errors)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def schemer
|
|
||||||
@schemer ||= JSONSchemer.schema(Rails.root.join('app/schemas/adresse-ban.json'))
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -3,6 +3,18 @@ class Champs::AddressChamp < Champs::TextChamp
|
||||||
data.present?
|
data.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def feature
|
||||||
|
''
|
||||||
|
end
|
||||||
|
|
||||||
|
def feature=(value)
|
||||||
|
return if value.blank?
|
||||||
|
feature = JSON.parse(value)
|
||||||
|
self.data = APIGeoService.parse_ban_address(feature)
|
||||||
|
rescue JSON::ParserError
|
||||||
|
self.data = nil
|
||||||
|
end
|
||||||
|
|
||||||
def address
|
def address
|
||||||
full_address? ? data : nil
|
full_address? ? data : nil
|
||||||
end
|
end
|
||||||
|
@ -13,7 +25,7 @@ class Champs::AddressChamp < Champs::TextChamp
|
||||||
|
|
||||||
def search_terms
|
def search_terms
|
||||||
if full_address?
|
if full_address?
|
||||||
[data['label'], data['departement'], data['region'], data['city']]
|
[data['label'], data['department_name'], data['region_name'], data['city_name']]
|
||||||
else
|
else
|
||||||
[address_label]
|
[address_label]
|
||||||
end
|
end
|
||||||
|
@ -35,45 +47,48 @@ class Champs::AddressChamp < Champs::TextChamp
|
||||||
address_label
|
address_label
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_external_data?
|
def code_departement
|
||||||
true
|
if full_address?
|
||||||
|
address.fetch('department_code')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_external_data
|
def code_region
|
||||||
APIAddress::AddressAdapter.new(external_id).to_params
|
if full_address?
|
||||||
|
address.fetch('region_code')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def departement_name
|
def departement_name
|
||||||
APIGeoService.departement_name(address.fetch('department_code'))
|
APIGeoService.departement_name(code_departement)
|
||||||
end
|
end
|
||||||
|
|
||||||
def departement_code_and_name
|
def departement_code_and_name
|
||||||
if full_address?
|
if full_address?
|
||||||
"#{address.fetch('department_code')} – #{departement_name}"
|
"#{code_departement} – #{departement_name}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def departement
|
def departement
|
||||||
if full_address?
|
if full_address?
|
||||||
{ code: address.fetch('department_code'), name: departement_name }
|
{ code: code_departement, name: departement_name }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def commune_name
|
def commune_name
|
||||||
if full_address?
|
if full_address?
|
||||||
"#{APIGeoService.commune_name(address.fetch('department_code'), address['city_code'])} (#{address['postal_code']})"
|
"#{APIGeoService.commune_name(code_departement, address['city_code'])} (#{address['postal_code']})"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def commune
|
def commune
|
||||||
if full_address?
|
if full_address?
|
||||||
department_code = address.fetch('department_code')
|
|
||||||
city_code = address.fetch('city_code')
|
city_code = address.fetch('city_code')
|
||||||
city_name = address.fetch('city_name')
|
city_name = address.fetch('city_name')
|
||||||
postal_code = address.fetch('postal_code')
|
postal_code = address.fetch('postal_code')
|
||||||
|
|
||||||
commune_name = APIGeoService.commune_name(department_code, city_code)
|
commune_name = APIGeoService.commune_name(code_departement, city_code)
|
||||||
commune_code = APIGeoService.commune_code(department_code, city_name)
|
commune_code = APIGeoService.commune_code(code_departement, city_name)
|
||||||
|
|
||||||
if commune_name.present?
|
if commune_name.present?
|
||||||
{
|
{
|
||||||
|
|
|
@ -42,7 +42,7 @@ class Champs::DepartementChamp < Champs::TextChamp
|
||||||
end
|
end
|
||||||
|
|
||||||
def code_region
|
def code_region
|
||||||
APIGeoService.region_code_by_departement(name)
|
APIGeoService.region_code_by_departement(code)
|
||||||
end
|
end
|
||||||
|
|
||||||
def value=(code)
|
def value=(code)
|
||||||
|
|
|
@ -44,7 +44,7 @@ class Champs::EpciChamp < Champs::TextChamp
|
||||||
end
|
end
|
||||||
|
|
||||||
def code_region
|
def code_region
|
||||||
APIGeoService.region_code_by_departement(departement_name)
|
APIGeoService.region_code_by_departement(code_departement)
|
||||||
end
|
end
|
||||||
|
|
||||||
def selected
|
def selected
|
||||||
|
|
|
@ -34,9 +34,9 @@ class APIGeoService
|
||||||
regions.find { _1[:name] == name }&.dig(:code)
|
regions.find { _1[:name] == name }&.dig(:code)
|
||||||
end
|
end
|
||||||
|
|
||||||
def region_code_by_departement(name)
|
def region_code_by_departement(code)
|
||||||
return if name.nil?
|
return if code.nil?
|
||||||
departements.find { _1[:name] == name }&.dig(:region_code)
|
departements.find { _1[:code] == code }&.dig(:region_code)
|
||||||
end
|
end
|
||||||
|
|
||||||
def departements
|
def departements
|
||||||
|
@ -81,6 +81,42 @@ class APIGeoService
|
||||||
communes(departement_code).find { _1[:name] == name }&.dig(:code)
|
communes(departement_code).find { _1[:name] == name }&.dig(:code)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def parse_ban_address(feature)
|
||||||
|
return unless ban_address_schema.valid?(feature)
|
||||||
|
|
||||||
|
properties = feature.fetch('properties')
|
||||||
|
city_code = properties.fetch('citycode')
|
||||||
|
|
||||||
|
territory = if properties['context'].present?
|
||||||
|
department_code = properties.fetch('context').split(',').first
|
||||||
|
region_code = region_code_by_departement(department_code)
|
||||||
|
|
||||||
|
{
|
||||||
|
department_name: departement_name(department_code),
|
||||||
|
department_code:,
|
||||||
|
region_name: region_name(region_code),
|
||||||
|
region_code:,
|
||||||
|
city_name: commune_name(department_code, city_code),
|
||||||
|
city_code:
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
city_name: properties['city'],
|
||||||
|
city_code:
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
{
|
||||||
|
label: properties.fetch('label'),
|
||||||
|
type: properties.fetch('type'),
|
||||||
|
street_address: properties.fetch('name'),
|
||||||
|
postal_code: properties.fetch('postcode'),
|
||||||
|
street_number: properties['housenumber'],
|
||||||
|
street_name: properties['street'],
|
||||||
|
geometry: feature['geometry']
|
||||||
|
}.merge(territory)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def communes_by_postal_code_map
|
def communes_by_postal_code_map
|
||||||
|
@ -114,5 +150,11 @@ class APIGeoService
|
||||||
'EN' => { 'XK' => 'Kosovo' }
|
'EN' => { 'XK' => 'Kosovo' }
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ban_address_schema
|
||||||
|
JSONSchemer.schema(Rails.root.join('app/schemas/adresse-ban.json'))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
- if champ.to_s.present?
|
%p= champ.to_s
|
||||||
= format_text_value(champ.to_s)
|
|
||||||
- if champ.full_address?
|
- if champ.full_address?
|
||||||
Code INSEE :
|
%p.fr-text--sm
|
||||||
= champ.commune&.fetch(:code)
|
Code INSEE :
|
||||||
|
= champ.commune&.fetch(:code)
|
||||||
|
%p.fr-text--sm
|
||||||
|
Departement :
|
||||||
|
= champ.departement_code_and_name
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
describe APIAddress::AddressAdapter do
|
|
||||||
let(:search_term) { 'Paris' }
|
|
||||||
let(:adapter) { described_class.new(search_term) }
|
|
||||||
let(:status) { 200 }
|
|
||||||
subject { adapter.to_params }
|
|
||||||
|
|
||||||
before do
|
|
||||||
Geocoder.configure(lookup: :ban_data_gouv_fr, use_https: true)
|
|
||||||
stub_request(:get, /https:\/\/api-adresse.data.gouv.fr\/search/)
|
|
||||||
.to_return(body: body, status: status)
|
|
||||||
end
|
|
||||||
|
|
||||||
after do
|
|
||||||
Geocoder.configure(lookup: :test)
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when responds with valid schema" do
|
|
||||||
let(:body) { File.read('spec/fixtures/files/api_address/address.json') }
|
|
||||||
|
|
||||||
it '#to_params returns a valid' do
|
|
||||||
expect(subject).to be_an_instance_of(Hash)
|
|
||||||
expect(subject[:city_name]).to eq(search_term)
|
|
||||||
expect(subject[:city_code]).to eq('75056')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when responds with an address which is not a direct match to search term" do
|
|
||||||
let(:body) { File.read('spec/fixtures/files/api_address/address.json') }
|
|
||||||
let(:search_term) { 'Lyon' }
|
|
||||||
|
|
||||||
it '#to_params ignores the response' do
|
|
||||||
expect(subject).to be_nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when responds with invalid schema" do
|
|
||||||
let(:body) { File.read('spec/fixtures/files/api_address/address_invalid.json') }
|
|
||||||
|
|
||||||
it '#to_params raise exception' do
|
|
||||||
expect { subject }.to raise_exception(APIAddress::AddressAdapter::InvalidSchemaError)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when responds without postcode" do
|
|
||||||
let(:body) {
|
|
||||||
json = JSON.parse(File.read('spec/fixtures/files/api_address/address.json'))
|
|
||||||
json["features"][0]["properties"].delete("postcode")
|
|
||||||
json.to_json
|
|
||||||
}
|
|
||||||
|
|
||||||
it "#to_params default to an empty postcode" do
|
|
||||||
expect(subject[:postal_code]).to eq("")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in a new issue