Projet

Général

Profil

0002-base_adresse-add-API-G-o-endpoints-11497.patch

Valentin Deniaud, 03 décembre 2019 16:36

Télécharger (26,8 ko)

Voir les différences:

Subject: [PATCH 2/2] =?UTF-8?q?base=5Fadresse:=20add=20API=20G=C3=A9o=20en?=
 =?UTF-8?q?dpoints=20(#11497)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Namely /cities/, /departements/ and /regions/.
 .../migrations/0015_auto_20191203_1624.py     |  97 ++++++++
 passerelle/apps/base_adresse/models.py        | 229 ++++++++++++++++--
 tests/test_base_adresse.py                    | 160 +++++++++++-
 3 files changed, 465 insertions(+), 21 deletions(-)
 create mode 100644 passerelle/apps/base_adresse/migrations/0015_auto_20191203_1624.py
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
-