0002-base_adresse-add-API-G-o-endpoints-11497.patch
passerelle/apps/base_adresse/migrations/0015_auto_20191203_1624.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.18 on 2019-12-03 15:24 |
|
3 |
from __future__ import unicode_literals |
|
4 | ||
5 |
from django.db import migrations, models |
|
6 |
import django.db.models.deletion |
|
7 |
import passerelle.apps.base_adresse.models |
|
8 | ||
9 | ||
10 |
class Migration(migrations.Migration): |
|
11 | ||
12 |
dependencies = [ |
|
13 |
('base_adresse', '0014_auto_20190207_0456'), |
|
14 |
] |
|
15 | ||
16 |
operations = [ |
|
17 |
migrations.CreateModel( |
|
18 |
name='CityModel', |
|
19 |
fields=[ |
|
20 |
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|
21 |
('name', models.CharField(max_length=100, verbose_name='City name')), |
|
22 |
('unaccent_name', models.CharField(max_length=150, null=True, verbose_name='City name ascii char')), |
|
23 |
('code', models.CharField(max_length=5, verbose_name='City code')), |
|
24 |
('zipcode', models.CharField(max_length=5, verbose_name='Postal code')), |
|
25 |
('population', models.PositiveIntegerField(verbose_name='Population')), |
|
26 |
], |
|
27 |
options={ |
|
28 |
'ordering': ['-population', 'zipcode', 'unaccent_name', 'name'], |
|
29 |
}, |
|
30 |
bases=(passerelle.apps.base_adresse.models.UnaccentNameMixin, models.Model), |
|
31 |
), |
|
32 |
migrations.CreateModel( |
|
33 |
name='DepartmentModel', |
|
34 |
fields=[ |
|
35 |
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|
36 |
('name', models.CharField(max_length=100, verbose_name='Department name')), |
|
37 |
('unaccent_name', models.CharField(max_length=150, null=True, verbose_name='Department name ascii char')), |
|
38 |
('code', models.CharField(max_length=3, unique=True, verbose_name='Department code')), |
|
39 |
], |
|
40 |
options={ |
|
41 |
'ordering': ['code'], |
|
42 |
}, |
|
43 |
bases=(passerelle.apps.base_adresse.models.UnaccentNameMixin, models.Model), |
|
44 |
), |
|
45 |
migrations.CreateModel( |
|
46 |
name='RegionModel', |
|
47 |
fields=[ |
|
48 |
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|
49 |
('name', models.CharField(max_length=100, verbose_name='Region name')), |
|
50 |
('unaccent_name', models.CharField(max_length=150, null=True, verbose_name='Region name ascii char')), |
|
51 |
('code', models.PositiveSmallIntegerField(unique=True, verbose_name='Region code')), |
|
52 |
], |
|
53 |
options={ |
|
54 |
'ordering': ['code'], |
|
55 |
}, |
|
56 |
bases=(passerelle.apps.base_adresse.models.UnaccentNameMixin, models.Model), |
|
57 |
), |
|
58 |
migrations.AddField( |
|
59 |
model_name='baseadresse', |
|
60 |
name='api_geo_url', |
|
61 |
field=models.CharField(default=b'https://geo.api.gouv.fr/', help_text='Base Adresse API Geo URL', max_length=128, verbose_name='API Geo URL'), |
|
62 |
), |
|
63 |
migrations.AddField( |
|
64 |
model_name='baseadresse', |
|
65 |
name='department', |
|
66 |
field=models.CharField(blank=True, max_length=3, null=True, verbose_name='Department code to get cities from'), |
|
67 |
), |
|
68 |
migrations.AddField( |
|
69 |
model_name='baseadresse', |
|
70 |
name='region', |
|
71 |
field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Region code to get cities and departments from'), |
|
72 |
), |
|
73 |
migrations.AlterField( |
|
74 |
model_name='baseadresse', |
|
75 |
name='zipcode', |
|
76 |
field=models.CharField(blank=True, max_length=600, verbose_name='Postal codes or department number to get streets, separated with commas'), |
|
77 |
), |
|
78 |
migrations.AddField( |
|
79 |
model_name='departmentmodel', |
|
80 |
name='region', |
|
81 |
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base_adresse.RegionModel'), |
|
82 |
), |
|
83 |
migrations.AddField( |
|
84 |
model_name='citymodel', |
|
85 |
name='department', |
|
86 |
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='base_adresse.DepartmentModel'), |
|
87 |
), |
|
88 |
migrations.AddField( |
|
89 |
model_name='citymodel', |
|
90 |
name='region', |
|
91 |
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='base_adresse.RegionModel'), |
|
92 |
), |
|
93 |
migrations.AlterUniqueTogether( |
|
94 |
name='citymodel', |
|
95 |
unique_together=set([('code', 'zipcode')]), |
|
96 |
), |
|
97 |
] |
passerelle/apps/base_adresse/models.py | ||
---|---|---|
23 | 23 |
verbose_name=_('Service URL'), |
24 | 24 |
help_text=_('Base Adresse Web Service URL')) |
25 | 25 | |
26 |
api_geo_url = models.CharField( |
|
27 |
max_length=128, blank=False, |
|
28 |
default='https://geo.api.gouv.fr/', |
|
29 |
verbose_name=_('API Geo URL'), |
|
30 |
help_text=_('Base Adresse API Geo URL')) |
|
31 | ||
26 | 32 |
category = _('Geographic information system') |
27 | 33 | |
28 |
api_description = _("The API is a partial view of OpenStreetMap's Nominatim " |
|
29 |
"own API; it currently doesn't support all parameters and " |
|
30 |
"is limited to the JSON format.") |
|
34 |
api_description = _("The geocoding endpoints are a partial view of OpenStreetMap's " |
|
35 |
"Nominatim own API; it currently doesn't support all parameters and " |
|
36 |
"is limited to the JSON format. The cities, departments and regions " |
|
37 |
"endpoints source data from French API Geo.") |
|
31 | 38 | |
32 | 39 |
zipcode = models.CharField( |
33 | 40 |
max_length=600, |
34 | 41 |
blank=True, |
35 |
verbose_name=_('Postal codes or county number to get streets, separated with commas')) |
|
42 |
verbose_name=_('Postal codes or department number to get streets, separated with commas')) |
|
43 | ||
44 |
department = models.CharField( |
|
45 |
blank=True, |
|
46 |
null=True, |
|
47 |
max_length=3, |
|
48 |
verbose_name=_('Department code to get cities from')) |
|
49 | ||
50 |
region = models.PositiveSmallIntegerField( |
|
51 |
blank=True, |
|
52 |
null=True, |
|
53 |
verbose_name=_('Region code to get cities and departments from')) |
|
36 | 54 | |
37 | 55 |
class Meta: |
38 | 56 |
verbose_name = _('Base Adresse Web Service') |
... | ... | |
114 | 132 |
result['address']['road'] = value |
115 | 133 |
return result |
116 | 134 | |
135 |
@staticmethod |
|
136 |
def retrieve_objects(model, id, q=None, field_lookups=()): |
|
137 |
if id is not None: |
|
138 |
try: |
|
139 |
id = int(id) |
|
140 |
except ValueError: |
|
141 |
return model.objects.none() |
|
142 |
return model.objects.filter(id=id) |
|
143 |
qs = model.objects.all() |
|
144 |
if not q: |
|
145 |
return qs |
|
146 |
unaccented_q = unicodedata.normalize('NFKD', q).encode('ascii', 'ignore').lower() |
|
147 |
queries = [Q(**{field: unaccented_q}) for field in field_lookups] |
|
148 |
return qs.filter(six.moves.reduce(Q.__or__, queries)) |
|
149 | ||
117 | 150 |
@endpoint(description=_('Streets from zipcode'), |
118 | 151 |
parameters={ |
119 | 152 |
'zipcode': {'description': _('Zipcode'), 'example_value': '00000'} |
120 | 153 |
}) |
121 | 154 |
def streets(self, request, zipcode=None, q=None, id=None, distinct=True, page_limit=None): |
122 | 155 |
result = [] |
123 |
if id is not None: |
|
124 |
try: |
|
125 |
id = int(id) |
|
126 |
except ValueError: |
|
127 |
return {'data': []} |
|
128 |
streets = StreetModel.objects.filter(id=id) |
|
129 |
else: |
|
130 |
streets = StreetModel.objects.all() |
|
131 |
if q: |
|
132 |
unaccented_q = unicodedata.normalize('NFKD', q).encode('ascii', 'ignore').lower() |
|
133 |
streets = streets.filter(unaccent_name__icontains=unaccented_q) |
|
156 |
streets = self.retrieve_objects(StreetModel, id, q, |
|
157 |
field_lookups=('unaccent_name__icontains',)) |
|
134 | 158 | |
135 | 159 |
if zipcode: |
136 | 160 |
streets = streets.filter(zipcode__startswith=zipcode) |
... | ... | |
152 | 176 | |
153 | 177 |
return {'data': result} |
154 | 178 | |
179 |
@endpoint(description=_('Cities list'), |
|
180 |
parameters={ |
|
181 |
'q': {'description': _("City name or zipcode"), 'example_value': 'Paris'}, |
|
182 |
'code': {'description': _('City code'), 'example_value': '75056'}, |
|
183 |
'region_code': {'description': _('Region code'), 'example_value': '11'}, |
|
184 |
'department_code': {'description': _('Department code'), 'example_value': '75'}, |
|
185 |
}) |
|
186 |
def cities(self, request, id=None, q=None, code=None, region_code=None, |
|
187 |
department_code=None): |
|
188 |
cities = self.retrieve_objects(CityModel, id, q, |
|
189 |
field_lookups=('unaccent_name__istartswith', |
|
190 |
'zipcode__istartswith')) |
|
191 | ||
192 |
if code: |
|
193 |
cities = cities.filter(code=code) |
|
194 |
if region_code or self.region: |
|
195 |
cities = cities.filter(region__code=region_code or self.region) |
|
196 |
if department_code or self.department: |
|
197 |
cities = cities.filter(department__code=department_code or self.department) |
|
198 | ||
199 |
cities = cities.select_related('department', 'region') |
|
200 |
return {'data': [city.to_json() for city in cities]} |
|
201 | ||
202 |
@endpoint(description=_('Departments list'), |
|
203 |
parameters={ |
|
204 |
'q': {'description': _('Department name or code'), 'example_value': 'Nord'}, |
|
205 |
'region_code': {'description': _('Region code'), 'example_value': '32'}, |
|
206 |
}) |
|
207 |
def departments(self, request, id=None, q=None, region_code=None): |
|
208 |
departments = self.retrieve_objects(DepartmentModel, id, q, |
|
209 |
field_lookups=('unaccent_name__istartswith', |
|
210 |
'code__istartswith')) |
|
211 | ||
212 |
if region_code or self.region: |
|
213 |
departments = departments.filter(region__code=region_code or self.region) |
|
214 | ||
215 |
return {'data': [department.to_json() for department in departments]} |
|
216 | ||
217 |
@endpoint(description=_('Regions list'), |
|
218 |
parameters={ |
|
219 |
'q': {'description': _('Region name or code'), 'example_value': 'Hauts-de-France'}, |
|
220 |
}) |
|
221 |
def regions(self, request, id=None, q=None, code=None): |
|
222 |
regions = self.retrieve_objects(RegionModel, id, q, |
|
223 |
field_lookups=('unaccent_name__istartswith', |
|
224 |
'code__istartswith')) |
|
225 | ||
226 |
return {'data': [region.to_json() for region in regions]} |
|
227 | ||
155 | 228 |
def check_status(self): |
156 | 229 |
if self.service_url == 'https://api-adresse.data.gouv.fr/': |
157 | 230 |
result = self.search(None, '169 rue du chateau, paris') |
... | ... | |
212 | 285 | |
213 | 286 |
self.get_streets_queryset().filter(last_update__lt=start_update).delete() |
214 | 287 | |
288 |
def update_api_geo_data(self): |
|
289 |
response = self.requests.get(os.path.join(self.api_geo_url, 'regions/')) |
|
290 |
for data in response.json(): |
|
291 |
defaults = { |
|
292 |
'name': data['nom'], |
|
293 |
} |
|
294 |
RegionModel.objects.update_or_create(code=data['code'], defaults=defaults) |
|
295 | ||
296 |
response = self.requests.get(os.path.join(self.api_geo_url, 'departements/')) |
|
297 |
for data in response.json(): |
|
298 |
defaults = { |
|
299 |
'name': data['nom'], |
|
300 |
'region': RegionModel.objects.get(code=data['codeRegion']), |
|
301 |
} |
|
302 |
DepartmentModel.objects.update_or_create(code=data['code'], defaults=defaults) |
|
303 | ||
304 |
response = self.requests.get(os.path.join(self.api_geo_url, 'communes/')) |
|
305 |
for data in response.json(): |
|
306 |
for zipcode in data['codesPostaux']: |
|
307 |
defaults = { |
|
308 |
'name': data['nom'], |
|
309 |
'population': data.get('population', 0), |
|
310 |
} |
|
311 |
if data.get('codeDepartement'): |
|
312 |
defaults['department'] = DepartmentModel.objects.get(code=data['codeDepartement']) |
|
313 |
if data.get('codeRegion'): |
|
314 |
defaults['region'] = RegionModel.objects.get(code=data['codeRegion']) |
|
315 |
CityModel.objects.update_or_create( |
|
316 |
code=data['code'], zipcode=zipcode, defaults=defaults) |
|
317 | ||
215 | 318 |
def hourly(self): |
216 | 319 |
super(BaseAdresse, self).hourly() |
320 |
# don't wait for daily job to grab data |
|
217 | 321 |
if self.get_zipcodes() and not self.get_streets_queryset().exists(): |
218 |
# don't wait for daily job to grab streets |
|
219 | 322 |
self.update_streets_data() |
323 |
if not CityModel.objects.exists(): |
|
324 |
self.update_api_geo_data() |
|
220 | 325 | |
221 | 326 |
def daily(self): |
222 | 327 |
super(BaseAdresse, self).daily() |
223 | 328 |
self.update_streets_data() |
329 |
self.update_api_geo_data() |
|
330 | ||
224 | 331 | |
332 |
class UnaccentNameMixin(object): |
|
333 | ||
334 |
def save(self, *args, **kwargs): |
|
335 |
self.unaccent_name = unicodedata.normalize('NFKD', self.name).encode('ascii', 'ignore').lower() |
|
336 |
super(UnaccentNameMixin, self).save(*args, **kwargs) |
|
225 | 337 | |
226 |
class StreetModel(models.Model): |
|
338 | ||
339 |
class StreetModel(UnaccentNameMixin, models.Model): |
|
227 | 340 | |
228 | 341 |
city = models.CharField(_('City'), max_length=100) |
229 | 342 |
name = models.CharField(_('Street name'), max_length=150) |
... | ... | |
239 | 352 |
def __unicode__(self): |
240 | 353 |
return self.name |
241 | 354 | |
242 |
def save(self, *args, **kwargs): |
|
243 |
self.unaccent_name = unicodedata.normalize('NFKD', self.name).encode('ascii', 'ignore') |
|
244 |
super(StreetModel, self).save(*args, **kwargs) |
|
355 | ||
356 |
@six.python_2_unicode_compatible |
|
357 |
class RegionModel(UnaccentNameMixin, models.Model): |
|
358 | ||
359 |
name = models.CharField(_('Region name'), max_length=100) |
|
360 |
unaccent_name = models.CharField(_('Region name ascii char'), max_length=150, null=True) |
|
361 |
code = models.PositiveSmallIntegerField(_('Region code'), unique=True) |
|
362 | ||
363 |
def to_json(self): |
|
364 |
return { |
|
365 |
'text': str(self), |
|
366 |
'id': self.id, |
|
367 |
'code': self.code, |
|
368 |
'name': self.name, |
|
369 |
} |
|
370 | ||
371 |
class Meta: |
|
372 |
ordering = ['code'] |
|
373 | ||
374 |
def __str__(self): |
|
375 |
return '%s %s' % (self.code, self.name) |
|
376 | ||
377 | ||
378 |
@six.python_2_unicode_compatible |
|
379 |
class DepartmentModel(UnaccentNameMixin, models.Model): |
|
380 | ||
381 |
name = models.CharField(_('Department name'), max_length=100) |
|
382 |
unaccent_name = models.CharField(_('Department name ascii char'), max_length=150, null=True) |
|
383 |
code = models.CharField(_('Department code'), max_length=3, unique=True) |
|
384 |
region = models.ForeignKey(RegionModel, on_delete=models.CASCADE) |
|
385 | ||
386 |
def to_json(self): |
|
387 |
return { |
|
388 |
'text': str(self), |
|
389 |
'id': self.id, |
|
390 |
'code': self.code, |
|
391 |
'name': self.name, |
|
392 |
'region_id': self.region.id, |
|
393 |
} |
|
394 | ||
395 |
class Meta: |
|
396 |
ordering = ['code'] |
|
397 | ||
398 |
def __str__(self): |
|
399 |
return '%s %s' % (self.code, self.name) |
|
400 | ||
401 | ||
402 |
@six.python_2_unicode_compatible |
|
403 |
class CityModel(UnaccentNameMixin, models.Model): |
|
404 | ||
405 |
name = models.CharField(_('City name'), max_length=100) |
|
406 |
unaccent_name = models.CharField(_('City name ascii char'), max_length=150, null=True) |
|
407 |
code = models.CharField(_('City code'), max_length=5) |
|
408 |
zipcode = models.CharField(_('Postal code'), max_length=5) |
|
409 |
population = models.PositiveIntegerField(_('Population')) |
|
410 |
department = models.ForeignKey(DepartmentModel, on_delete=models.CASCADE, blank=True, null=True) |
|
411 |
region = models.ForeignKey(RegionModel, on_delete=models.CASCADE, blank=True, null=True) |
|
412 | ||
413 |
def to_json(self): |
|
414 |
data = { |
|
415 |
'text': str(self), |
|
416 |
'id': self.id, |
|
417 |
'code': self.code, |
|
418 |
'name': self.name, |
|
419 |
'zipcode': self.zipcode, |
|
420 |
'population': self.population, |
|
421 |
} |
|
422 |
if self.department: |
|
423 |
data['department_id'] = self.department.id |
|
424 |
if self.region: |
|
425 |
data['region_id'] = self.region.id |
|
426 |
return data |
|
427 | ||
428 |
class Meta: |
|
429 |
ordering = ['-population', 'zipcode', 'unaccent_name', 'name'] |
|
430 |
unique_together = ('code', 'zipcode') |
|
431 | ||
432 |
def __str__(self): |
|
433 |
return '%s %s' % (self.zipcode, self.name) |
tests/test_base_adresse.py | ||
---|---|---|
8 | 8 | |
9 | 9 |
from django.core.management import call_command |
10 | 10 | |
11 |
from passerelle.apps.base_adresse.models import BaseAdresse, StreetModel |
|
11 |
from passerelle.apps.base_adresse.models import (BaseAdresse, StreetModel, CityModel, |
|
12 |
DepartmentModel, RegionModel) |
|
12 | 13 | |
13 | 14 |
FAKED_CONTENT = json.dumps({ |
14 | 15 |
"limit": 1, |
... | ... | |
74 | 75 |
citycode=u'73001') |
75 | 76 | |
76 | 77 | |
78 |
@pytest.fixture |
|
79 |
def region(db): |
|
80 |
return RegionModel.objects.create(name=u'Auvergne-Rhône-Alpes', code=84) |
|
81 | ||
82 | ||
83 |
@pytest.fixture |
|
84 |
def department(db, region): |
|
85 |
return DepartmentModel.objects.create(name=u'Savoie', code='73', region=region) |
|
86 | ||
87 | ||
88 |
@pytest.fixture |
|
89 |
def city(db, region, department): |
|
90 |
return CityModel.objects.create(name=u'Chambéry', code='73065', zipcode='73000', |
|
91 |
population=42000, region=region, department=department) |
|
92 | ||
93 | ||
94 |
@pytest.fixture |
|
95 |
def miquelon(db): |
|
96 |
return CityModel.objects.create(name=u'Miquelon-Langlade', code='97501', zipcode='97500', |
|
97 |
population=42) |
|
98 | ||
99 | ||
100 |
@pytest.fixture |
|
101 |
def mock_update_api_geo(): |
|
102 |
with mock.patch('passerelle.apps.base_adresse.models.BaseAdresse.update_api_geo_data', |
|
103 |
new=lambda x: None) as _fixture: |
|
104 |
yield _fixture |
|
105 | ||
106 | ||
77 | 107 |
@mock.patch('passerelle.utils.Request.get') |
78 | 108 |
def test_base_adresse_search(mocked_get, app, base_adresse): |
79 | 109 |
endpoint = utils.generic_endpoint_url('base-adresse', 'search', slug=base_adresse.slug) |
... | ... | |
195 | 225 |
assert len(resp.json['data']) == 0 |
196 | 226 | |
197 | 227 | |
228 |
@pytest.mark.usefixtures('mock_update_api_geo') |
|
198 | 229 |
@mock.patch('passerelle.utils.Request.get') |
199 | 230 |
def test_base_adresse_command_update(mocked_get, db, base_adresse): |
200 | 231 |
filepath = os.path.join(os.path.dirname(__file__), 'data', 'update_streets_test.bz2') |
... | ... | |
214 | 245 |
mocked_get.call_count == 2 |
215 | 246 | |
216 | 247 | |
248 |
@pytest.mark.usefixtures('mock_update_api_geo') |
|
217 | 249 |
@mock.patch('passerelle.utils.Request.get') |
218 | 250 |
def test_base_adresse_command_hourly_update(mocked_get, db, base_adresse): |
251 |
base_adresse.update_api_geo_data = lambda: None |
|
219 | 252 |
filepath = os.path.join(os.path.dirname(__file__), 'data', 'update_streets_test.bz2') |
220 | 253 |
mocked_get.return_value = utils.FakedResponse(content=open(filepath).read(), status_code=200) |
221 | 254 |
# check the first hourly job downloads streets |
... | ... | |
227 | 260 |
mocked_get.call_count == 1 |
228 | 261 | |
229 | 262 | |
263 |
@pytest.mark.usefixtures('mock_update_api_geo') |
|
230 | 264 |
@mock.patch('passerelle.utils.Request.get') |
231 | 265 |
def test_base_adresse_command_update_97x(mocked_get, db, base_adresse_97x): |
266 |
base_adresse_97x.update_api_geo_data = lambda: None |
|
232 | 267 |
filepath = os.path.join(os.path.dirname(__file__), 'data', 'update_streets_test.bz2') |
233 | 268 |
mocked_get.return_value = utils.FakedResponse(content=open(filepath).read(), status_code=200) |
234 | 269 |
call_command('cron', 'daily') |
... | ... | |
236 | 271 |
assert StreetModel.objects.count() == 2 |
237 | 272 | |
238 | 273 | |
274 |
@pytest.mark.usefixtures('mock_update_api_geo') |
|
239 | 275 |
@mock.patch('passerelle.utils.Request.get') |
240 | 276 |
def test_base_adresse_command_update_corsica(mocked_get, db, base_adresse_corsica): |
277 |
base_adresse_corsica.update_api_geo_data = lambda: None |
|
241 | 278 |
filepath = os.path.join(os.path.dirname(__file__), 'data', 'update_streets_test.bz2') |
242 | 279 |
mocked_get.return_value = utils.FakedResponse(content=open(filepath).read(), status_code=200) |
243 | 280 |
call_command('cron', 'daily') |
... | ... | |
247 | 284 |
assert StreetModel.objects.count() == 0 |
248 | 285 | |
249 | 286 | |
287 |
@pytest.mark.usefixtures('mock_update_api_geo') |
|
250 | 288 |
@mock.patch('passerelle.utils.Request.get') |
251 | 289 |
def test_base_adresse_command_update_multiple(mocked_get, db, base_adresse_multiple): |
290 |
base_adresse_multiple.update_api_geo_data = lambda: None |
|
252 | 291 |
filepath = os.path.join(os.path.dirname(__file__), 'data', 'update_streets_test.bz2') |
253 | 292 |
mocked_get.return_value = utils.FakedResponse(content=open(filepath).read(), status_code=200) |
254 | 293 |
call_command('cron', 'daily') |
... | ... | |
258 | 297 |
mocked_get.assert_any_call('http://bano.openstreetmap.fr/BAN_odbl/BAN_odbl_2A-json.bz2') |
259 | 298 |
mocked_get.assert_any_call('http://bano.openstreetmap.fr/BAN_odbl/BAN_odbl_2B-json.bz2') |
260 | 299 |
assert StreetModel.objects.count() == 5 |
300 | ||
301 | ||
302 |
def test_base_adresse_cities(app, base_adresse, city, department, region): |
|
303 |
resp = app.get('/base-adresse/%s/cities?q=chambe' % base_adresse.slug) |
|
304 |
result = resp.json['data'][0] |
|
305 |
assert result['name'] == city.name |
|
306 |
assert result['text'] == '%s %s' % (city.zipcode, city.name) |
|
307 |
assert result['code'] == city.code |
|
308 |
assert result['zipcode'] == city.zipcode |
|
309 |
assert result['id'] == city.id |
|
310 |
assert result['population'] == city.population |
|
311 |
assert result['region_id'] == city.region.id |
|
312 |
assert result['department_id'] == city.department.id |
|
313 | ||
314 |
resp = app.get('/base-adresse/%s/cities?q=73' % base_adresse.slug) |
|
315 |
assert resp.json['data'][0] == result |
|
316 | ||
317 |
resp = app.get('/base-adresse/%s/cities?code=73065' % base_adresse.slug) |
|
318 |
assert resp.json['data'][0] == result |
|
319 | ||
320 | ||
321 |
def test_base_adresse_cities_missing_region_and_department(app, base_adresse, miquelon): |
|
322 |
resp = app.get('/base-adresse/%s/cities?q=miqu' % base_adresse.slug) |
|
323 |
result = resp.json['data'][0] |
|
324 |
assert result['name'] == miquelon.name |
|
325 |
assert not 'department_id' in result |
|
326 |
assert not 'region_id' in result |
|
327 | ||
328 | ||
329 |
def test_base_adresse_cities_region_department(app, base_adresse, miquelon, city): |
|
330 |
reg = RegionModel.objects.create(name=u'IdF', code='11') |
|
331 |
dep = DepartmentModel.objects.create(name=u'Paris', code='75', region=reg) |
|
332 |
paris = CityModel.objects.create(name=u'Paris', code='75056', zipcode='75014', |
|
333 |
population=2000000, region=reg, department=dep) |
|
334 |
base_adresse.department = paris.department.code |
|
335 |
base_adresse.region = paris.region.code |
|
336 |
base_adresse.save() |
|
337 |
resp = app.get('/base-adresse/%s/cities' % base_adresse.slug) |
|
338 |
result = resp.json['data'] |
|
339 |
assert len(result) == 1 |
|
340 |
assert result[0]['name'] == paris.name |
|
341 | ||
342 |
resp = app.get('/base-adresse/%s/cities?region_code=84&department_code=73' % base_adresse.slug) |
|
343 |
result = resp.json['data'] |
|
344 |
assert len(result) == 1 |
|
345 |
assert result[0]['name'] == city.name |
|
346 | ||
347 | ||
348 |
def test_base_adresse_cities_sort_order(app, base_adresse, miquelon, city): |
|
349 |
assert miquelon.population < city.population |
|
350 |
resp = app.get('/base-adresse/%s/cities' % base_adresse.slug) |
|
351 |
result = resp.json['data'] |
|
352 |
assert result[0]['name'] == city.name |
|
353 |
assert result[1]['name'] == miquelon.name |
|
354 | ||
355 | ||
356 |
def test_base_adresse_cities_get_by_id(app, base_adresse, city): |
|
357 |
for i in range(1, 10): |
|
358 |
# create additional cities |
|
359 |
city.pk = None |
|
360 |
city.zipcode = int(city.zipcode) + i |
|
361 |
city.save() |
|
362 | ||
363 |
resp = app.get('/base-adresse/%s/cities?q=cham' % base_adresse.slug) |
|
364 |
result = resp.json['data'][0] |
|
365 |
assert len(resp.json['data']) == 10 |
|
366 |
city_id = result['id'] |
|
367 | ||
368 |
resp = app.get('/base-adresse/%s/cities?id=%s' % (base_adresse.slug, city_id)) |
|
369 |
assert len(resp.json['data']) == 1 |
|
370 |
result2 = resp.json['data'][0] |
|
371 |
assert result2['text'] == result['text'] |
|
372 | ||
373 |
# non integer id. |
|
374 |
resp = app.get('/base-adresse/%s/cities?id=%s' % (base_adresse.slug, 'XXX')) |
|
375 |
assert len(resp.json['data']) == 0 |
|
376 | ||
377 |
# integer but without match. |
|
378 |
resp = app.get('/base-adresse/%s/cities?id=%s' % (base_adresse.slug, '-20')) |
|
379 |
assert len(resp.json['data']) == 0 |
|
380 | ||
381 | ||
382 |
def test_base_adresse_departments(app, base_adresse, department, region): |
|
383 |
resp = app.get('/base-adresse/%s/departments?q=sav' % base_adresse.slug) |
|
384 |
result = resp.json['data'][0] |
|
385 |
assert result['name'] == department.name |
|
386 |
assert result['code'] == department.code |
|
387 |
assert result['id'] == department.id |
|
388 |
assert result['text'] == '%s %s' % (department.code, department.name) |
|
389 |
assert result['region_id'] == region.id |
|
390 | ||
391 |
resp = app.get('/base-adresse/%s/departments?q=73' % base_adresse.slug) |
|
392 |
result = resp.json['data'][0] |
|
393 |
assert result['name'] == department.name |
|
394 | ||
395 | ||
396 |
def test_base_adresse_departments_region(app, base_adresse, department): |
|
397 |
reg = RegionModel.objects.create(name=u'IdF', code='11') |
|
398 |
paris = DepartmentModel.objects.create(name=u'Paris', code='75', region=reg) |
|
399 |
base_adresse.region = paris.region.code |
|
400 |
base_adresse.save() |
|
401 |
resp = app.get('/base-adresse/%s/departments' % base_adresse.slug) |
|
402 |
result = resp.json['data'] |
|
403 |
assert len(result) == 1 |
|
404 |
assert result[0]['name'] == paris.name |
|
405 | ||
406 |
resp = app.get('/base-adresse/%s/departments?region_code=84' % base_adresse.slug) |
|
407 |
result = resp.json['data'] |
|
408 |
assert len(result) == 1 |
|
409 |
assert result[0]['name'] == department.name |
|
410 | ||
411 | ||
412 |
def test_base_adresse_regions(app, base_adresse, region): |
|
413 |
resp = app.get('/base-adresse/%s/regions?q=au' % base_adresse.slug) |
|
414 |
result = resp.json['data'][0] |
|
415 |
assert result['name'] == region.name |
|
416 |
assert result['code'] == region.code |
|
417 |
assert result['id'] == region.id |
|
418 |
assert result['text'] == '%s %s' % (region.code, region.name) |
|
261 |
- |