Projet

Général

Profil

0001-general-add-generic-endpoint-view-11204.patch

Frédéric Péters, 05 juin 2016 10:07

Télécharger (13,8 ko)

Voir les différences:

Subject: [PATCH] general: add generic endpoint view (#11204)

 passerelle/apps/base_adresse/models.py             |  66 +++++++++++++
 .../base_adresse/base_adresse_detail.html          |   8 +-
 passerelle/apps/base_adresse/urls.py               |   3 -
 passerelle/apps/base_adresse/views.py              | 106 +++------------------
 passerelle/urls.py                                 |   7 +-
 passerelle/views.py                                |  27 +++++-
 6 files changed, 111 insertions(+), 106 deletions(-)
passerelle/apps/base_adresse/models.py
1
import requests
2
import urllib
3
import urlparse
4

  
1 5
from django.core.urlresolvers import reverse
2 6
from django.db import models
3 7
from django.utils.translation import ugettext_lazy as _
4 8

  
5 9
from passerelle.base.models import BaseResource
10
from passerelle.utils.api import endpoint
6 11

  
7 12

  
8 13
class BaseAdresse(BaseResource):
......
22 27
    @classmethod
23 28
    def get_icon_class(cls):
24 29
        return 'gis'
30

  
31
    @endpoint
32
    def search(self, request, q, format='json'):
33
        if format != 'json':
34
            raise NotImplementedError()
35

  
36
        scheme, netloc, path, params, query, fragment = urlparse.urlparse(self.service_url)
37
        path = '/search/'
38
        query = urllib.urlencode({'q': q, 'limit': 1})
39
        url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
40

  
41
        result_response = requests.get(url)
42
        result = []
43

  
44
        for feature in result_response.json().get('features'):
45
            if not feature['geometry']['type'] == 'Point':
46
                continue # skip unknown
47
            result.append({
48
                'lon': str(feature['geometry']['coordinates'][0]),
49
                'lat': str(feature['geometry']['coordinates'][1]),
50
                'display_name': feature['properties']['label'],
51
            })
52
            break
53

  
54
        return result
55

  
56
    @endpoint
57
    def reverse(self, request, lat, lon, format='json'):
58
        response_format = 'json'
59
        if format != 'json':
60
            raise NotImplementedError()
61

  
62
        scheme, netloc, path, params, query, fragment = urlparse.urlparse(self.service_url)
63
        path = '/reverse/'
64
        query = urllib.urlencode({'lat': lat, 'lon': lon})
65
        url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
66

  
67
        result_response = requests.get(url)
68
        result = None
69

  
70
        for feature in result_response.json().get('features'):
71
            if not feature['geometry']['type'] == 'Point':
72
                continue # skip unknown
73
            result = {}
74
            result['lon'] = str(feature['geometry']['coordinates'][0])
75
            result['lat'] = str(feature['geometry']['coordinates'][1])
76
            result['address'] = {'country': 'France'}
77
            for prop in feature['properties']:
78
                if prop in ('city', 'postcode'):
79
                    result['address'][prop] = feature['properties'][prop]
80
                elif prop == 'housenumber':
81
                    result['address']['house_number'] = feature['properties'][prop]
82
                elif prop == 'label':
83
                    result['display_name'] = feature['properties'][prop]
84
                elif prop == 'name':
85
                    house_number = feature['properties'].get('housenumber')
86
                    value = feature['properties'][prop]
87
                    if house_number and value.startswith(house_number):
88
                        value = value[len(house_number):].strip()
89
                    result['address']['road'] = value
90
        return result
passerelle/apps/base_adresse/templates/base_adresse/base_adresse_detail.html
19 19
</p>
20 20

  
21 21
<ul>
22
<li>{% trans 'Geocoding:' %} <a href="{% url 'base_adresse-search' slug=object.slug %}?q=169 rue du chateau, paris&format=json"
23
        >{{ site_base_uri }}{% url 'base_adresse-search' slug=object.slug %}</a>?q=<i>q</i>&amp;format=json</li>
24
<li>{% trans 'Reverse geocoding:' %} <a href="{% url 'base_adresse-reverse' slug=object.slug %}?lat=48.833708&amp;lon=2.323349&amp;format=json"
25
        >{{ site_base_uri }}{% url 'base_adresse-reverse' slug=object.slug %}</a>?lat=<i>lat</i>&amp;lon=<i>lon</i>&amp;format=json</li>
22
<li>{% trans 'Geocoding:' %} <a href="search?q=169 rue du chateau, paris&format=json"
23
  >{{ site_base_uri }}{{ object.get_absolute_url }}search</a>?q=<i>q</i>&amp;format=json</li>
24
<li>{% trans 'Reverse geocoding:' %} <a href="reverse?lat=48.833708&amp;lon=2.323349&amp;format=json"
25
  >{{ site_base_uri }}{{ object.get_absolute_url }}reverse</a>?lat=<i>lat</i>&amp;lon=<i>lon</i>&amp;format=json</li>
26 26
</ul>
27 27
</div>
28 28

  
passerelle/apps/base_adresse/urls.py
7 7

  
8 8
urlpatterns = patterns('',
9 9
    url(r'^(?P<slug>[\w,-]+)/$', BaseAdresseDetailView.as_view(), name='base_adresse-view'),
10

  
11
    url(r'^(?P<slug>[\w,-]+)/search$', SearchView.as_view(), name='base_adresse-search'),
12 10
    url(r'^(?P<slug>[\w,-]+)/search/(?P<path>.*)$', SearchPathView.as_view(), name='base_adresse-path-search'),
13
    url(r'^(?P<slug>[\w,-]+)/reverse$', ReverseView.as_view(), name='base_adresse-reverse'),
14 11
)
passerelle/apps/base_adresse/views.py
1
import requests
2
import urllib
3
import urlparse
4

  
5
from django.http import HttpResponseBadRequest
6
from django.views.generic.base import View
7
from django.views.generic.detail import SingleObjectMixin, DetailView
8

  
9
from passerelle import utils
10

  
1
from django.views.generic.detail import DetailView
2
from passerelle.views import GenericEndpointView
11 3
from .models import BaseAdresse
12 4

  
13 5

  
......
20 12
        return context
21 13

  
22 14

  
23
class SearchView(View, SingleObjectMixin):
24
    model = BaseAdresse
25

  
26
    def get_query(self, request, *args, **kwargs):
27
        if not 'q' in request.GET:
28
            return HttpResponseBadRequest('missing parameter')
29
        return request.GET['q']
30

  
31
    def get(self, request, *args, **kwargs):
32
        response_format = 'json'
33
        if 'format' in request.GET:
34
            response_format = request.GET['format']
35
        if response_format != 'json':
36
            raise NotImplementedError()
37

  
38
        scheme, netloc, path, params, query, fragment = urlparse.urlparse(
39
                self.get_object().service_url)
40
        path = '/search/'
41
        query = urllib.urlencode({'q': self.get_query(request, *args, **kwargs), 'limit': 1})
42
        url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
43

  
44
        result_response = requests.get(url)
45
        result = []
46

  
47
        for feature in result_response.json().get('features'):
48
            if not feature['geometry']['type'] == 'Point':
49
                continue # skip unknown
50
            result.append({
51
                'lon': str(feature['geometry']['coordinates'][0]),
52
                'lat': str(feature['geometry']['coordinates'][1]),
53
                'display_name': feature['properties']['label'],
54
            })
55
            break
15
class SearchPathView(GenericEndpointView):
16
    def get_connector(self, **kwargs):
17
        return 'base_adresse'
56 18

  
57
        return utils.response_for_json(request, result)
58

  
59

  
60
class SearchPathView(SearchView):
61
    def get_query(self, request, *args, **kwargs):
62
        return kwargs.get('path')
63

  
64

  
65
class ReverseView(View, SingleObjectMixin):
66
    model = BaseAdresse
19
    def get_params(self, request, *args, **kwargs):
20
        params = super(SearchPathView, self).get_params(request, *args, **kwargs)
21
        params['q'] = kwargs.pop('path')
22
        return params
67 23

  
68 24
    def get(self, request, *args, **kwargs):
69
        response_format = 'json'
70
        if 'format' in request.GET:
71
            response_format = request.GET['format']
72
        if response_format != 'json':
73
            raise NotImplementedError()
74

  
75
        if not 'lat' in request.GET or not 'lon' in request.GET:
76
            return HttpResponseBadRequest('missing parameter')
77

  
78
        lat = request.GET['lat']
79
        lon = request.GET['lon']
80

  
81
        scheme, netloc, path, params, query, fragment = urlparse.urlparse(
82
                self.get_object().service_url)
83
        path = '/reverse/'
84
        query = urllib.urlencode({'lat': lat, 'lon': lon})
85
        url = urlparse.urlunparse((scheme, netloc, path, params, query, fragment))
86

  
87
        result_response = requests.get(url)
88
        result = None
89

  
90
        for feature in result_response.json().get('features'):
91
            if not feature['geometry']['type'] == 'Point':
92
                continue # skip unknown
93
            result = {}
94
            result['lon'] = str(feature['geometry']['coordinates'][0])
95
            result['lat'] = str(feature['geometry']['coordinates'][1])
96
            result['address'] = {'country': 'France'}
97
            for prop in feature['properties']:
98
                if prop in ('city', 'postcode'):
99
                    result['address'][prop] = feature['properties'][prop]
100
                elif prop == 'housenumber':
101
                    result['address']['house_number'] = feature['properties'][prop]
102
                elif prop == 'label':
103
                    result['display_name'] = feature['properties'][prop]
104
                elif prop == 'name':
105
                    house_number = feature['properties'].get('housenumber')
106
                    value = feature['properties'][prop]
107
                    if house_number and value.startswith(house_number):
108
                        value = value[len(house_number):].strip()
109
                    result['address']['road'] = value
110
        return utils.response_for_json(request, result)
25
        kwargs['endpoint'] = 'search'
26
        return super(SearchPathView, self).get(request, *args, **kwargs)
passerelle/urls.py
8 8

  
9 9
from .views import (HomePageView, ManageView, ManageAddView,
10 10
        GenericCreateConnectorView, GenericDeleteConnectorView,
11
        GenericEditConnectorView,
11
        GenericEditConnectorView, GenericEndpointView,
12 12
        LEGACY_APPS_PATTERNS, LegacyPageView, login, logout)
13 13
from .urls_utils import decorated_includes, required, app_enabled
14 14
from .base.urls import access_urlpatterns
......
74 74
        )
75 75

  
76 76
urlpatterns += patterns('',
77
    url(r'^(?P<connector>[\w,-]+)/(?P<slug>[\w,-]+)/(?P<endpoint>[\w,-]+)$',
78
        GenericEndpointView.as_view())
79
)
80

  
81
urlpatterns += patterns('',
77 82
    url(r'^manage/(?P<connector>[\w,-]+)/', decorated_includes(login_required,
78 83
        include(patterns('',
79 84
            url(r'^add$',
passerelle/views.py
1 1
from django.apps import apps
2 2
from django.contrib.auth import logout as auth_logout
3 3
from django.contrib.auth import views as auth_views
4
from django.http import HttpResponseRedirect, Http404
5
from django.views.generic import TemplateView, CreateView, DeleteView, UpdateView
4
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect, Http404
5
from django.views.generic import View, TemplateView, CreateView, DeleteView, UpdateView
6
from django.views.generic.detail import SingleObjectMixin
6 7
from django.conf import settings
7 8
from django.db import models
8 9
from django.shortcuts import resolve_url
......
17 18

  
18 19
from passerelle.base.models import BaseResource
19 20

  
21
from .utils import response_for_json
20 22
from .forms import GenericConnectorForm
21 23

  
22 24

  
......
76 78

  
77 79

  
78 80
class GenericConnectorMixin(object):
81
    def get_connector(self, **kwargs):
82
        return kwargs.get('connector')
83

  
79 84
    def dispatch(self, request, *args, **kwargs):
80
        connector = kwargs.get('connector')
85
        connector = self.get_connector(**kwargs)
81 86
        try:
82 87
            app = apps.get_app_config(connector.replace('-', '_'))
83 88
        except LookupError:
......
107 112
        return reverse('manage-home')
108 113

  
109 114

  
115
class GenericEndpointView(GenericConnectorMixin, SingleObjectMixin, View):
116
    def get_params(self, request, *args, **kwargs):
117
        return dict([(x, request.GET[x]) for x in request.GET.keys()])
118

  
119
    def get(self, request, *args, **kwargs):
120
        endpoint = getattr(self.get_object(), kwargs.get('endpoint'), None)
121
        endpoint_info = getattr(endpoint, 'endpoint_info', None)
122
        if endpoint_info is None:
123
            raise Http404()
124
        try:
125
            result = endpoint(request, **self.get_params(request, *args, **kwargs))
126
        except TypeError as e:
127
            return HttpResponseBadRequest(unicode(e))
128
        return response_for_json(request, result)
129

  
130

  
110 131
# legacy
111 132
LEGACY_APPS_PATTERNS = {
112 133
    'datasources': {'url': 'data', 'name': 'Data Sources'},
113
-