Projet

Général

Profil

0001-base_adresse-add-addresses-endpoint-39387.patch

Valentin Deniaud, 30 janvier 2020 14:04

Télécharger (8,57 ko)

Voir les différences:

Subject: [PATCH] base_adresse: add /addresses/ endpoint (#39387)

Compatible with wcs API.
 .../migrations/0016_addresscachemodel.py      |  25 +++++
 passerelle/apps/base_adresse/models.py        | 101 +++++++++++++-----
 2 files changed, 98 insertions(+), 28 deletions(-)
 create mode 100644 passerelle/apps/base_adresse/migrations/0016_addresscachemodel.py
passerelle/apps/base_adresse/migrations/0016_addresscachemodel.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2020-01-30 11:34
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations, models
6
import jsonfield.fields
7

  
8

  
9
class Migration(migrations.Migration):
10

  
11
    dependencies = [
12
        ('base_adresse', '0015_auto_20191206_1244'),
13
    ]
14

  
15
    operations = [
16
        migrations.CreateModel(
17
            name='AddressCacheModel',
18
            fields=[
19
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20
                ('api_id', models.CharField(max_length=30, unique=True)),
21
                ('data', jsonfield.fields.JSONField(default=dict)),
22
                ('timestamp', models.DateTimeField(auto_now=True)),
23
            ],
24
        ),
25
    ]
passerelle/apps/base_adresse/models.py
1 1
import bz2
2
import datetime
2 3
import unicodedata
3 4

  
5
from jsonfield import JSONField
4 6
from requests import RequestException
5 7

  
6 8
from django.db import connection, models
......
49 51
    class Meta:
50 52
        verbose_name = _('Base Adresse Web Service')
51 53

  
54
    @staticmethod
55
    def format_address_data(data):
56
        result = {}
57
        result['lon'] = str(data['geometry']['coordinates'][0])
58
        result['lat'] = str(data['geometry']['coordinates'][1])
59
        result['address'] = {'country': 'France'}
60
        for prop, value in data['properties'].items():
61
            if prop in ('city', 'postcode', 'citycode'):
62
                result['address'][prop] = value
63
            elif prop == 'housenumber':
64
                result['address']['house_number'] = value
65
            elif prop == 'label':
66
                result['text'] = result['display_name'] = value
67
            elif prop == 'name':
68
                house_number = data['properties'].get('housenumber')
69
                if house_number and value.startswith(house_number):
70
                    value = value[len(house_number):].strip()
71
                result['address']['road'] = value
72
            elif prop == 'id':
73
                result['id'] = value
74
        return result
75

  
52 76
    @endpoint(pattern='(?P<q>.+)?$',
53 77
              description=_('Geocoding'),
54 78
              parameters={
55
                  'q': {'description': _('Address'), 'example_value': '169 rue du chateau, paris'}
79
                  'id': {'description': _('Address identifier')},
80
                  'q': {'description': _('Address'), 'example_value': '169 rue du chateau, paris'},
81
                  'page_limit': {'description': _('Maximum number of results to return. Must be '
82
                                                  'lower than 20.')},
83
                  'zipcode': {'description': _('Zipcode'), 'example_value': '75014'},
84
                  'lat': {'description': _('Prioritize results according to coordinates. "lat" '
85
                                           'parameter must be present.')},
86
                  'lon': {'description': _('Prioritize results according to coordinates. "lon" '
87
                                           'parameter must be present.')},
56 88
              })
57
    def search(self, request, q, zipcode='', lat=None, lon=None, **kwargs):
89
    def addresses(self, request, id=None, q=None, zipcode='', lat=None, lon=None,
90
               page_limit=5, **kwargs):
58 91
        if kwargs.get('format', 'json') != 'json':
59 92
            raise NotImplementedError()
93
        if id is not None:
94
            try:
95
                address = AddressCacheModel.objects.get(api_id=id)
96
            except AddressCacheModel.DoesNotExist:
97
                return {'err': _('Address ID not found')}
98
            address.update_timestamp()
99
            return {'data': [address.data]}
60 100

  
61 101
        if not q:
62
            return []
102
            return {'data': []}
103

  
104
        if page_limit > 20:
105
            page_limit = 20
63 106

  
64 107
        scheme, netloc, path, params, query, fragment = urlparse.urlparse(self.service_url)
65 108
        path = urlparse.urljoin(path, 'search/')
66
        query_args = {'q': q, 'limit': 1}
109
        query_args = {'q': q, 'limit': page_limit}
67 110
        if zipcode:
68 111
            query_args['postcode'] = zipcode
69 112
        if lat and lon:
......
78 121
        for feature in result_response.json().get('features'):
79 122
            if not feature['geometry']['type'] == 'Point':
80 123
                continue  # skip unknown
81
            result.append({
82
                'lon': str(feature['geometry']['coordinates'][0]),
83
                'lat': str(feature['geometry']['coordinates'][1]),
84
                'display_name': feature['properties']['label'],
85
            })
86
            break
124
            data = self.format_address_data(feature)
125
            result.append(data)
126
            AddressCacheModel.objects.update_or_create(api_id=data['id'], data=data)
87 127

  
88
        return result
128
        return {'data': result}
129

  
130
    @endpoint(pattern='(?P<q>.+)?$', description=_('Deprecated, use /addresses/'),)
131
    def search(self, request, q, zipcode='', lat=None, lon=None, **kwargs):
132
        result = self.addresses(request, q=q, zipcode=zipcode, lat=lat, lon=lon, page_limit=1, **kwargs)
133
        return result['data']
89 134

  
90 135
    @endpoint(description=_('Reverse geocoding'),
91 136
              parameters={
......
107 152
        for feature in result_response.json().get('features'):
108 153
            if not feature['geometry']['type'] == 'Point':
109 154
                continue  # skip unknown
110
            result = {}
111
            result['lon'] = str(feature['geometry']['coordinates'][0])
112
            result['lat'] = str(feature['geometry']['coordinates'][1])
113
            result['address'] = {'country': 'France'}
114
            for prop in feature['properties']:
115
                if prop in ('city', 'postcode', 'citycode'):
116
                    result['address'][prop] = feature['properties'][prop]
117
                elif prop == 'housenumber':
118
                    result['address']['house_number'] = feature['properties'][prop]
119
                elif prop == 'label':
120
                    result['display_name'] = feature['properties'][prop]
121
                elif prop == 'name':
122
                    house_number = feature['properties'].get('housenumber')
123
                    value = feature['properties'][prop]
124
                    if house_number and value.startswith(house_number):
125
                        value = value[len(house_number):].strip()
126
                    result['address']['road'] = value
155
            result = self.format_address_data(feature)
127 156
        return result
128 157

  
129 158
    @endpoint(description=_('Streets from zipcode'),
......
368 397
                    code=data['code'], zipcode=zipcode, defaults=defaults)
369 398
        CityModel.objects.filter(last_update__lt=start_update).delete()
370 399

  
400
    def clean_addresses_cache(self):
401
        old_addresses = AddressCacheModel.objects.filter(
402
            timestamp__lt=timezone.now() - datetime.timedelta(hours=1)
403
        )
404
        old_addresses.delete()
405

  
371 406
    def hourly(self):
372 407
        super(BaseAdresse, self).hourly()
408
        self.clean_addresses_cache()
373 409
        # don't wait for daily job to grab data
374 410
        if self.get_zipcodes() and not self.get_streets_queryset().exists():
375 411
            self.update_streets_data()
......
489 525

  
490 526
    def __str__(self):
491 527
        return '%s %s' % (self.zipcode, self.name)
528

  
529

  
530
class AddressCacheModel(models.Model):
531
    api_id = models.CharField(max_length=30, unique=True)
532
    data = JSONField()
533
    timestamp = models.DateTimeField(auto_now=True)
534

  
535
    def update_timestamp(self):
536
        self.save()
492
-