0001-general-add-generic-endpoint-view-11204.patch
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>&format=json</li>
|
|
24 |
<li>{% trans 'Reverse geocoding:' %} <a href="{% url 'base_adresse-reverse' slug=object.slug %}?lat=48.833708&lon=2.323349&format=json"
|
|
25 |
>{{ site_base_uri }}{% url 'base_adresse-reverse' slug=object.slug %}</a>?lat=<i>lat</i>&lon=<i>lon</i>&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>&format=json</li>
|
|
24 |
<li>{% trans 'Reverse geocoding:' %} <a href="reverse?lat=48.833708&lon=2.323349&format=json"
|
|
25 |
>{{ site_base_uri }}{{ object.get_absolute_url }}reverse</a>?lat=<i>lat</i>&lon=<i>lon</i>&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 |
- |