Use custom pager instead of Suq-Pagination. Don't request the first GeoJSON items...
[infos-pratiques:etalage.git] / poiscasse / controllers.py
1 # -*- coding: utf-8 -*-
2
3
4 # PoisCasse -- Open Data POIs portal
5 # By: Emmanuel Raviart <eraviart@easter-eggs.com>
6 #     Romain Soufflet <rsoufflet@easter-eggs.com>
7 #
8 # Copyright (C) 2011 Easter-eggs
9 # http://gitorious.org/infos-pratiques/poiscasse
10 #
11 # This file is part of PoisCasse.
12 #
13 # PoisCasse is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU Affero General Public License as
15 # published by the Free Software Foundation, either version 3 of the
16 # License, or (at your option) any later version.
17 #
18 # PoisCasse is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 # GNU Affero General Public License for more details.
22 #
23 # You should have received a copy of the GNU Affero General Public License
24 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
25
26
27 """Controllers for territories"""
28
29
30 import datetime
31 import itertools
32 import logging
33 import simplejson as json
34 import urlparse
35
36 from biryani import strings
37
38 from . import contexts, conf, conv, model, pois, ramdb, templates, urls, wsgihelpers
39
40
41 log = logging.getLogger(__name__)
42 N_ = lambda message: message
43
44
45 @wsgihelpers.wsgify
46 def about(req):
47     ctx = contexts.Ctx(req)
48
49     params = req.GET
50     init_ctx(ctx, params)
51     return templates.render(ctx, '/about.mako')
52
53
54 @wsgihelpers.wsgify
55 @ramdb.ramdb_based
56 def autocomplete_category(req):
57     ctx = contexts.Ctx(req)
58     ctx.controller_name = 'autocomplete_category'
59
60     headers = []
61     params = req.GET
62     params = dict(
63         context = params.get('context'),
64         jsonp = params.get('jsonp'),
65         page = params.get('page'),
66         tag = params.getall('tag'),
67         term = params.get('term'),
68         )
69     data, errors = conv.pipe(
70         conv.struct(
71             dict(
72                 page = conv.pipe(
73                     conv.str_to_int,
74                     conv.make_greater_or_equal(1),
75                     conv.default(1),
76                     ),
77                 tag = conv.uniform_sequence(conv.str_to_category_slug),
78                 term = conv.make_str_to_slug(separator = u' ', transform = strings.upper),
79                 ),
80             default = 'ignore',
81             ),
82         conv.rename_item('page', 'page_number'),
83         conv.rename_item('tag', 'tags_slug'),
84         )(params, state = ctx)
85     if errors is not None:
86         raise wsgihelpers.respond_json(ctx,
87             dict(
88                 apiVersion = '1.0',
89                 context = params['context'],
90                 error = dict(
91                     code = 400, # Bad Request
92                     errors = [
93                         dict(
94                             location = key,
95                             message = error,
96                             )
97                         for key, error in sorted(errors.iteritems())
98                         ],
99                     # message will be automatically defined.
100                     ),
101                 method = ctx.controller_name,
102                 params = params,
103                 ),
104             headers = headers,
105             jsonp = params['jsonp'],
106             )
107
108     page_size = 20
109     categories_json = [
110         ramdb.categories_by_slug[category_slug].name
111         for category_slug in itertools.islice(
112             sorted(ramdb.iter_categories_slug(
113                 tags_slug = data.get('tags_slug'),
114                 term = data.get('term'),
115                 )),
116             (data['page_number'] - 1) * page_size,
117             data['page_number'] * page_size,
118             )
119         ]
120     return wsgihelpers.respond_json(ctx,
121         dict(
122             apiVersion = '1.0',
123             context = params['context'],
124             data = dict(
125                 currentItemCount = len(categories_json),
126                 items = categories_json,
127                 itemsPerPage = page_size,
128                 pageIndex = data['page_number'],
129                 startIndex = (data['page_number'] - 1) * page_size + 1,
130                 # totalItems = pager.item_count,
131                 # totalPages = pager.page_count,
132                 ),
133             method = ctx.controller_name,
134             params = params,
135             ),
136         headers = headers,
137         jsonp = params['jsonp'],
138         )
139
140
141 @wsgihelpers.wsgify
142 @ramdb.ramdb_based
143 def geojson(req):
144     ctx = contexts.Ctx(req)
145
146     params = req.GET
147     init_ctx(ctx, params)
148     params = dict(
149         category = params.get('category'),
150         jsonp = params.get('jsonp'),
151         page = params.get('page'),
152         term = params.get('term'),
153         territory = params.get('territory'),
154         )
155
156     pager, errors = conv.params_to_pois_pager(params, state = ctx)
157     if errors is not None:
158         raise wsgihelpers.bad_request(ctx, explanation = ctx._('Error: {0}').format(errors))
159
160     response = json.dumps(
161         conv.check(conv.pois_to_geojson)(date.items, state = ctx),
162         encoding = 'utf-8',
163         ensure_ascii = False,
164         )
165     if params['jsonp']:
166         req.response.content_type = 'application/javascript; charset=utf-8'
167         return u'{0}({1})'.format(params['jsonp'], response)
168     else:
169         req.response.content_type = 'application/json; charset=utf-8'
170         return response
171
172
173 @wsgihelpers.wsgify
174 @ramdb.ramdb_based
175 def index(req):
176     ctx = contexts.Ctx(req)
177
178     mode = {
179         None: 'list',
180         'carte': 'map',
181         }[req.urlvars.get('mode')]
182
183     params = req.GET
184     init_ctx(ctx, params)
185     params = dict(
186         category = params.get('category'),
187         page = params.get('page'),
188         term = params.get('term'),
189         territory = params.get('territory'),
190         )
191
192     pager, errors = conv.params_to_pois_pager(params, state = ctx)
193
194     template = '/{0}.mako'.format(mode)
195     return templates.render(ctx, template,
196         mode = mode,
197         errors = errors,
198         pager = pager,
199         params = params,
200         )
201
202
203 def init_ctx(ctx, params):
204     ctx.base_categories_slug, error = conv.uniform_sequence(
205         conv.str_to_category_slug,
206         )(params.getall('base_category'), state = ctx)
207     if error is not None:
208         raise wsgihelpers.bad_request(ctx, explanation = ctx._('Base Categories Error: {0}').format(error))
209
210     ctx.category_tags_slug, error = conv.uniform_sequence(
211         conv.str_to_category_slug,
212         )(params.getall('category_tag'), state = ctx)
213     if error is not None:
214         raise wsgihelpers.bad_request(ctx, explanation = ctx._('Category Tags Error: {0}').format(error))
215
216     container_base_url = params.get('container_base_url') or None
217     if container_base_url is None:
218         container_hostname = None
219     else:
220         container_hostname = urlparse.urlsplit(container_base_url).hostname or None
221     try:
222         gadget_id = int(params.get('gadget'))
223     except (TypeError, ValueError):
224         gadget_id = None
225     if gadget_id is None:
226         if container_base_url is not None:
227             # Ignore container site when no gadget ID is given.
228             container_base_url = None
229             container_hostname = None
230 #    else:
231 #        subscriber = Subscriber.retrieve_by_subscription_id(ctx, gadget_id)
232 #        if subscriber is None:
233 #            ctx.front_office = True
234 #            return wsgihelpers.bad_request(ctx, body = htmlhelpers.modify_html(ctx, templates.render(ctx,
235 #                '/error-invalid-gadget-id.mako', gadget_id = gadget_id)))
236 #        for site in subscriber.sites or []:
237 #            for subscription in site.get('subscriptions') or []:
238 #                if subscription['id'] == gadget_id:
239 #                    break
240 #            else:
241 #                continue
242 #            break
243 #        else:
244 #            ctx.front_office = True
245 #            return wsgihelpers.bad_request(ctx, body = htmlhelpers.modify_html(ctx, templates.render(ctx,
246 #                '/error-invalid-gadget-id.mako', gadget_id = gadget_id)))
247 #        ctx.subscriber = subscriber
248 #        if gadget_id is not None and container_base_url is None and subscription.get('url') is not None:
249 #            # When in gadget mode but without a container_base_url, we are accessed through the noscript iframe or by a
250 #            # search engine. We need to retrieve the URL of page containing gadget to do a JavaScript redirection (in
251 #            # publication.mako).
252 #            container_base_url = subscription['url'] or None
253 #            container_hostname = urlparse.urlsplit(container_base_url).hostname or None
254     ctx.container_base_url = container_base_url
255     ctx.gadget_id = gadget_id
256
257 #    base_territory_type = req.urlvars.get('base_territory_type')
258 #    base_territory_code = req.urlvars.get('base_territory_code')
259 #    if base_territory_type is not None and base_territory_code is not None:
260 #        base_territory_kind = urls.territories_kind[base_territory_type]
261 #        ctx.base_territory = Territory.kind_to_class(base_territory_kind).get(base_territory_code)
262 #        if ctx.base_territory is None:
263 #            return wsgihelpers.not_found(ctx, body = htmlhelpers.modify_html(ctx, templates.render(ctx,
264 #                '/error-unknown-territory.mako', territory_code = base_territory_code,
265 #                territory_kind = base_territory_kind)))
266 #        if ctx.subscriber is not None:
267 #            subscriber_territory = ctx.subscriber.territory
268 #            if subscriber_territory._id not in ctx.base_territory.ancestors_id:
269 #                return wsgihelpers.not_found(ctx, body = htmlhelpers.modify_html(ctx, templates.render(ctx,
270 #                    '/error-invalid-territory.mako', parent_territory = subscriber_territory,
271 #                    territory = ctx.base_territory)))
272 #    if ctx.base_territory is None and user is not None and user.territory is not None:
273 #        ctx.base_territory = Territory.get_variant_class(user.territory['kind']).get(user.territory['code'])
274 #        if ctx.base_territory is None:
275 #            return wsgihelpers.not_found(ctx, body = htmlhelpers.modify_html(ctx, templates.render(ctx,
276 #                '/error-unknown-territory.mako', territory_code = user.territory['code'],
277 #                territory_kind = user.territory['kind'])))
278
279
280 def make_router():
281     """Return a WSGI application that dispatches requests to controllers """
282     return urls.make_router(
283         ('GET', '^/((?P<mode>carte)/?)?$', index),
284         ('GET', '^/a-propos/?$', about),
285         ('GET', '^/api/v1/autocomplete-category/?$', autocomplete_category),
286         ('GET', '^/api/v1/geojson/?$', geojson),
287         ('GET', '^/organismes/(?P<poi_id>[a-z0-9]{24})/?$', poi),
288         )
289
290
291 @wsgihelpers.wsgify
292 @ramdb.ramdb_based
293 def poi(req):
294     ctx = contexts.Ctx(req)
295
296     params = req.params
297     init_ctx(ctx, params)
298     params = dict(
299         poi_id = req.urlvars.get('poi_id'),
300         )
301
302     poi_id, error = conv.str_to_object_id(params['poi_id'], ctx)
303     if error is not None:
304         raise wsgihelpers.not_found(ctx, explanation = ctx._('Poi ID Error: {0}').format(error))
305
306     poi = pois.Poi.find_one({"_id": poi_id})
307     return templates.render(ctx, '/poi.mako', poi = poi)