Add type champs "Address" plug at the BAN

This commit is contained in:
Xavier J 2016-06-09 12:08:18 +02:00
parent 35a07aec87
commit 23ab25396f
18 changed files with 2804 additions and 13 deletions

View file

@ -29,8 +29,7 @@
//= require franceconnect //= require franceconnect
//= require bootstrap-wysihtml5 //= require bootstrap-wysihtml5
//= require bootstrap-wysihtml5/locales/fr-FR //= require bootstrap-wysihtml5/locales/fr-FR
//= require typeahead.bundle
$(document).on('page:load', scroll_to); $(document).on('page:load', scroll_to);
$(document).ready(scroll_to); $(document).ready(scroll_to);

View file

@ -13,6 +13,8 @@ function action_type_de_champs() {
toggleErrorClass(this, validatePhone(val)); toggleErrorClass(this, validatePhone(val));
}); });
address_type_init();
} }
function toggleErrorClass(node, boolean) { function toggleErrorClass(node, boolean) {
@ -34,4 +36,28 @@ function validateEmail(email) {
function validateInput(input, regex) { function validateInput(input, regex) {
return regex.test(input); return regex.test(input);
}
function address_type_init() {
display = 'label';
var bloodhound = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace(display),
queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: {
url: '/ban/search?request=%QUERY',
wildcard: '%QUERY'
}
});
bloodhound.initialize();
$("input[type='address']").typeahead({
minLength: 1
}, {
display: display,
source: bloodhound,
limit: 5
});
} }

View file

@ -21,6 +21,15 @@
} }
} }
.type_champ-address {
@extend .col-md-6;
@extend .col-lg-6;
input[type='address'] {
width: 100%;
}
}
.type_champ-email { .type_champ-email {
@extend .col-md-4; @extend .col-md-4;
@extend .col-lg-4; @extend .col-lg-4;

View file

@ -0,0 +1,35 @@
.tt-menu {
width: 555px;
padding: 8px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
box-shadow: 0 5px 10px rgba(0, 0, 0, .2);
}
.tt-suggestion {
padding: 3px 20px;
font-size: 18px;
line-height: 24px;
}
.twitter-typeahead {
width: 555px;
}
.tt-suggestion:hover {
cursor: pointer;
color: #fff;
background-color: #0097cf;
}
.tt-suggestion.tt-cursor {
color: #fff;
background-color: #0097cf;
}

View file

@ -0,0 +1,9 @@
class Ban::SearchController < ApplicationController
def get
request = params[:request]
render json: Carto::Bano::AddressRetriever.new(request).list.inject([]) {
|acc, value| acc.push({label: value})
}.to_json
end
end

View file

@ -7,4 +7,9 @@ class Champ < ActiveRecord::Base
def mandatory? def mandatory?
mandatory mandatory
end end
def data_provide
return 'datepicker' if type_champ == 'datetime'
return 'typeahead' if type_champ == 'address'
end
end end

View file

@ -1,12 +1,14 @@
class TypeDeChamp < ActiveRecord::Base class TypeDeChamp < ActiveRecord::Base
enum type_champs: {text: 'text', enum type_champs: {
email: 'email', text: 'text',
phone: 'phone', textarea: 'textarea',
civilite: 'civilite', datetime: 'datetime',
textarea: 'textarea', number: 'number',
datetime: 'datetime', checkbox: 'checkbox',
number: 'number', civilite: 'civilite',
checkbox: 'checkbox' email: 'email',
phone: 'phone',
address: 'address'
} }
belongs_to :procedure belongs_to :procedure

View file

@ -34,5 +34,5 @@
id: "champs_#{champ.id}", id: "champs_#{champ.id}",
value: champ.value, value: champ.value,
type: champ.type_champ, type: champ.type_champ,
'data-provide' => ('datepicker' if champ.type_champ == 'datetime'), 'data-provide' => champ.data_provide,
'data-date-format' => ('dd/mm/yyyy' if champ.type_champ == 'datetime')} 'data-date-format' => ('dd/mm/yyyy' if champ.type_champ == 'datetime')}

View file

@ -108,6 +108,10 @@ Rails.application.routes.draw do
resources :gestionnaires, only: [:index, :create, :destroy] resources :gestionnaires, only: [:index, :create, :destroy]
end end
namespace :ban do
get 'search' => 'search#get'
end
get 'backoffice' => 'backoffice#index' get 'backoffice' => 'backoffice#index'
namespace :backoffice do namespace :backoffice do

View file

@ -0,0 +1,36 @@
module Carto
module Bano
# input : address
# output : Array List label address
class AddressRetriever
def initialize(address)
@address = address
end
def list
@list ||= convert_driver_result_to_full_address
end
private
def driver
@driver ||= Carto::Bano::Driver.new @address, 5
end
def convert_driver_result_to_full_address
result = JSON.parse(driver.call)
if result['features'].size == 0
Rails.logger.error "unable to find location for address #{@address}"
return []
end
result['features'].inject([]) do |acc, feature|
acc.push feature['properties']['label']
end
rescue TypeError, JSON::ParserError
[]
end
end
end
end

View file

@ -3,12 +3,15 @@ module Carto
# input : string (address) # input : string (address)
# output : json # output : json
class Driver class Driver
def initialize(address) def initialize(address, limit = 1)
@address = address @address = address
@limit = limit
end end
def call def call
RestClient.get api_url, params: { q: @address, limit: 1 } RestClient.get api_url, params: { q: @address, limit: @limit }
rescue RestClient::ServiceUnavailable
nil
end end
def api_url def api_url

View file

@ -0,0 +1,16 @@
require 'spec_helper'
describe Ban::SearchController, type: :controller do
describe '#GET' do
let (:request) { '' }
before do
stub_request(:get, "http://api-adresse.data.gouv.fr/search?limit=5&q=").
to_return(:status => 200, :body => 'Missing query', :headers => {})
get :get, request: request
end
it { expect(response.status).to eq 200 }
end
end

4
spec/factories/champ.rb Normal file
View file

@ -0,0 +1,4 @@
FactoryGirl.define do
factory :champ do
end
end

View file

@ -0,0 +1,44 @@
require 'spec_helper'
describe Carto::Bano::AddressRetriever do
describe '#list' do
let(:request) { 'Paris' }
let(:response) { File.open('spec/support/files/ban_address_search.json') }
let(:status) { 200 }
subject { described_class.new(request).list }
before do
stub_request(:get, "http://api-adresse.data.gouv.fr/search?&q=#{request}&limit=5")
.to_return(status: status, body: response, headers: {})
end
context 'when address return a list of address' do
it { expect(subject.size).to eq 5 }
it { is_expected.to be_an_instance_of Array }
end
context 'when address return an empty list' do
let(:response) { File.open('spec/support/files/ban_address_search_no_result.json') }
it { expect(subject.size).to eq 0 }
it { is_expected.to be_an_instance_of Array }
end
context 'when BAN is unavailable' do
let(:status) { 503 }
let(:response) { '' }
it { expect(subject.size).to eq 0 }
it { is_expected.to be_an_instance_of Array }
end
context 'when request is empty' do
let(:response) { 'Missing query' }
let(:request) { '' }
it { expect(subject.size).to eq 0 }
it { is_expected.to be_an_instance_of Array }
end
end
end

View file

@ -15,4 +15,26 @@ describe Champ do
it { is_expected.to delegate_method(:type_champ).to(:type_de_champ) } it { is_expected.to delegate_method(:type_champ).to(:type_de_champ) }
it { is_expected.to delegate_method(:order_place).to(:type_de_champ) } it { is_expected.to delegate_method(:order_place).to(:type_de_champ) }
end end
describe 'data_provide' do
let(:champ) { create :champ }
subject { champ.data_provide }
context 'when type_champ is datetime' do
before do
champ.type_de_champ = create :type_de_champ, type_champ: 'datetime'
end
it { is_expected.to eq 'datepicker' }
end
context 'when type_champ is address' do
before do
champ.type_de_champ = create :type_de_champ, type_champ: 'address'
end
it { is_expected.to eq 'typeahead' }
end
end
end end

View file

@ -0,0 +1,117 @@
{
"limit": 5,
"attribution": "BAN",
"version": "draft",
"licence": "ODbL 1.0",
"query": "Paris",
"type": "FeatureCollection",
"features": [
{
"geometry": {
"type": "Point",
"coordinates": [
2.3469,
48.8589
]
},
"properties": {
"adm_weight": "6",
"citycode": "75056",
"name": "Paris",
"city": "Paris",
"postcode": "75000",
"context": "75, \u00cele-de-France",
"score": 1.0,
"label": "Paris",
"id": "75056",
"type": "city",
"population": "2244"
},
"type": "Feature"
},
{
"geometry": {
"type": "Point",
"coordinates": [
4.366801,
44.425528
]
},
"properties": {
"citycode": "07330",
"postcode": "07150",
"name": "Paris",
"id": "07330_B095_bd3524",
"context": "07, Ard\u00e8che, Rh\u00f4ne-Alpes",
"score": 0.8291454545454544,
"label": "Paris 07150 Vallon-Pont-d'Arc",
"city": "Vallon-Pont-d'Arc",
"type": "locality"
},
"type": "Feature"
},
{
"geometry": {
"type": "Point",
"coordinates": [
3.564293,
45.766413
]
},
"properties": {
"citycode": "63125",
"postcode": "63120",
"name": "Paris",
"city": "Courpi\u00e8re",
"context": "63, Puy-de-D\u00f4me, Auvergne",
"score": 0.8255363636363636,
"label": "Paris 63120 Courpi\u00e8re",
"id": "63125_B221_03549b",
"type": "locality"
},
"type": "Feature"
},
{
"geometry": {
"type": "Point",
"coordinates": [
1.550208,
44.673592
]
},
"properties": {
"citycode": "46138",
"postcode": "46240",
"name": "PARIS (Vaillac)",
"city": "C\u0153ur de Causse",
"context": "46, Lot, Midi-Pyr\u00e9n\u00e9es",
"score": 0.824090909090909,
"label": "PARIS (Vaillac) 46240 C\u0153ur de Causse",
"id": "46138_XXXX_6ee4ec",
"type": "street"
},
"type": "Feature"
},
{
"geometry": {
"type": "Point",
"coordinates": [
-0.526884,
43.762253
]
},
"properties": {
"citycode": "40282",
"postcode": "40500",
"name": "Paris",
"city": "Saint-Sever",
"context": "40, Landes, Aquitaine",
"score": 0.8236181818181818,
"label": "Paris 40500 Saint-Sever",
"id": "40282_B237_2364e3",
"type": "locality"
},
"type": "Feature"
}
]
}

View file

@ -0,0 +1,9 @@
{
"limit": 5,
"attribution": "BAN",
"version": "draft",
"licence": "ODbL 1.0",
"query": "Paris",
"type": "FeatureCollection",
"features": []
}

File diff suppressed because it is too large Load diff