0004-base_adresse-add-API-G-o-endpoints-11497.patch
passerelle/apps/base_adresse/migrations/0015_auto_20191206_1244.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.18 on 2019-12-06 11:44 |
|
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=150, 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='INSEE code')), |
|
24 |
('zipcode', models.CharField(max_length=5, verbose_name='Postal code')), |
|
25 |
('population', models.PositiveIntegerField(verbose_name='Population')), |
|
26 |
('last_update', models.DateTimeField(auto_now=True, null=True, verbose_name='Last update')), |
|
27 |
], |
|
28 |
options={ |
|
29 |
'ordering': ['-population', 'zipcode', 'unaccent_name', 'name'], |
|
30 |
}, |
|
31 |
bases=(passerelle.apps.base_adresse.models.UnaccentNameMixin, models.Model), |
|
32 |
), |
|
33 |
migrations.CreateModel( |
|
34 |
name='DepartmentModel', |
|
35 |
fields=[ |
|
36 |
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|
37 |
('name', models.CharField(max_length=100, verbose_name='Department name')), |
|
38 |
('unaccent_name', models.CharField(max_length=150, null=True, verbose_name='Department name ascii char')), |
|
39 |
('code', models.CharField(max_length=3, unique=True, verbose_name='Department code')), |
|
40 |
('last_update', models.DateTimeField(auto_now=True, null=True, verbose_name='Last update')), |
|
41 |
], |
|
42 |
options={ |
|
43 |
'ordering': ['code'], |
|
44 |
}, |
|
45 |
bases=(passerelle.apps.base_adresse.models.UnaccentNameMixin, models.Model), |
|
46 |
), |
|
47 |
migrations.CreateModel( |
|
48 |
name='RegionModel', |
|
49 |
fields=[ |
|
50 |
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|
51 |
('name', models.CharField(max_length=150, verbose_name='Region name')), |
|
52 |
('unaccent_name', models.CharField(max_length=150, null=True, verbose_name='Region name ascii char')), |
|
53 |
('code', models.CharField(max_length=2, unique=True, verbose_name='Region code')), |
|
54 |
('last_update', models.DateTimeField(auto_now=True, null=True, verbose_name='Last update')), |
|
55 |
], |
|
56 |
options={ |
|
57 |
'ordering': ['code'], |
|
58 |
}, |
|
59 |
bases=(passerelle.apps.base_adresse.models.UnaccentNameMixin, models.Model), |
|
60 |
), |
|
61 |
migrations.AddField( |
|
62 |
model_name='baseadresse', |
|
63 |
name='api_geo_url', |
|
64 |
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'), |
|
65 |
), |
|
66 |
migrations.AlterField( |
|
67 |
model_name='baseadresse', |
|
68 |
name='zipcode', |
|
69 |
field=models.CharField(blank=True, max_length=600, verbose_name='Postal codes or department number to get streets, separated with commas'), |
|
70 |
), |
|
71 |
migrations.AlterField( |
|
72 |
model_name='streetmodel', |
|
73 |
name='city', |
|
74 |
field=models.CharField(max_length=150, verbose_name='City'), |
|
75 |
), |
|
76 |
migrations.AddField( |
|
77 |
model_name='departmentmodel', |
|
78 |
name='region', |
|
79 |
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base_adresse.RegionModel'), |
|
80 |
), |
|
81 |
migrations.AddField( |
|
82 |
model_name='citymodel', |
|
83 |
name='department', |
|
84 |
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='base_adresse.DepartmentModel'), |
|
85 |
), |
|
86 |
migrations.AddField( |
|
87 |
model_name='citymodel', |
|
88 |
name='region', |
|
89 |
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='base_adresse.RegionModel'), |
|
90 |
), |
|
91 |
migrations.AlterUniqueTogether( |
|
92 |
name='citymodel', |
|
93 |
unique_together=set([('code', 'zipcode')]), |
|
94 |
), |
|
95 |
] |
passerelle/apps/base_adresse/models.py | ||
---|---|---|
1 | 1 |
import bz2 |
2 | 2 |
import json |
3 |
import os |
|
4 | 3 |
import urlparse |
5 | 4 |
import unicodedata |
6 | 5 | |
6 |
from requests import RequestException |
|
7 | 7 | |
8 | 8 |
from django.db import connection, models |
9 | 9 |
from django.db.models import Q |
... | ... | |
15 | 15 | |
16 | 16 |
from passerelle.base.models import BaseResource |
17 | 17 |
from passerelle.utils.api import endpoint |
18 |
from passerelle.utils.conversion import simplify |
|
19 |
from passerelle.utils.jsonresponse import APIError |
|
18 | 20 | |
19 | 21 | |
20 | 22 |
class BaseAdresse(BaseResource): |
... | ... | |
24 | 26 |
verbose_name=_('Service URL'), |
25 | 27 |
help_text=_('Base Adresse Web Service URL')) |
26 | 28 | |
29 |
api_geo_url = models.CharField( |
|
30 |
max_length=128, blank=False, |
|
31 |
default='https://geo.api.gouv.fr/', |
|
32 |
verbose_name=_('API Geo URL'), |
|
33 |
help_text=_('Base Adresse API Geo URL')) |
|
34 | ||
27 | 35 |
category = _('Geographic information system') |
28 | 36 | |
29 |
api_description = _("The API is a partial view of OpenStreetMap's Nominatim " |
|
30 |
"own API; it currently doesn't support all parameters and " |
|
31 |
"is limited to the JSON format.") |
|
37 |
api_description = _("The geocoding endpoints are a partial view of OpenStreetMap's " |
|
38 |
"Nominatim own API; it currently doesn't support all parameters and " |
|
39 |
"is limited to the JSON format. The cities, departments and regions " |
|
40 |
"endpoints source data from French API Geo.") |
|
32 | 41 | |
33 | 42 |
zipcode = models.CharField( |
34 | 43 |
max_length=600, |
35 | 44 |
blank=True, |
36 |
verbose_name=_('Postal codes or county number to get streets, separated with commas'))
|
|
45 |
verbose_name=_('Postal codes or department number to get streets, separated with commas'))
|
|
37 | 46 | |
38 | 47 |
class Meta: |
39 | 48 |
verbose_name = _('Base Adresse Web Service') |
... | ... | |
135 | 144 |
else: |
136 | 145 |
streets = StreetModel.objects.all() |
137 | 146 |
if q: |
138 |
unaccented_q = unicodedata.normalize('NFKD', q).encode('ascii', 'ignore').lower() |
|
139 |
streets = streets.filter(unaccent_name__icontains=unaccented_q) |
|
147 |
streets = streets.filter(unaccent_name__icontains=simplify(q)) |
|
140 | 148 | |
141 | 149 |
if zipcode: |
142 | 150 |
streets = streets.filter(zipcode__startswith=zipcode) |
... | ... | |
158 | 166 | |
159 | 167 |
return {'data': result} |
160 | 168 | |
169 |
@endpoint(description=_('Cities list'), |
|
170 |
parameters={ |
|
171 |
'id': {'description': _('Get exactly one city using its code and postal code ' |
|
172 |
'separated with a dot'), |
|
173 |
'example_value': '75056.75014'}, |
|
174 |
'q': {'description': _("Search text in name or postal code"), |
|
175 |
'example_value': 'Paris'}, |
|
176 |
'code': {'description': _('INSEE code'), 'example_value': '75056'}, |
|
177 |
'region_code': {'description': _('Region code'), 'example_value': '11'}, |
|
178 |
'department_code': {'description': _('Department code'), 'example_value': '75'}, |
|
179 |
}) |
|
180 |
def cities(self, request, id=None, q=None, code=None, region_code=None, |
|
181 |
department_code=None): |
|
182 |
cities = CityModel.objects.all() |
|
183 | ||
184 |
if id is not None: |
|
185 |
try: |
|
186 |
code, zipcode = id.split('.') |
|
187 |
except ValueError: |
|
188 |
raise APIError('Invalid id') |
|
189 |
cities = cities.filter(code=code, zipcode=zipcode) |
|
190 |
if q: |
|
191 |
unaccented_q = simplify(q) |
|
192 |
cities = cities.filter(Q(unaccent_name__istartswith=unaccented_q) | |
|
193 |
Q(zipcode__istartswith=unaccented_q)) |
|
194 |
if code: |
|
195 |
cities = cities.filter(code=code) |
|
196 |
if region_code: |
|
197 |
cities = cities.filter(region__code=region_code) |
|
198 |
if department_code: |
|
199 |
cities = cities.filter(department__code=department_code) |
|
200 | ||
201 |
cities = cities.select_related('department', 'region') |
|
202 |
return {'data': [city.to_json() for city in cities]} |
|
203 | ||
204 |
@endpoint(description=_('Departments list'), |
|
205 |
parameters={ |
|
206 |
'id': {'description': _('Get exactly one department using its code'), |
|
207 |
'example_value': '59'}, |
|
208 |
'q': {'description': _('Search text in name or code'), 'example_value': 'Nord'}, |
|
209 |
'region_code': {'description': _('Region code'), 'example_value': '32'}, |
|
210 |
}) |
|
211 |
def departments(self, request, id=None, q=None, region_code=None): |
|
212 |
departments = DepartmentModel.objects.all() |
|
213 | ||
214 |
if id is not None: |
|
215 |
departments = departments.filter(code=id) |
|
216 |
if q: |
|
217 |
unaccented_q = simplify(q) |
|
218 |
departments = departments.filter(Q(unaccent_name__istartswith=unaccented_q) | |
|
219 |
Q(code__istartswith=unaccented_q)) |
|
220 |
if region_code: |
|
221 |
departments = departments.filter(region__code=region_code) |
|
222 | ||
223 |
departments = departments.select_related('region') |
|
224 |
return {'data': [department.to_json() for department in departments]} |
|
225 | ||
226 |
@endpoint(description=_('Regions list'), |
|
227 |
parameters={ |
|
228 |
'id': {'description': _('Get exactly one region using its code'), |
|
229 |
'example_value': '32'}, |
|
230 |
'q': {'description': _('Search text in name or code'), |
|
231 |
'example_value': 'Hauts-de-France'}, |
|
232 |
}) |
|
233 |
def regions(self, request, id=None, q=None): |
|
234 |
regions = RegionModel.objects.all() |
|
235 | ||
236 |
if id is not None: |
|
237 |
regions = regions.filter(code=id) |
|
238 |
if q: |
|
239 |
unaccented_q = simplify(q) |
|
240 |
regions = regions.filter(Q(unaccent_name__istartswith=unaccented_q) | |
|
241 |
Q(code__istartswith=unaccented_q)) |
|
242 | ||
243 |
return {'data': [region.to_json() for region in regions]} |
|
244 | ||
161 | 245 |
def check_status(self): |
162 | 246 |
if self.service_url == 'https://api-adresse.data.gouv.fr/': |
163 | 247 |
result = self.search(None, '169 rue du chateau, paris') |
... | ... | |
174 | 258 |
criteria |= Q(zipcode__startswith=zipcode) |
175 | 259 |
return StreetModel.objects.filter(criteria) |
176 | 260 | |
261 |
def cities_exist(self): |
|
262 |
return CityModel.objects.exists() |
|
263 | ||
177 | 264 |
def update_streets_data(self): |
178 | 265 |
if not self.get_zipcodes(): |
179 | 266 |
return |
... | ... | |
218 | 305 | |
219 | 306 |
self.get_streets_queryset().filter(last_update__lt=start_update).delete() |
220 | 307 | |
308 |
def get_api_geo_endpoint(self, endpoint): |
|
309 |
if not self.api_geo_url: |
|
310 |
return |
|
311 |
error = None |
|
312 |
try: |
|
313 |
response = self.requests.get(urljoin(self.api_geo_url, endpoint)) |
|
314 |
except RequestException as e: |
|
315 |
error = e |
|
316 |
else: |
|
317 |
if response.status_code != 200: |
|
318 |
error = 'bad status code (%s)' % response.status_code |
|
319 |
else: |
|
320 |
try: |
|
321 |
result = response.json() |
|
322 |
except ValueError: |
|
323 |
error = 'invalid json, got: %s' % response.text |
|
324 |
if error: |
|
325 |
self.logger.error('failed to update api geo data for endpoint %s: %s', |
|
326 |
endpoint, error) |
|
327 |
return |
|
328 |
if not result: |
|
329 |
raise Exception('api geo returns empty json') |
|
330 |
return result |
|
331 | ||
332 |
def update_api_geo_data(self): |
|
333 |
regions_json = self.get_api_geo_endpoint('regions') |
|
334 |
departments_json = self.get_api_geo_endpoint('departements') |
|
335 |
cities_json = self.get_api_geo_endpoint('communes') |
|
336 |
if not (regions_json and departments_json and cities_json): |
|
337 |
return |
|
338 |
start_update = timezone.now() |
|
339 | ||
340 |
for data in regions_json: |
|
341 |
defaults = { |
|
342 |
'name': data['nom'], |
|
343 |
} |
|
344 |
RegionModel.objects.update_or_create(code=data['code'], defaults=defaults) |
|
345 |
RegionModel.objects.filter(last_update__lt=start_update).delete() |
|
346 | ||
347 |
for data in departments_json: |
|
348 |
defaults = { |
|
349 |
'name': data['nom'], |
|
350 |
'region': RegionModel.objects.get(code=data['codeRegion']), |
|
351 |
} |
|
352 |
DepartmentModel.objects.update_or_create(code=data['code'], defaults=defaults) |
|
353 |
DepartmentModel.objects.filter(last_update__lt=start_update).delete() |
|
354 | ||
355 |
for data in cities_json: |
|
356 |
for zipcode in data['codesPostaux']: |
|
357 |
defaults = { |
|
358 |
'name': data['nom'], |
|
359 |
'population': data.get('population', 0), |
|
360 |
} |
|
361 |
if data.get('codeDepartement'): |
|
362 |
defaults['department'] = DepartmentModel.objects.get(code=data['codeDepartement']) |
|
363 |
if data.get('codeRegion'): |
|
364 |
defaults['region'] = RegionModel.objects.get(code=data['codeRegion']) |
|
365 |
CityModel.objects.update_or_create( |
|
366 |
code=data['code'], zipcode=zipcode, defaults=defaults) |
|
367 |
CityModel.objects.filter(last_update__lt=start_update).delete() |
|
368 | ||
221 | 369 |
def hourly(self): |
222 | 370 |
super(BaseAdresse, self).hourly() |
371 |
# don't wait for daily job to grab data |
|
223 | 372 |
if self.get_zipcodes() and not self.get_streets_queryset().exists(): |
224 |
# don't wait for daily job to grab streets |
|
225 | 373 |
self.update_streets_data() |
374 |
if not CityModel.objects.exists(): |
|
375 |
self.update_api_geo_data() |
|
226 | 376 | |
227 | 377 |
def daily(self): |
228 | 378 |
super(BaseAdresse, self).daily() |
229 | 379 |
self.update_streets_data() |
380 |
self.update_api_geo_data() |
|
230 | 381 | |
231 | 382 | |
232 |
class StreetModel(models.Model):
|
|
383 |
class UnaccentNameMixin(object):
|
|
233 | 384 | |
234 |
city = models.CharField(_('City'), max_length=100) |
|
385 |
def save(self, *args, **kwargs): |
|
386 |
self.unaccent_name = unicodedata.normalize('NFKD', self.name).encode('ascii', 'ignore').lower() |
|
387 |
super(UnaccentNameMixin, self).save(*args, **kwargs) |
|
388 | ||
389 | ||
390 |
class StreetModel(UnaccentNameMixin, models.Model): |
|
391 | ||
392 |
city = models.CharField(_('City'), max_length=150) |
|
235 | 393 |
name = models.CharField(_('Street name'), max_length=150) |
236 | 394 |
unaccent_name = models.CharField(_('Street name ascii char'), max_length=150, null=True) |
237 | 395 |
zipcode = models.CharField(_('Postal code'), max_length=5) |
... | ... | |
245 | 403 |
def __unicode__(self): |
246 | 404 |
return self.name |
247 | 405 | |
248 |
def save(self, *args, **kwargs): |
|
249 |
self.unaccent_name = unicodedata.normalize('NFKD', self.name).encode('ascii', 'ignore') |
|
250 |
super(StreetModel, self).save(*args, **kwargs) |
|
406 | ||
407 |
@six.python_2_unicode_compatible |
|
408 |
class RegionModel(UnaccentNameMixin, models.Model): |
|
409 | ||
410 |
name = models.CharField(_('Region name'), max_length=150) |
|
411 |
unaccent_name = models.CharField(_('Region name ascii char'), max_length=150, null=True) |
|
412 |
code = models.CharField(_('Region code'), max_length=2, unique=True) |
|
413 |
last_update = models.DateTimeField(_('Last update'), null=True, auto_now=True) |
|
414 | ||
415 |
def to_json(self): |
|
416 |
return { |
|
417 |
'text': str(self), |
|
418 |
'id': self.code, |
|
419 |
'code': self.code, |
|
420 |
'name': self.name, |
|
421 |
} |
|
422 | ||
423 |
class Meta: |
|
424 |
ordering = ['code'] |
|
425 | ||
426 |
def __str__(self): |
|
427 |
return '%s %s' % (self.code, self.name) |
|
428 | ||
429 | ||
430 |
@six.python_2_unicode_compatible |
|
431 |
class DepartmentModel(UnaccentNameMixin, models.Model): |
|
432 | ||
433 |
name = models.CharField(_('Department name'), max_length=100) |
|
434 |
unaccent_name = models.CharField(_('Department name ascii char'), max_length=150, null=True) |
|
435 |
code = models.CharField(_('Department code'), max_length=3, unique=True) |
|
436 |
region = models.ForeignKey(RegionModel, on_delete=models.CASCADE) |
|
437 |
last_update = models.DateTimeField(_('Last update'), null=True, auto_now=True) |
|
438 | ||
439 |
def to_json(self): |
|
440 |
return { |
|
441 |
'text': str(self), |
|
442 |
'id': self.code, |
|
443 |
'code': self.code, |
|
444 |
'name': self.name, |
|
445 |
'region_code': self.region.code, |
|
446 |
'region_name': self.region.name, |
|
447 |
} |
|
448 | ||
449 |
class Meta: |
|
450 |
ordering = ['code'] |
|
451 | ||
452 |
def __str__(self): |
|
453 |
return '%s %s' % (self.code, self.name) |
|
454 | ||
455 | ||
456 |
@six.python_2_unicode_compatible |
|
457 |
class CityModel(UnaccentNameMixin, models.Model): |
|
458 | ||
459 |
name = models.CharField(_('City name'), max_length=150) |
|
460 |
unaccent_name = models.CharField(_('City name ascii char'), max_length=150, null=True) |
|
461 |
code = models.CharField(_('INSEE code'), max_length=5) |
|
462 |
zipcode = models.CharField(_('Postal code'), max_length=5) |
|
463 |
population = models.PositiveIntegerField(_('Population')) |
|
464 |
department = models.ForeignKey(DepartmentModel, on_delete=models.CASCADE, blank=True, null=True) |
|
465 |
region = models.ForeignKey(RegionModel, on_delete=models.CASCADE, blank=True, null=True) |
|
466 |
last_update = models.DateTimeField(_('Last update'), null=True, auto_now=True) |
|
467 | ||
468 |
def to_json(self): |
|
469 |
data = { |
|
470 |
'text': str(self), |
|
471 |
'id': '%s.%s' % (self.code, self.zipcode), |
|
472 |
'code': self.code, |
|
473 |
'name': self.name, |
|
474 |
'zipcode': self.zipcode, |
|
475 |
'population': self.population, |
|
476 |
'department_code': self.department.code if self.department else None, |
|
477 |
'department_name': self.department.name if self.department else None, |
|
478 |
'region_code': self.region.code if self.region else None, |
|
479 |
'region_name': self.region.name if self.region else None, |
|
480 |
} |
|
481 |
return data |
|
482 | ||
483 |
class Meta: |
|
484 |
ordering = ['-population', 'zipcode', 'unaccent_name', 'name'] |
|
485 |
unique_together = ('code', 'zipcode') |
|
486 | ||
487 |
def __str__(self): |
|
488 |
return '%s %s' % (self.zipcode, self.name) |
passerelle/apps/base_adresse/templates/base_adresse/baseadresse_detail.html | ||
---|---|---|
8 | 8 |
{% trans "Street data is not available yet, it should soon be downloaded." %} |
9 | 9 |
</div> |
10 | 10 |
{% endif %} |
11 |
{% if object.api_geo_url and not object.cities_exist %} |
|
12 |
<div class="infonotice"> |
|
13 |
{% trans "API Géo data is not available yet, it should soon be downloaded." %} |
|
14 |
</div> |
|
15 |
{% endif %} |
|
11 | 16 |
{% endblock %} |
12 | 17 | |
13 | 18 |
{% block security %} |
passerelle/locale/fr/LC_MESSAGES/django.po | ||
---|---|---|
7 | 7 |
msgstr "" |
8 | 8 |
"Project-Id-Version: Passerelle 0\n" |
9 | 9 |
"Report-Msgid-Bugs-To: \n" |
10 |
"POT-Creation-Date: 2019-11-25 14:55+0100\n"
|
|
10 |
"POT-Creation-Date: 2019-12-06 12:34+0100\n"
|
|
11 | 11 |
"PO-Revision-Date: 2019-11-07 19:50+0100\n" |
12 | 12 |
"Last-Translator: Frederic Peters <fpeters@entrouvert.com>\n" |
13 | 13 |
"Language: fr\n" |
... | ... | |
87 | 87 |
#: contrib/maarch/models.py:37 contrib/mdph13/models.py:56 |
88 | 88 |
#: contrib/planitech/models.py:277 contrib/seisin_by_email/models.py:37 |
89 | 89 |
#: contrib/solis_apa/models.py:53 contrib/teamnet_axel/models.py:77 |
90 |
#: contrib/toulouse_axel/models.py:118
|
|
90 |
#: contrib/toulouse_axel/models.py:122
|
|
91 | 91 |
msgid "Business Process Connectors" |
92 | 92 |
msgstr "Connecteurs métiers" |
93 | 93 | |
... | ... | |
225 | 225 |
msgid "API Particulier" |
226 | 226 |
msgstr "API Particulier" |
227 | 227 | |
228 |
#: apps/arcgis/models.py:33 apps/base_adresse/models.py:26
|
|
228 |
#: apps/arcgis/models.py:33 apps/base_adresse/models.py:36
|
|
229 | 229 |
#: apps/opengis/models.py:57 contrib/adict/models.py:25 |
230 | 230 |
#: contrib/grandlyon_streetsections/models.py:43 |
231 | 231 |
msgid "Geographic information system" |
... | ... | |
257 | 257 |
msgstr "Nom de la couche ou de la table" |
258 | 258 | |
259 | 259 |
#: apps/arcgis/models.py:56 apps/arcgis/models.py:170 |
260 |
#: apps/opengis/models.py:217 contrib/adict/models.py:36 |
|
260 |
#: apps/base_adresse/models.py:91 apps/opengis/models.py:217 |
|
261 |
#: contrib/adict/models.py:36 |
|
261 | 262 |
msgid "Latitude" |
262 | 263 |
msgstr "Latitude" |
263 | 264 | |
264 | 265 |
#: apps/arcgis/models.py:57 apps/arcgis/models.py:171 |
265 |
#: apps/opengis/models.py:218 contrib/adict/models.py:37 |
|
266 |
#: apps/base_adresse/models.py:92 apps/opengis/models.py:218 |
|
267 |
#: contrib/adict/models.py:37 |
|
266 | 268 |
msgid "Longitude" |
267 | 269 |
msgstr "Longitude" |
268 | 270 | |
... | ... | |
365 | 367 |
msgid "Association ID" |
366 | 368 |
msgstr "Identifiant de l'association" |
367 | 369 | |
368 |
#: apps/astregs/models.py:352 contrib/toulouse_axel/models.py:135 |
|
369 |
#: contrib/toulouse_axel/models.py:169 |
|
370 |
#: apps/astregs/models.py:352 contrib/toulouse_axel/models.py:139 |
|
371 |
#: contrib/toulouse_axel/models.py:177 contrib/toulouse_axel/models.py:205 |
|
372 |
#: contrib/toulouse_axel/models.py:215 |
|
370 | 373 |
msgid "Publik ID" |
371 | 374 |
msgstr "Publik NameID" |
372 | 375 | |
... | ... | |
545 | 548 |
msgid "Anything" |
546 | 549 |
msgstr "Informations additionnelles" |
547 | 550 | |
548 |
#: apps/base_adresse/models.py:23 apps/bdp/models.py:13 apps/gdc/models.py:25
|
|
551 |
#: apps/base_adresse/models.py:27 apps/bdp/models.py:13 apps/gdc/models.py:25
|
|
549 | 552 |
#: apps/okina/models.py:29 apps/solis/models.py:78 contrib/gdema/models.py:147 |
550 | 553 |
msgid "Service URL" |
551 | 554 |
msgstr "URL du webservice" |
552 | 555 | |
553 |
#: apps/base_adresse/models.py:24
|
|
556 |
#: apps/base_adresse/models.py:28
|
|
554 | 557 |
msgid "Base Adresse Web Service URL" |
555 | 558 |
msgstr "URL du webservice de la Base Adresse" |
556 | 559 | |
557 |
#: apps/base_adresse/models.py:31 |
|
558 |
msgid "Postal codes or county number to get streets, separated with commas" |
|
560 |
#: apps/base_adresse/models.py:33 |
|
561 |
msgid "API Geo URL" |
|
562 |
msgstr "URL de base de l'API Géo" |
|
563 | ||
564 |
#: apps/base_adresse/models.py:34 |
|
565 |
msgid "Base Adresse API Geo URL" |
|
566 |
msgstr "URL du webservice de l'API Géo" |
|
567 | ||
568 |
#: apps/base_adresse/models.py:38 |
|
569 |
msgid "" |
|
570 |
"The geocoding endpoints are a partial view of OpenStreetMap's Nominatim own " |
|
571 |
"API; it currently doesn't support all parameters and is limited to the JSON " |
|
572 |
"format. The cities, departments and regions endpoints source data from " |
|
573 |
"French API Geo." |
|
574 |
msgstr "" |
|
575 |
"\n" |
|
576 |
"L'API est une vue partielle de l'API Nominatim de OpenStreetMap ;\n" |
|
577 |
" elle ne prend pour le moment pas en charge tous les paramètres et est " |
|
578 |
"limitée au format JSON. Les points d'accès cities, departments et regions " |
|
579 |
"utilisent les données de l'API Géo.\n" |
|
580 | ||
581 |
#: apps/base_adresse/models.py:46 |
|
582 |
msgid "Postal codes or department number to get streets, separated with commas" |
|
559 | 583 |
msgstr "" |
560 | 584 |
"Codes postaux ou numéros de départements d'où récupérer les rues, séparés " |
561 | 585 |
"par des virgules." |
562 | 586 | |
563 |
#: apps/base_adresse/models.py:34
|
|
587 |
#: apps/base_adresse/models.py:49
|
|
564 | 588 |
msgid "Base Adresse Web Service" |
565 | 589 |
msgstr "Service Web Base Adresse" |
566 | 590 | |
567 |
#: apps/base_adresse/models.py:213 apps/family/models.py:408 |
|
568 |
#: apps/family/models.py:449 contrib/iws/models.py:155 |
|
569 |
msgid "City" |
|
570 |
msgstr "Ville" |
|
591 |
#: apps/base_adresse/models.py:52 |
|
592 |
msgid "Geocoding" |
|
593 |
msgstr "Géocodage" |
|
594 | ||
595 |
#: apps/base_adresse/models.py:54 |
|
596 |
#: contrib/agoraplus/templates/passerelle/contrib/agoraplus/detail.html:44 |
|
597 |
msgid "Address" |
|
598 |
msgstr "Adresse" |
|
599 | ||
600 |
#: apps/base_adresse/models.py:89 |
|
601 |
msgid "Reverse geocoding" |
|
602 |
msgstr "Géocodage inversé" |
|
603 | ||
604 |
#: apps/base_adresse/models.py:128 |
|
605 |
msgid "Streets from zipcode" |
|
606 |
msgstr "Rues d'un code postal :" |
|
607 | ||
608 |
#: apps/base_adresse/models.py:130 |
|
609 |
#, fuzzy |
|
610 |
#| msgid "Get exactly one region using its code" |
|
611 |
msgid "Get exactly one street" |
|
612 |
msgstr "Récupérer exactement une région depuis son code" |
|
571 | 613 | |
572 |
#: apps/base_adresse/models.py:214 apps/family/models.py:404
|
|
573 |
#: apps/family/models.py:445 |
|
614 |
#: apps/base_adresse/models.py:131 apps/base_adresse/models.py:392
|
|
615 |
#: apps/family/models.py:404 apps/family/models.py:445
|
|
574 | 616 |
msgid "Street name" |
575 | 617 |
msgstr "Nom de la voie" |
576 | 618 | |
577 |
#: apps/base_adresse/models.py:215 |
|
619 |
#: apps/base_adresse/models.py:132 apps/family/models.py:407 |
|
620 |
#: apps/family/models.py:448 |
|
621 |
msgid "Zipcode" |
|
622 |
msgstr "Code postal" |
|
623 | ||
624 |
#: apps/base_adresse/models.py:133 |
|
625 |
msgid "Maximum number of results to return" |
|
626 |
msgstr "Nombre maximal de résultats" |
|
627 | ||
628 |
#: apps/base_adresse/models.py:135 |
|
629 |
msgid "Remove duplicate streets" |
|
630 |
msgstr "Ne pas inclure les doublons" |
|
631 | ||
632 |
#: apps/base_adresse/models.py:170 |
|
633 |
msgid "Cities list" |
|
634 |
msgstr "Liste des villes" |
|
635 | ||
636 |
#: apps/base_adresse/models.py:172 |
|
637 |
msgid "" |
|
638 |
"Get exactly one city using its code and postal code separated with a dot" |
|
639 |
msgstr "" |
|
640 |
"Récupérer exactement une ville grâce à son code INSEE et son code postal, " |
|
641 |
"séparés par un point" |
|
642 | ||
643 |
#: apps/base_adresse/models.py:175 |
|
644 |
msgid "Search text in name or postal code" |
|
645 |
msgstr "Recherche du texte dans le nom et le code postal" |
|
646 | ||
647 |
#: apps/base_adresse/models.py:177 apps/base_adresse/models.py:459 |
|
648 |
msgid "INSEE code" |
|
649 |
msgstr "" |
|
650 | ||
651 |
#: apps/base_adresse/models.py:178 apps/base_adresse/models.py:210 |
|
652 |
#: apps/base_adresse/models.py:411 |
|
653 |
msgid "Region code" |
|
654 |
msgstr "Code de la région" |
|
655 | ||
656 |
#: apps/base_adresse/models.py:179 apps/base_adresse/models.py:434 |
|
657 |
msgid "Department code" |
|
658 |
msgstr "Code du département" |
|
659 | ||
660 |
#: apps/base_adresse/models.py:205 |
|
661 |
msgid "Departments list" |
|
662 |
msgstr "Liste des départements" |
|
663 | ||
664 |
#: apps/base_adresse/models.py:207 |
|
665 |
msgid "Get exactly one department using its code" |
|
666 |
msgstr "" |
|
667 | ||
668 |
#: apps/base_adresse/models.py:209 apps/base_adresse/models.py:231 |
|
669 |
msgid "Search text in name or code" |
|
670 |
msgstr "Recherche du texte dans le nom ou le code" |
|
671 | ||
672 |
#: apps/base_adresse/models.py:227 |
|
673 |
msgid "Regions list" |
|
674 |
msgstr "Liste des régions" |
|
675 | ||
676 |
#: apps/base_adresse/models.py:229 |
|
677 |
msgid "Get exactly one region using its code" |
|
678 |
msgstr "Récupérer exactement une région depuis son code" |
|
679 | ||
680 |
#: apps/base_adresse/models.py:391 apps/family/models.py:408 |
|
681 |
#: apps/family/models.py:449 contrib/iws/models.py:155 |
|
682 |
msgid "City" |
|
683 |
msgstr "Ville" |
|
684 | ||
685 |
#: apps/base_adresse/models.py:393 |
|
578 | 686 |
msgid "Street name ascii char" |
579 | 687 |
msgstr "Nom de la voie (en ASCII)" |
580 | 688 | |
581 |
#: apps/base_adresse/models.py:216
|
|
689 |
#: apps/base_adresse/models.py:394 apps/base_adresse/models.py:460
|
|
582 | 690 |
msgid "Postal code" |
583 | 691 |
msgstr "Code postal" |
584 | 692 | |
585 |
#: apps/base_adresse/models.py:217
|
|
693 |
#: apps/base_adresse/models.py:395
|
|
586 | 694 |
msgid "Street type" |
587 | 695 |
msgstr "Type de la voie" |
588 | 696 | |
589 |
#: apps/base_adresse/models.py:218
|
|
697 |
#: apps/base_adresse/models.py:396
|
|
590 | 698 |
msgid "City Code" |
591 | 699 |
msgstr "Code de la ville (INSEE)" |
592 | 700 | |
593 |
#: apps/base_adresse/models.py:219 contrib/tcl/models.py:175 |
|
594 |
#: contrib/tcl/models.py:262 |
|
701 |
#: apps/base_adresse/models.py:397 apps/base_adresse/models.py:412 |
|
702 |
#: apps/base_adresse/models.py:436 apps/base_adresse/models.py:464 |
|
703 |
#: contrib/tcl/models.py:175 contrib/tcl/models.py:262 |
|
595 | 704 |
msgid "Last update" |
596 | 705 |
msgstr "Dernière mise à jour" |
597 | 706 | |
707 |
#: apps/base_adresse/models.py:409 |
|
708 |
msgid "Region name" |
|
709 |
msgstr "Nom de la région" |
|
710 | ||
711 |
#: apps/base_adresse/models.py:410 |
|
712 |
msgid "Region name ascii char" |
|
713 |
msgstr "Nom de la région (en ASCII)" |
|
714 | ||
715 |
#: apps/base_adresse/models.py:432 |
|
716 |
msgid "Department name" |
|
717 |
msgstr "Nom du département" |
|
718 | ||
719 |
#: apps/base_adresse/models.py:433 |
|
720 |
msgid "Department name ascii char" |
|
721 |
msgstr "Nom du département (en ASCII)" |
|
722 | ||
723 |
#: apps/base_adresse/models.py:457 |
|
724 |
msgid "City name" |
|
725 |
msgstr "Nom de la ville" |
|
726 | ||
727 |
#: apps/base_adresse/models.py:458 |
|
728 |
msgid "City name ascii char" |
|
729 |
msgstr "Nom de la ville (en ASCII)" |
|
730 | ||
731 |
#: apps/base_adresse/models.py:461 |
|
732 |
msgid "Population" |
|
733 |
msgstr "Population" |
|
734 | ||
598 | 735 |
#: apps/base_adresse/templates/base_adresse/baseadresse_detail.html:8 |
599 | 736 |
msgid "Street data is not available yet, it should soon be downloaded." |
600 | 737 |
msgstr "" |
601 | 738 |
"Les données des rues ne sont pas encore disponibles; elles devraient bientôt " |
602 | 739 |
"être téléchargées." |
603 | 740 | |
604 |
#: apps/base_adresse/templates/base_adresse/baseadresse_detail.html:15 |
|
605 |
msgid "" |
|
606 |
"\n" |
|
607 |
"The API is a partial view of <a href=\"http://wiki.openstreetmap.org/wiki/" |
|
608 |
"Nominatim\">Nominatim</a>\n" |
|
609 |
"own API; it currently doesn't support all parameters and is limited to the " |
|
610 |
"JSON\n" |
|
611 |
"format.\n" |
|
741 |
#: apps/base_adresse/templates/base_adresse/baseadresse_detail.html:13 |
|
742 |
#, fuzzy |
|
743 |
#| msgid "Street data is not available yet, it should soon be downloaded." |
|
744 |
msgid "API Géo data is not available yet, it should soon be downloaded." |
|
612 | 745 |
msgstr "" |
613 |
"\n" |
|
614 |
"L'API est une vue partielle de l'API de <a href=\"http://wiki.openstreetmap." |
|
615 |
"org/wiki/Nominatim\">Nominatim</a>;\n" |
|
616 |
" elle ne prend pour le moment pas en charge tous les paramètres et est " |
|
617 |
"limitée au format JSON.\n" |
|
618 | ||
619 |
#: apps/base_adresse/templates/base_adresse/baseadresse_detail.html:23 |
|
620 |
msgid "Geocoding:" |
|
621 |
msgstr "Géocodage :" |
|
622 | ||
623 |
#: apps/base_adresse/templates/base_adresse/baseadresse_detail.html:25 |
|
624 |
msgid "Reverse geocoding:" |
|
625 |
msgstr "Géocodage inversé :" |
|
626 | ||
627 |
#: apps/base_adresse/templates/base_adresse/baseadresse_detail.html:27 |
|
628 |
msgid "Streets from zipcode:" |
|
629 |
msgstr "Rues d'un code postal :" |
|
746 |
"Les données des rues ne sont pas encore disponibles; elles devraient bientôt " |
|
747 |
"être téléchargées." |
|
630 | 748 | |
631 |
#: apps/base_adresse/templates/base_adresse/baseadresse_detail.html:34
|
|
749 |
#: apps/base_adresse/templates/base_adresse/baseadresse_detail.html:20
|
|
632 | 750 |
msgid "Accessing the listings is open." |
633 | 751 |
msgstr "L'accès aux listes est ouvert." |
634 | 752 | |
... | ... | |
1182 | 1300 |
msgstr "Nom de la feuille" |
1183 | 1301 | |
1184 | 1302 |
#: apps/csvdatasource/models.py:136 apps/feeds/models.py:29 |
1185 |
#: apps/jsondatastore/models.py:78
|
|
1303 |
#: apps/jsondatastore/models.py:79
|
|
1186 | 1304 |
#: apps/mdel/templates/mdel/mdel_detail.html:124 contrib/nancypoll/models.py:21 |
1187 | 1305 |
msgid "Data Sources" |
1188 | 1306 |
msgstr "Sources des données" |
... | ... | |
1245 | 1363 |
msgstr "Êtes-vous sûr·e de vouloir supprimer cette requête ?" |
1246 | 1364 | |
1247 | 1365 |
#: apps/csvdatasource/templates/csvdatasource/query_confirm_delete.html:20 |
1248 |
#: apps/jsondatastore/models.py:142
|
|
1366 |
#: apps/jsondatastore/models.py:160
|
|
1249 | 1367 |
#: apps/sp_fr/templates/sp_fr/resource_detail.html:32 |
1250 | 1368 |
#: templates/passerelle/manage/accessright_confirm_delete.html:12 |
1251 | 1369 |
#: templates/passerelle/manage/apiuser_confirm_delete.html:20 |
... | ... | |
1348 | 1466 |
msgid "Address complement" |
1349 | 1467 |
msgstr "Complément d'adresse" |
1350 | 1468 | |
1351 |
#: apps/family/models.py:407 apps/family/models.py:448 |
|
1352 |
msgid "Zipcode" |
|
1353 |
msgstr "Code postal" |
|
1354 | ||
1355 | 1469 |
#: apps/family/models.py:409 |
1356 | 1470 |
msgid "Family quotient" |
1357 | 1471 |
msgstr "Quotient familial" |
... | ... | |
1570 | 1684 |
msgid "Form identifier" |
1571 | 1685 |
msgstr "Identifiant de la demande" |
1572 | 1686 | |
1573 |
#: apps/jsondatastore/models.py:47
|
|
1687 |
#: apps/jsondatastore/models.py:48
|
|
1574 | 1688 |
msgid "uuid" |
1575 | 1689 |
msgstr "uuid" |
1576 | 1690 | |
1577 |
#: apps/jsondatastore/models.py:50
|
|
1691 |
#: apps/jsondatastore/models.py:51
|
|
1578 | 1692 |
msgid "Content" |
1579 | 1693 |
msgstr "Contenu" |
1580 | 1694 | |
1581 |
#: apps/jsondatastore/models.py:80
|
|
1695 |
#: apps/jsondatastore/models.py:81
|
|
1582 | 1696 |
msgid "Template for \"text\" key value" |
1583 | 1697 |
msgstr "Modèle à appliquer pour la valeur de la clé « text »" |
1584 | 1698 | |
1585 |
#: apps/jsondatastore/models.py:84
|
|
1699 |
#: apps/jsondatastore/models.py:85
|
|
1586 | 1700 |
msgid "JSON Data Store" |
1587 | 1701 |
msgstr "Stockage de données JSON" |
1588 | 1702 | |
1589 |
#: apps/jsondatastore/models.py:87
|
|
1703 |
#: apps/jsondatastore/models.py:88
|
|
1590 | 1704 |
msgid "Listing" |
1591 | 1705 |
msgstr "Liste" |
1592 | 1706 | |
1593 |
#: apps/jsondatastore/models.py:99 |
|
1707 |
#: apps/jsondatastore/models.py:90 |
|
1708 |
msgid "" |
|
1709 |
"More filtering on attributes is possible using \"key=val\" additionals " |
|
1710 |
"parameters" |
|
1711 |
msgstr "" |
|
1712 | ||
1713 |
#: apps/jsondatastore/models.py:94 apps/jsondatastore/models.py:145 |
|
1714 |
#: apps/jsondatastore/models.py:163 |
|
1715 |
msgid "Object identifier" |
|
1716 |
msgstr "Identifiant de l'objet" |
|
1717 | ||
1718 |
#: apps/jsondatastore/models.py:98 |
|
1719 |
#, fuzzy |
|
1720 |
#| msgid "Template for \"text\" key value" |
|
1721 |
msgid "Filter on \"text\" key value" |
|
1722 |
msgstr "Modèle à appliquer pour la valeur de la clé « text »" |
|
1723 | ||
1724 |
#: apps/jsondatastore/models.py:117 |
|
1594 | 1725 |
msgid "Create" |
1595 | 1726 |
msgstr "Créer" |
1596 | 1727 | |
1597 |
#: apps/jsondatastore/models.py:124
|
|
1728 |
#: apps/jsondatastore/models.py:142
|
|
1598 | 1729 |
msgid "Get" |
1599 | 1730 |
msgstr "Récupérer" |
1600 | 1731 | |
1601 |
#: apps/jsondatastore/models.py:125
|
|
1732 |
#: apps/jsondatastore/models.py:143
|
|
1602 | 1733 |
msgid "Replace" |
1603 | 1734 |
msgstr "Remplacer" |
1604 | 1735 | |
1605 |
#: apps/jsondatastore/models.py:126
|
|
1736 |
#: apps/jsondatastore/models.py:144
|
|
1606 | 1737 |
msgid "Update" |
1607 | 1738 |
msgstr "Mise à jour" |
1608 | 1739 | |
1609 |
#: apps/jsondatastore/models.py:127 apps/jsondatastore/models.py:145 |
|
1610 |
msgid "Object identifier" |
|
1611 |
msgstr "Identifiant de l'objet" |
|
1612 | ||
1613 |
#: apps/jsondatastore/models.py:155 |
|
1740 |
#: apps/jsondatastore/models.py:173 |
|
1614 | 1741 |
msgid "Get a single object by attribute" |
1615 | 1742 |
msgstr "Récupérer un objet selon un attribut" |
1616 | 1743 | |
1617 |
#: apps/jsondatastore/models.py:156
|
|
1744 |
#: apps/jsondatastore/models.py:174
|
|
1618 | 1745 |
msgid "Attribute name" |
1619 | 1746 |
msgstr "Nom de l'attribut" |
1620 | 1747 | |
1621 |
#: apps/jsondatastore/models.py:157
|
|
1748 |
#: apps/jsondatastore/models.py:175
|
|
1622 | 1749 |
msgid "Attribute value" |
1623 | 1750 |
msgstr "Valeur de l'attribut" |
1624 | 1751 | |
... | ... | |
2078 | 2205 |
msgstr "Liste des usagers Solis APA liés au name_id" |
2079 | 2206 | |
2080 | 2207 |
#: apps/solis/models.py:310 apps/solis/models.py:322 apps/solis/models.py:367 |
2081 |
#: apps/solis/models.py:575 apps/solis/models.py:587
|
|
2208 |
#: apps/solis/models.py:580 apps/solis/models.py:592
|
|
2082 | 2209 |
msgid "user identifier" |
2083 | 2210 |
msgstr "identifiant de l'utilisateur" |
2084 | 2211 | |
... | ... | |
2112 | 2239 |
msgid "Send data to \"integrationDemandeApa\"" |
2113 | 2240 |
msgstr "Créer une demande APA — envoi vers « integrationDemandeApa »" |
2114 | 2241 | |
2115 |
#: apps/solis/models.py:528
|
|
2242 |
#: apps/solis/models.py:533
|
|
2116 | 2243 |
msgid "" |
2117 | 2244 |
"Create link between name_id and Solis RSA. Payload: name_id, user_id, code" |
2118 | 2245 |
msgstr "" |
2119 | 2246 |
"Créer un lien entre le name_id et un usager Solis RSA (paramètres dans un " |
2120 | 2247 |
"dictionnaire : name_id, user_id et code)" |
2121 | 2248 | |
2122 |
#: apps/solis/models.py:555
|
|
2249 |
#: apps/solis/models.py:560
|
|
2123 | 2250 |
msgid "Delete a Solis RSA link. Payload: name_id, user_id" |
2124 | 2251 |
msgstr "" |
2125 | 2252 |
"Supprimer le lien entre l'usager Solis RSA et le name_id (paramètres dans un " |
2126 | 2253 |
"dictionnaire : name_id et user_id)" |
2127 | 2254 | |
2128 |
#: apps/solis/models.py:572
|
|
2255 |
#: apps/solis/models.py:577
|
|
2129 | 2256 |
msgid "List linked Solis RSA users" |
2130 | 2257 |
msgstr "Liste des usagers Solis RSA liés au name_id" |
2131 | 2258 | |
2132 |
#: apps/solis/models.py:584
|
|
2259 |
#: apps/solis/models.py:589
|
|
2133 | 2260 |
msgid "Get informations about a linked Solis RSA user" |
2134 | 2261 |
msgstr "Renvoie les informations d'un usager Solis RSA lié" |
2135 | 2262 | |
2136 |
#: apps/solis/models.py:591
|
|
2263 |
#: apps/solis/models.py:596
|
|
2137 | 2264 |
msgid "Solis RSA user identifier" |
2138 | 2265 |
msgstr "identifiant de l'usager Solis RSA" |
2139 | 2266 | |
2140 |
#: apps/solis/models.py:595
|
|
2267 |
#: apps/solis/models.py:600
|
|
2141 | 2268 |
msgid "" |
2142 | 2269 |
"individu, actions, allocataires, engagements, evaluations, evenements, " |
2143 | 2270 |
"indus, menages, presences, rdvs" |
... | ... | |
2145 | 2272 |
"individu, actions, allocataires, engagements, evaluations, evenements, " |
2146 | 2273 |
"indus, menages, presences, rdvs" |
2147 | 2274 | |
2148 |
#: apps/solis/models.py:600
|
|
2275 |
#: apps/solis/models.py:605
|
|
2149 | 2276 |
msgid "get a specific item, if applicable" |
2150 | 2277 |
msgstr "renvoie un élément spécifique, si possible" |
2151 | 2278 | |
2152 |
#: apps/solis/models.py:603
|
|
2279 |
#: apps/solis/models.py:608
|
|
2153 | 2280 |
msgid "get linked informations (comma separated list, empty for all)" |
2154 | 2281 |
msgstr "" |
2155 | 2282 |
"renvoie aussi les informations liées (noms des «_links» séparés par une " |
2156 | 2283 |
"virgule, ou vide pour tout récupérer)" |
2157 | 2284 | |
2158 |
#: apps/solis/models.py:607
|
|
2285 |
#: apps/solis/models.py:612
|
|
2159 | 2286 |
msgid "" |
2160 | 2287 |
"filter response (list), ex: idStructure=399 or idStructure!=399," |
2161 | 2288 |
"prescriptionPlacement=Placement" |
... | ... | |
2483 | 2610 |
msgid "Down" |
2484 | 2611 |
msgstr "Hors service" |
2485 | 2612 | |
2486 |
#: base/models.py:865
|
|
2613 |
#: base/models.py:869
|
|
2487 | 2614 |
msgid "Basic authentication username" |
2488 | 2615 |
msgstr "Identifiant d'authentification basique" |
2489 | 2616 | |
2490 |
#: base/models.py:869
|
|
2617 |
#: base/models.py:873
|
|
2491 | 2618 |
msgid "Basic authentication password" |
2492 | 2619 |
msgstr "Mot de passe pour l'authentification basique" |
2493 | 2620 | |
2494 |
#: base/models.py:872
|
|
2621 |
#: base/models.py:876
|
|
2495 | 2622 |
msgid "TLS client certificate" |
2496 | 2623 |
msgstr "Certificat client" |
2497 | 2624 | |
2498 |
#: base/models.py:876
|
|
2625 |
#: base/models.py:880
|
|
2499 | 2626 |
msgid "TLS trusted CAs" |
2500 | 2627 |
msgstr "Autorités de confiance" |
2501 | 2628 | |
2502 |
#: base/models.py:880
|
|
2629 |
#: base/models.py:884
|
|
2503 | 2630 |
msgid "TLS verify certificates" |
2504 | 2631 |
msgstr "Vérification du certificat client" |
2505 | 2632 | |
2506 |
#: base/models.py:885
|
|
2633 |
#: base/models.py:889
|
|
2507 | 2634 |
msgid "HTTP and HTTPS proxy" |
2508 | 2635 |
msgstr "Proxy HTTP et HTTPS" |
2509 | 2636 | |
... | ... | |
2648 | 2775 |
msgid "Update profession:" |
2649 | 2776 |
msgstr "Mettre à jour la profession :" |
2650 | 2777 | |
2651 |
#: contrib/agoraplus/templates/passerelle/contrib/agoraplus/detail.html:44 |
|
2652 |
msgid "Address" |
|
2653 |
msgstr "Adresse" |
|
2654 | ||
2655 | 2778 |
#: contrib/agoraplus/templates/passerelle/contrib/agoraplus/detail.html:46 |
2656 | 2779 |
#: contrib/solis_apa/templates/passerelle/contrib/solis_apa/detail.html:18 |
2657 | 2780 |
msgid "Communes:" |
... | ... | |
3144 | 3267 |
#: contrib/greco/models.py:78 contrib/iparapheur/models.py:86 |
3145 | 3268 |
#: contrib/iparapheur/models.py:87 contrib/maarch/models.py:25 |
3146 | 3269 |
#: contrib/seisin_by_email/models.py:25 contrib/teamnet_axel/models.py:59 |
3147 |
#: contrib/toulouse_axel/models.py:115
|
|
3270 |
#: contrib/toulouse_axel/models.py:119
|
|
3148 | 3271 |
msgid "WSDL URL" |
3149 | 3272 |
msgstr "URL du WSDL" |
3150 | 3273 | |
... | ... | |
3765 | 3888 |
msgid "Get invoice details" |
3766 | 3889 |
msgstr "Récupérer les détails d'une facture" |
3767 | 3890 | |
3768 |
#: contrib/stub_invoices/models.py:82 contrib/stub_invoices/models.py:93
|
|
3769 |
#: contrib/stub_invoices/models.py:107
|
|
3891 |
#: contrib/stub_invoices/models.py:82 contrib/stub_invoices/models.py:94
|
|
3892 |
#: contrib/stub_invoices/models.py:109
|
|
3770 | 3893 |
msgid "Invoice identifier" |
3771 | 3894 |
msgstr "Identifiant de facture" |
3772 | 3895 | |
... | ... | |
3774 | 3897 |
msgid "Get invoice as a PDF file" |
3775 | 3898 |
msgstr "Récupérer une facture au format PDF" |
3776 | 3899 | |
3777 |
#: contrib/stub_invoices/models.py:103 |
|
3900 |
#: contrib/stub_invoices/models.py:90 contrib/stub_invoices/models.py:105 |
|
3901 |
msgid "not yet implemented" |
|
3902 |
msgstr "" |
|
3903 | ||
3904 |
#: contrib/stub_invoices/models.py:104 |
|
3778 | 3905 |
msgid "Pay invoice" |
3779 | 3906 |
msgstr "Payer une facture" |
3780 | 3907 | |
... | ... | |
3864 | 3991 |
msgid "Call stopped:" |
3865 | 3992 |
msgstr "Fin d'appel" |
3866 | 3993 | |
3867 |
#: contrib/toulouse_axel/models.py:116
|
|
3994 |
#: contrib/toulouse_axel/models.py:120
|
|
3868 | 3995 |
msgid "Toulouse Axel WSDL URL" |
3869 | 3996 |
msgstr "URL du WSDL Axel" |
3870 | 3997 | |
3871 |
#: contrib/toulouse_axel/models.py:121
|
|
3998 |
#: contrib/toulouse_axel/models.py:125
|
|
3872 | 3999 |
msgid "Toulouse Axel" |
3873 | 4000 |
msgstr "Toulouse Axel" |
3874 | 4001 | |
3875 |
#: contrib/toulouse_axel/models.py:132
|
|
4002 |
#: contrib/toulouse_axel/models.py:136
|
|
3876 | 4003 |
msgid "Create link between user and Toulouse Axel" |
3877 | 4004 |
msgstr "Lier un compte usager à Toulouse Axel" |
3878 | 4005 | |
3879 |
#: contrib/toulouse_axel/models.py:166 |
|
4006 |
#: contrib/toulouse_axel/models.py:173 |
|
4007 |
#, fuzzy |
|
4008 |
#| msgid "Create link between user and Toulouse Axel" |
|
4009 |
msgid "Delete link between user and Toulouse Axel" |
|
4010 |
msgstr "Lier un compte usager à Toulouse Axel" |
|
4011 | ||
4012 |
#: contrib/toulouse_axel/models.py:202 |
|
3880 | 4013 |
msgid "Get information about user's family" |
3881 | 4014 |
msgstr "Renvoie les informations famille d'un usager" |
3882 | 4015 | |
4016 |
#: contrib/toulouse_axel/models.py:212 |
|
4017 |
#, fuzzy |
|
4018 |
#| msgid "Get information about user's family" |
|
4019 |
msgid "Get information about a child" |
|
4020 |
msgstr "Renvoie les informations famille d'un usager" |
|
4021 | ||
4022 |
#: contrib/toulouse_axel/models.py:216 |
|
4023 |
#, fuzzy |
|
4024 |
#| msgid "Child:" |
|
4025 |
msgid "Child ID" |
|
4026 |
msgstr "Enfant :" |
|
4027 | ||
3883 | 4028 |
#: pbx/models.py:22 |
3884 | 4029 |
msgid "welco URL" |
3885 | 4030 |
msgstr "URL de Welco" |
... | ... | |
3893 | 4038 |
msgstr "L'envoi de messages est limité aux utilisateurs d'API suivants :" |
3894 | 4039 | |
3895 | 4040 |
#: templates/passerelle/base.html:16 templates/passerelle/manage.html:5 |
3896 |
#: views.py:89
|
|
4041 |
#: views.py:90
|
|
3897 | 4042 |
msgid "Web Services" |
3898 | 4043 |
msgstr "Services web" |
3899 | 4044 | |
... | ... | |
4123 | 4268 |
msgid "Incorrect password. Try again." |
4124 | 4269 |
msgstr "Mot de passe incorrect ; essayez à nouveau." |
4125 | 4270 | |
4126 |
#: utils/sftp.py:191
|
|
4271 |
#: utils/sftp.py:193
|
|
4127 | 4272 |
msgid "SSH private key needs a password" |
4128 | 4273 |
msgstr "La clé privée SSH nécessite un mot de passe" |
4129 | 4274 | |
4130 |
#: utils/sftp.py:193
|
|
4275 |
#: utils/sftp.py:195
|
|
4131 | 4276 |
msgid "SSH private key invalid" |
4132 | 4277 |
msgstr "Clé privée SSH invalide" |
4133 | 4278 | |
4279 |
#~ msgid "City code" |
|
4280 |
#~ msgstr "Code de la ville (INSEE)" |
|
4281 | ||
4134 | 4282 |
#~ msgid "Same filter, but case insensitive:" |
4135 | 4283 |
#~ msgstr "Même filtre mais insensible à la casse :" |
4136 | 4284 |
tests/test_base_adresse.py | ||
---|---|---|
6 | 6 |
import utils |
7 | 7 |
import json |
8 | 8 | |
9 |
from requests.exceptions import ConnectionError |
|
10 | ||
9 | 11 |
from django.core.management import call_command |
12 |
from django.core.management.base import CommandError |
|
13 |
from django.utils.six.moves.urllib.parse import urljoin |
|
10 | 14 | |
11 |
from passerelle.apps.base_adresse.models import BaseAdresse, StreetModel |
|
15 |
from passerelle.apps.base_adresse.models import (BaseAdresse, StreetModel, CityModel, |
|
16 |
DepartmentModel, RegionModel) |
|
12 | 17 | |
13 | 18 |
FAKED_CONTENT = json.dumps({ |
14 | 19 |
"limit": 1, |
... | ... | |
41 | 46 | |
42 | 47 |
FAKE_DATA = '' |
43 | 48 | |
49 |
FAKE_API_GEO_LIST = [ |
|
50 |
{ |
|
51 |
"code": "75056", |
|
52 |
"codeDepartement": "75", |
|
53 |
"codeRegion": "11", |
|
54 |
"codesPostaux": [ |
|
55 |
"75001", |
|
56 |
"75002", |
|
57 |
], |
|
58 |
"nom": "Paris", |
|
59 |
"population": 2190327, |
|
60 |
}, |
|
61 |
{ |
|
62 |
"code": "97501", |
|
63 |
"codesPostaux": [ |
|
64 |
"97500" |
|
65 |
], |
|
66 |
"nom": "Miquelon-Langlade", |
|
67 |
"population": 596 |
|
68 |
} |
|
69 |
] |
|
70 | ||
71 |
FAKE_API_GEO = json.dumps(FAKE_API_GEO_LIST) |
|
72 | ||
73 |
FAKE_API_GEO_DEPARTMENTS = json.dumps([ |
|
74 |
{ |
|
75 |
"code": "75", |
|
76 |
"codeRegion": "11", |
|
77 |
"nom": "Paris" |
|
78 |
}, |
|
79 |
{ |
|
80 |
"code": "58", |
|
81 |
"codeRegion": "27", |
|
82 |
"nom": "Nièvre", |
|
83 |
} |
|
84 |
]) |
|
85 | ||
86 |
FAKE_API_GEO_REGIONS = json.dumps([ |
|
87 |
{ |
|
88 |
"code": "11", |
|
89 |
"nom": "Île-de-France" |
|
90 |
}, |
|
91 |
{ |
|
92 |
"code": "27", |
|
93 |
"nom": "Bourgogne-Franche-Comté" |
|
94 |
} |
|
95 |
]) |
|
96 | ||
44 | 97 | |
45 | 98 |
@pytest.fixture |
46 | 99 |
def base_adresse(db): |
... | ... | |
74 | 127 |
citycode=u'73001') |
75 | 128 | |
76 | 129 | |
130 |
@pytest.fixture |
|
131 |
def region(db): |
|
132 |
return RegionModel.objects.create(name=u'Auvergne-Rhône-Alpes', code='84') |
|
133 | ||
134 | ||
135 |
@pytest.fixture |
|
136 |
def department(db, region): |
|
137 |
return DepartmentModel.objects.create(name=u'Savoie', code='73', region=region) |
|
138 | ||
139 | ||
140 |
@pytest.fixture |
|
141 |
def city(db, region, department): |
|
142 |
return CityModel.objects.create(name=u'Chambéry', code='73065', zipcode='73000', |
|
143 |
population=42000, region=region, department=department) |
|
144 | ||
145 | ||
146 |
@pytest.fixture |
|
147 |
def miquelon(db): |
|
148 |
return CityModel.objects.create(name=u'Miquelon-Langlade', code='97501', zipcode='97500', |
|
149 |
population=42) |
|
150 | ||
151 | ||
152 |
@pytest.fixture |
|
153 |
def mock_update_api_geo(): |
|
154 |
with mock.patch('passerelle.apps.base_adresse.models.BaseAdresse.update_api_geo_data', |
|
155 |
new=lambda x: None) as _fixture: |
|
156 |
yield _fixture |
|
157 | ||
158 | ||
159 |
@pytest.fixture |
|
160 |
def mock_update_streets(): |
|
161 |
with mock.patch('passerelle.apps.base_adresse.models.BaseAdresse.update_streets_data', |
|
162 |
new=lambda x: None) as _fixture: |
|
163 |
yield _fixture |
|
164 | ||
165 | ||
77 | 166 |
@mock.patch('passerelle.utils.Request.get') |
78 | 167 |
def test_base_adresse_search(mocked_get, app, base_adresse): |
79 | 168 |
endpoint = utils.generic_endpoint_url('base-adresse', 'search', slug=base_adresse.slug) |
... | ... | |
195 | 284 |
assert len(resp.json['data']) == 0 |
196 | 285 | |
197 | 286 | |
287 |
@pytest.mark.usefixtures('mock_update_api_geo') |
|
198 | 288 |
@mock.patch('passerelle.utils.Request.get') |
199 | 289 |
def test_base_adresse_command_update(mocked_get, db, base_adresse): |
200 | 290 |
filepath = os.path.join(os.path.dirname(__file__), 'data', 'update_streets_test.bz2') |
... | ... | |
214 | 304 |
assert mocked_get.call_count == 2 |
215 | 305 | |
216 | 306 | |
307 |
@pytest.mark.usefixtures('mock_update_api_geo') |
|
217 | 308 |
@mock.patch('passerelle.utils.Request.get') |
218 | 309 |
def test_base_adresse_command_hourly_update(mocked_get, db, base_adresse): |
310 |
base_adresse.update_api_geo_data = lambda: None |
|
219 | 311 |
filepath = os.path.join(os.path.dirname(__file__), 'data', 'update_streets_test.bz2') |
220 | 312 |
mocked_get.return_value = utils.FakedResponse(content=open(filepath).read(), status_code=200) |
221 | 313 |
# check the first hourly job downloads streets |
... | ... | |
227 | 319 |
assert mocked_get.call_count == 1 |
228 | 320 | |
229 | 321 | |
322 |
@pytest.mark.usefixtures('mock_update_api_geo') |
|
230 | 323 |
@mock.patch('passerelle.utils.Request.get') |
231 | 324 |
def test_base_adresse_command_update_97x(mocked_get, db, base_adresse_97x): |
325 |
base_adresse_97x.update_api_geo_data = lambda: None |
|
232 | 326 |
filepath = os.path.join(os.path.dirname(__file__), 'data', 'update_streets_test.bz2') |
233 | 327 |
mocked_get.return_value = utils.FakedResponse(content=open(filepath).read(), status_code=200) |
234 | 328 |
call_command('cron', 'daily') |
... | ... | |
236 | 330 |
assert StreetModel.objects.count() == 2 |
237 | 331 | |
238 | 332 | |
333 |
@pytest.mark.usefixtures('mock_update_api_geo') |
|
239 | 334 |
@mock.patch('passerelle.utils.Request.get') |
240 | 335 |
def test_base_adresse_command_update_corsica(mocked_get, db, base_adresse_corsica): |
336 |
base_adresse_corsica.update_api_geo_data = lambda: None |
|
241 | 337 |
filepath = os.path.join(os.path.dirname(__file__), 'data', 'update_streets_test.bz2') |
242 | 338 |
mocked_get.return_value = utils.FakedResponse(content=open(filepath).read(), status_code=200) |
243 | 339 |
call_command('cron', 'daily') |
... | ... | |
247 | 343 |
assert StreetModel.objects.count() == 0 |
248 | 344 | |
249 | 345 | |
346 |
@pytest.mark.usefixtures('mock_update_api_geo') |
|
250 | 347 |
@mock.patch('passerelle.utils.Request.get') |
251 | 348 |
def test_base_adresse_command_update_multiple(mocked_get, db, base_adresse_multiple): |
349 |
base_adresse_multiple.update_api_geo_data = lambda: None |
|
252 | 350 |
filepath = os.path.join(os.path.dirname(__file__), 'data', 'update_streets_test.bz2') |
253 | 351 |
mocked_get.return_value = utils.FakedResponse(content=open(filepath).read(), status_code=200) |
254 | 352 |
call_command('cron', 'daily') |
... | ... | |
258 | 356 |
mocked_get.assert_any_call('http://bano.openstreetmap.fr/BAN_odbl/BAN_odbl_2A-json.bz2') |
259 | 357 |
mocked_get.assert_any_call('http://bano.openstreetmap.fr/BAN_odbl/BAN_odbl_2B-json.bz2') |
260 | 358 |
assert StreetModel.objects.count() == 5 |
359 | ||
360 | ||
361 |
def test_base_adresse_cities(app, base_adresse, city, department, region): |
|
362 |
resp = app.get('/base-adresse/%s/cities?q=chambe' % base_adresse.slug) |
|
363 |
result = resp.json['data'][0] |
|
364 |
assert result['name'] == city.name |
|
365 |
assert result['text'] == '%s %s' % (city.zipcode, city.name) |
|
366 |
assert result['code'] == city.code |
|
367 |
assert result['zipcode'] == city.zipcode |
|
368 |
assert result['id'] == '%s.%s' % (city.code, city.zipcode) |
|
369 |
assert result['population'] == city.population |
|
370 |
assert result['region_code'] == city.region.code |
|
371 |
assert result['region_name'] == city.region.name |
|
372 |
assert result['department_code'] == city.department.code |
|
373 |
assert result['department_name'] == city.department.name |
|
374 | ||
375 |
resp = app.get('/base-adresse/%s/cities?q=73' % base_adresse.slug) |
|
376 |
assert resp.json['data'][0] == result |
|
377 | ||
378 |
resp = app.get('/base-adresse/%s/cities?code=73065' % base_adresse.slug) |
|
379 |
assert resp.json['data'][0] == result |
|
380 | ||
381 | ||
382 |
def test_base_adresse_cities_missing_region_and_department(app, base_adresse, miquelon): |
|
383 |
resp = app.get('/base-adresse/%s/cities?q=miqu' % base_adresse.slug) |
|
384 |
result = resp.json['data'][0] |
|
385 |
assert result['name'] == miquelon.name |
|
386 |
assert not result['department_code'] |
|
387 |
assert not result['region_code'] |
|
388 |
assert not result['department_name'] |
|
389 |
assert not result['region_name'] |
|
390 | ||
391 | ||
392 |
def test_base_adresse_cities_region_department(app, base_adresse, miquelon, city): |
|
393 |
reg = RegionModel.objects.create(name=u'IdF', code='11') |
|
394 |
dep = DepartmentModel.objects.create(name=u'Paris', code='75', region=reg) |
|
395 |
paris = CityModel.objects.create(name=u'Paris', code='75056', zipcode='75014', |
|
396 |
population=2000000, region=reg, department=dep) |
|
397 | ||
398 |
resp = app.get('/base-adresse/%s/cities?department_code=73' % base_adresse.slug) |
|
399 |
result = resp.json['data'] |
|
400 |
assert len(result) == 1 |
|
401 |
assert result[0]['name'] == city.name |
|
402 | ||
403 |
resp = app.get('/base-adresse/%s/cities?region_code=84' % base_adresse.slug) |
|
404 |
result = resp.json['data'] |
|
405 |
assert len(result) == 1 |
|
406 |
assert result[0]['name'] == city.name |
|
407 | ||
408 |
resp = app.get('/base-adresse/%s/cities?region_code=84&department_code=75' % base_adresse.slug) |
|
409 |
result = resp.json['data'] |
|
410 |
assert not result |
|
411 | ||
412 | ||
413 |
def test_base_adresse_cities_sort_order(app, base_adresse, miquelon, city): |
|
414 |
assert miquelon.population < city.population |
|
415 |
resp = app.get('/base-adresse/%s/cities' % base_adresse.slug) |
|
416 |
result = resp.json['data'] |
|
417 |
assert result[0]['name'] == city.name |
|
418 |
assert result[1]['name'] == miquelon.name |
|
419 | ||
420 | ||
421 |
def test_base_adresse_cities_get_by_id(app, base_adresse, city): |
|
422 |
for i in range(1, 10): |
|
423 |
# create additional cities |
|
424 |
city.pk = None |
|
425 |
city.zipcode = int(city.zipcode) + i |
|
426 |
city.save() |
|
427 | ||
428 |
resp = app.get('/base-adresse/%s/cities?q=cham' % base_adresse.slug) |
|
429 |
result = resp.json['data'][0] |
|
430 |
assert len(resp.json['data']) == 10 |
|
431 |
city_id = result['id'] |
|
432 | ||
433 |
resp = app.get('/base-adresse/%s/cities?id=%s' % (base_adresse.slug, city_id)) |
|
434 |
assert len(resp.json['data']) == 1 |
|
435 |
result2 = resp.json['data'][0] |
|
436 |
assert result2['text'] == result['text'] |
|
437 | ||
438 |
# non integer id. |
|
439 |
resp = app.get('/base-adresse/%s/cities?id=%s' % (base_adresse.slug, 'XXX')) |
|
440 |
assert resp.json['err'] == 1 |
|
441 | ||
442 |
# integer but without match. |
|
443 |
resp = app.get('/base-adresse/%s/cities?id=%s' % (base_adresse.slug, '1.1')) |
|
444 |
assert len(resp.json['data']) == 0 |
|
445 | ||
446 | ||
447 |
def test_base_adresse_departments(app, base_adresse, department, region): |
|
448 |
resp = app.get('/base-adresse/%s/departments?q=sav' % base_adresse.slug) |
|
449 |
result = resp.json['data'][0] |
|
450 |
assert result['name'] == department.name |
|
451 |
assert result['code'] == department.code |
|
452 |
assert result['id'] == department.code |
|
453 |
assert result['text'] == '%s %s' % (department.code, department.name) |
|
454 |
assert result['region_code'] == region.code |
|
455 |
assert result['region_name'] == region.name |
|
456 | ||
457 |
resp = app.get('/base-adresse/%s/departments?q=73' % base_adresse.slug) |
|
458 |
result = resp.json['data'][0] |
|
459 |
assert result['name'] == department.name |
|
460 | ||
461 |
resp = app.get('/base-adresse/%s/departments?id=%s' % (base_adresse.slug, department.code)) |
|
462 |
result = resp.json['data'][0] |
|
463 |
assert result['name'] == department.name |
|
464 | ||
465 | ||
466 |
def test_base_adresse_departments_region(app, base_adresse, department): |
|
467 |
reg = RegionModel.objects.create(name=u'IdF', code='11') |
|
468 |
paris = DepartmentModel.objects.create(name=u'Paris', code='75', region=reg) |
|
469 | ||
470 |
resp = app.get('/base-adresse/%s/departments?region_code=84' % base_adresse.slug) |
|
471 |
result = resp.json['data'] |
|
472 |
assert len(result) == 1 |
|
473 |
assert result[0]['name'] == department.name |
|
474 | ||
475 | ||
476 |
def test_base_adresse_regions(app, base_adresse, region): |
|
477 |
resp = app.get('/base-adresse/%s/regions?q=au' % base_adresse.slug) |
|
478 |
result = resp.json['data'][0] |
|
479 |
assert result['name'] == region.name |
|
480 |
assert result['code'] == region.code |
|
481 |
assert result['id'] == region.code |
|
482 |
assert result['text'] == '%s %s' % (region.code, region.name) |
|
483 | ||
484 |
resp = app.get('/base-adresse/%s/regions?id=%s' % (base_adresse.slug, region.code)) |
|
485 |
result = resp.json['data'][0] |
|
486 |
assert result['name'] == region.name |
|
487 | ||
488 | ||
489 |
@pytest.mark.usefixtures('mock_update_streets') |
|
490 |
@mock.patch('passerelle.utils.Request.get') |
|
491 |
def test_base_adresse_command_update_geo(mocked_get, db, base_adresse): |
|
492 |
return_values = [utils.FakedResponse(content=content, status_code=200) |
|
493 |
for content in (FAKE_API_GEO_REGIONS, FAKE_API_GEO_DEPARTMENTS, FAKE_API_GEO)] |
|
494 |
mocked_get.side_effect = return_values |
|
495 |
call_command('cron', 'daily') |
|
496 |
assert mocked_get.call_count == 3 |
|
497 |
mocked_get.assert_any_call(urljoin(base_adresse.api_geo_url, 'communes')) |
|
498 |
mocked_get.assert_any_call(urljoin(base_adresse.api_geo_url, 'regions')) |
|
499 |
mocked_get.assert_any_call(urljoin(base_adresse.api_geo_url, 'departements')) |
|
500 | ||
501 |
regions = RegionModel.objects.all() |
|
502 |
assert regions.count() == 2 |
|
503 |
idf = regions.get(name='Île-de-France') |
|
504 |
assert idf.code == '11' |
|
505 |
centre = regions.get(name='Bourgogne-Franche-Comté') |
|
506 |
assert centre.code == '27' |
|
507 | ||
508 |
departments = DepartmentModel.objects.all() |
|
509 |
assert departments.count() == 2 |
|
510 |
paris_dep = departments.get(name='Paris') |
|
511 |
assert paris_dep.code == '75' |
|
512 |
assert paris_dep.region == idf |
|
513 |
nievre = departments.get(name='Nièvre') |
|
514 |
assert nievre.code == '58' |
|
515 |
assert nievre.region == centre |
|
516 | ||
517 |
cities = CityModel.objects.all() |
|
518 |
assert cities.count() == 3 |
|
519 |
paris = cities.get(zipcode='75001') |
|
520 |
assert paris.name == 'Paris' |
|
521 |
assert paris.code == '75056' |
|
522 |
assert paris.population == 2190327 |
|
523 |
assert paris.department.code == '75' |
|
524 |
assert paris.region.code == '11' |
|
525 | ||
526 |
paris2 = cities.get(zipcode='75002') |
|
527 |
paris_json = paris.to_json() |
|
528 |
for key, value in paris2.to_json().items(): |
|
529 |
if not key in ['id', 'text', 'zipcode']: |
|
530 |
assert paris_json[key] == value |
|
531 | ||
532 |
miquelon = cities.get(zipcode='97500') |
|
533 |
assert miquelon.name == 'Miquelon-Langlade' |
|
534 |
assert miquelon.code == '97501' |
|
535 |
assert miquelon.population == 596 |
|
536 |
assert not miquelon.department |
|
537 |
assert not miquelon.region |
|
538 | ||
539 |
# check a new call downloads again |
|
540 |
mocked_get.side_effect = return_values |
|
541 |
call_command('cron', 'daily') |
|
542 |
assert mocked_get.call_count == 6 |
|
543 |
# and doesn't delete anything |
|
544 |
assert CityModel.objects.count() == 3 |
|
545 |
assert DepartmentModel.objects.count() == 2 |
|
546 |
assert RegionModel.objects.count() == 2 |
|
547 | ||
548 | ||
549 |
@pytest.mark.usefixtures('mock_update_streets') |
|
550 |
@mock.patch('passerelle.utils.Request.get') |
|
551 |
def test_base_adresse_command_update_geo_delete(mocked_get, db, base_adresse): |
|
552 |
return_values = [utils.FakedResponse(content=content, status_code=200) |
|
553 |
for content in (FAKE_API_GEO_REGIONS, FAKE_API_GEO_DEPARTMENTS, FAKE_API_GEO)] |
|
554 |
mocked_get.side_effect = return_values |
|
555 |
call_command('cron', 'daily') |
|
556 |
assert CityModel.objects.count() == 3 |
|
557 | ||
558 |
new_fake_api_geo = json.dumps([FAKE_API_GEO_LIST[1]]) |
|
559 |
return_values = [utils.FakedResponse(content=content, status_code=200) |
|
560 |
for content in (FAKE_API_GEO_REGIONS, FAKE_API_GEO_DEPARTMENTS, new_fake_api_geo)] |
|
561 |
mocked_get.side_effect = return_values |
|
562 |
call_command('cron', 'daily') |
|
563 |
assert CityModel.objects.count() == 1 |
|
564 | ||
565 | ||
566 |
@pytest.mark.usefixtures('mock_update_streets') |
|
567 |
@mock.patch('passerelle.utils.Request.get') |
|
568 |
def test_base_adresse_command_hourly_update_geo(mocked_get, db, base_adresse): |
|
569 |
return_values = [utils.FakedResponse(content=content, status_code=200) |
|
570 |
for content in (FAKE_API_GEO_REGIONS, FAKE_API_GEO_DEPARTMENTS, FAKE_API_GEO)] |
|
571 |
mocked_get.side_effect = return_values |
|
572 |
# check the first hourly job downloads data |
|
573 |
call_command('cron', 'hourly') |
|
574 |
assert mocked_get.call_count == 3 |
|
575 |
assert CityModel.objects.count() == 3 |
|
576 |
# check a second call doesn't download anything |
|
577 |
call_command('cron', 'hourly') |
|
578 |
assert mocked_get.call_count == 3 |
|
579 | ||
580 | ||
581 |
@pytest.mark.usefixtures('mock_update_streets') |
|
582 |
@mock.patch('passerelle.utils.Request.get') |
|
583 |
def test_base_adresse_command_update_geo_invalid(mocked_get, db, base_adresse): |
|
584 |
mocked_get.return_value = utils.FakedResponse(content='{}', status_code=200) |
|
585 |
with pytest.raises(CommandError): |
|
586 |
call_command('cron', 'daily') |
|
587 |
assert mocked_get.call_count == 1 |
|
588 |
assert not RegionModel.objects.exists() |
|
589 | ||
590 |
mocked_get.return_value = utils.FakedResponse(content=FAKE_API_GEO, status_code=500) |
|
591 |
call_command('cron', 'daily') |
|
592 |
assert mocked_get.call_count == 4 |
|
593 |
assert not RegionModel.objects.exists() |
|
594 | ||
595 |
mocked_get.return_value = utils.FakedResponse(content='not-json', status_code=200) |
|
596 |
call_command('cron', 'daily') |
|
597 |
assert mocked_get.call_count == 7 |
|
598 |
assert not RegionModel.objects.exists() |
|
599 | ||
600 | ||
601 |
@pytest.mark.usefixtures('mock_update_streets') |
|
602 |
@mock.patch('passerelle.utils.Request.get', side_effect=ConnectionError) |
|
603 |
def test_base_adresse_command_update_geo_no_connection(mocked_get, db, base_adresse): |
|
604 |
call_command('cron', 'daily') |
|
605 |
assert mocked_get.call_count == 3 |
|
606 |
assert not RegionModel.objects.exists() |
|
261 |
- |