diff options
Diffstat (limited to 'chimere/views.py')
| -rw-r--r-- | chimere/views.py | 1361 |
1 files changed, 1361 insertions, 0 deletions
diff --git a/chimere/views.py b/chimere/views.py new file mode 100644 index 0000000..8eaa83a --- /dev/null +++ b/chimere/views.py @@ -0,0 +1,1361 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2008-2016 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet> +# +# RSS : Copyright (C) 2010 Pierre Clarenc <pierre.crc_AT_gmailDOTcom>, +# Samuel Renard <renard.samuel_AT_gmailDOTcom>, + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# See the file COPYING for details. + +""" +Views of the project +""" + +import datetime +from itertools import groupby +import re + +from django.conf import settings +from django.contrib.gis.geos import GEOSGeometry, Polygon as GEOSPolygon +from django.contrib.gis.gdal.error import OGRException +from django.contrib.gis.measure import D +from django.contrib.sites.models import get_current_site +from django.core.exceptions import ObjectDoesNotExist +from django.core.urlresolvers import reverse +from django.db.models import Q +from django.http import HttpResponseRedirect, HttpResponse, Http404 +from django.shortcuts import get_object_or_404, redirect, render_to_response +from django.template import RequestContext, defaultfilters +from django.utils import simplejson as json +from django.utils.http import urlquote +from django.utils.translation import ugettext as _ +from django.views.generic import TemplateView, ListView + +from chimere.actions import actions +from chimere.models import Category, SubCategory, PropertyModel, Page,\ + Marker, Route, Polygon, SimpleArea, Area, Color, TinyUrl, RouteFile,\ + AggregatedRoute, AggregatedPolygon, PropertyModelChoice + +from chimere.widgets import PointChooserWidget, RouteChooserWidget, AreaWidget,\ + PolygonChooserWidget +from chimere.forms import MarkerForm, RouteForm, ContactForm, FileForm, \ + FullFileForm, MultimediaFileFormSet, PictureFileFormSet, notifySubmission,\ + notifyStaff, AreaForm, RoutingForm, PolygonForm + +from chimere.route import router + + +def get_base_uri(request): + base_uri = 'http://' + if 'HTTP_REFERER' in request.META: + if request.META['HTTP_REFERER'].startswith('https:'): + base_uri = 'https://' + if 'SERVER_NAME' in request.META: + base_uri += request.META['SERVER_NAME'] + if 'SERVER_PORT' in request.META and \ + str(request.META['SERVER_PORT']) != '80': + base_uri += ":" + str(request.META['SERVER_PORT']) + return base_uri + +# TODO: convert to requestcontext + + +def get_base_response(request, area_name=""): + """ + Get the base url + """ + base_response_dct = {'media_path': settings.MEDIA_URL} + base_response_dct['MOBILE'] = \ + settings.MOBILE_TEST or \ + get_current_site(request).domain in settings.MOBILE_DOMAINS + base_url = reverse("chimere:index") + if not base_url.startswith('/'): + base_url = '/' + base_url + if area_name and area_name.endswith('/'): + area_name = area_name[:-1] + if area_name: + base_response_dct['area_name_slash'] = area_name + "/" + if base_url[-1] != '/': + base_url += '/' + base_url += area_name + '/' + base_response_dct['extra_url'] = base_url + area = None + if area_name: + try: + area = Area.objects.get(urn=area_name, available=True) + except ObjectDoesNotExist: + return None, redirect(reverse('chimere:index')) + else: + try: + area = Area.objects.get(default=True) + area_name = area.urn + except ObjectDoesNotExist: + pass + + base_response_dct['area'] = area + base_response_dct['area_name'] = area_name + if area and area.external_css: + base_response_dct['css_area'] = area.external_css + base_response_dct['dynamic_categories'] = \ + True if area and area.dynamic_categories else False + base_response_dct['JQUERY_JS_URLS'] = settings.JQUERY_JS_URLS + base_response_dct['JQUERY_CSS_URLS'] = settings.JQUERY_CSS_URLS + base_response_dct['PROJECT_NAME'] = settings.PROJECT_NAME + if hasattr(settings, 'EXTRA_CSS'): + base_response_dct['EXTRA_CSS'] = settings.EXTRA_CSS + return base_response_dct, None + + +def getShareUrl(request, area_name='', network=''): + """ + Get a share url + """ + data = getTinyfiedUrl(request, request.GET.urlencode(), area_name) + for name, url, img in settings.CHIMERE_SHARE_NETWORKS: + if defaultfilters.slugify(name) == network: + return HttpResponse(url % {'text': data['text'], + 'url': data['url']}) + return HttpResponse('') + + +def getShareNetwork(request, area_name='', marker=None): + """ + Get URLs to share items + """ + parameters = "" + if marker: + parameters = u'current_feature=%d' % marker.pk + parameters += u"&checked_categories=%s" % "_".join( + [str(m.id) for m in marker.categories.all()]) + net_dct = getTinyfiedUrl(request, parameters, area_name) + share_networks = [] + for network in settings.CHIMERE_SHARE_NETWORKS: + share_networks.append((network[0], network[1] % net_dct, network[2])) + return share_networks, net_dct + + +def index(request, area_name=None, default_area=None, simple=False, + get_response=False): + """ + Main page + """ + # show the news + # only if user is not came yet today + today = datetime.date.today().strftime('%y-%m-%d') + news_visible = False + if 'last_visit' not in request.session or \ + request.session['last_visit'] != today: + request.session['last_visit'] = today + news_visible = True + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + # don't mess with permalink + zoomout = True + if request.GET and 'lat' in request.GET \ + and 'lon' in request.GET: + zoomout = None + if hasattr(settings, 'CHIMERE_ENABLE_ROUTING') and \ + settings.CHIMERE_ENABLE_ROUTING: + response_dct['itinerary_form'] = RoutingForm() + response_dct['routing_transport'] = settings.CHIMERE_ROUTING_TRANSPORT + if request.GET and 'current_feature' in request.GET: + try: + m = Marker.objects.get(pk=request.GET['current_feature']) + if m.route: + response_dct['current_route'] = m.route.pk + except: + pass + response_dct.update({ + 'actions': actions(response_dct['area_name']), + 'action_selected': ('view',), + 'error_message': '', + 'is_map': True, + 'news_visible': news_visible, + 'areas_visible': settings.CHIMERE_DISPLAY_AREAS + and Area.objects.filter(available=True).count() > 1, + 'map_layer': settings.CHIMERE_DEFAULT_MAP_LAYER, + 'dynamic_categories': response_dct['dynamic_categories'], + 'zoomout': zoomout, + 'has_default_area': Area.objects.filter(default=True).count(), + 'zoomout': zoomout, + 'has_search': hasattr(settings, 'CHIMERE_SEARCH_ENGINE') and + settings.CHIMERE_SEARCH_ENGINE + }) + if hasattr(settings, 'CONTACT_EMAIL') and settings.CONTACT_EMAIL: + response_dct['contact_email'] = settings.CONTACT_EMAIL + response_dct['share_networks'], net_dct = \ + getShareNetwork(request, response_dct['area_name']) + tpl = 'chimere/main_map.html' + response_dct['simple'] = simple + if simple: + tpl = 'chimere/main_map_simple.html' + if get_response: + return tpl, response_dct + return render_to_response(tpl, response_dct, + context_instance=RequestContext(request)) + + +def edit(request, area_name="", item_id=None, submited=False): + """ + Edition page + """ + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + current_actions = actions(response_dct['area_name']) + redir = action_do_redirect('edit-no-page', current_actions) + # a redir is always send... or there is a problem + return redirect(redir) + + +def get_edit_page(redirect_url, item_cls, item_form, + multimediafile_formset=MultimediaFileFormSet, + picturefile_formset=PictureFileFormSet): + """ + Edition page + """ + def func(request, area_name="", item_id=None, cat_type=['M']): + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir, None, None + if 'area_name' in response_dct: + area_name = response_dct['area_name'] + subcategories = SubCategory.getAvailable(cat_type, area_name, + public=True) + listed_subcats = [] + if subcategories: + for cat, subcats in subcategories: + listed_subcats.append( + (unicode(cat), + [(subcat.pk, subcat.name) for subcat in subcats])) + # if an item_id is provided: modification + init_item, ref_item = None, None + if item_id: + try: + init_item = item_cls.objects.get(pk=item_id) + except: + return redirect(redirect_url, area_name + '/' if area_name + else ''), None, None + ref_item = init_item + modified_item = item_cls.objects.filter( + ref_item=init_item, + submiter_session_key=request.session.session_key) + if modified_item.count(): + init_item = modified_item.all()[0] + response_dct['is_modification'] = True + + init_multi = init_item.get_init_multi() if init_item else None + init_picture = init_item.get_init_picture() if init_item else None + if init_item and not request.user.is_superuser and \ + not init_item.submiter_session_key == \ + request.session.session_key: + # hide personal information + for k in ('submiter_name', 'submiter_email', 'submiter_comment'): + setattr(init_item, k, '') + response_dct['is_superuser'] = request.user.is_superuser + # If the form has been submited + if request.method == 'POST': + inst = None + # allow to directly modify only if owner or superuser + if init_item and ( + request.user.is_superuser or + init_item.submiter_session_key == + request.session.session_key): + inst = init_item + form = item_form(request.POST, request.FILES, instance=inst, + subcategories=listed_subcats, + area_name=area_name) + formset_multi = multimediafile_formset( + request.POST, request.FILES, initial=init_multi, + prefix='multimedia') + formset_picture = picturefile_formset( + request.POST, request.FILES, initial=init_picture, + prefix='picture') + # All validation rules pass + if form.is_valid() and formset_multi.is_valid() and \ + formset_picture.is_valid(): + item = form.save() + # set the session key (to permit modifications) + item.submiter_session_key = request.session.session_key + + # associate to the reference item + if ref_item: + item.ref_item = ref_item + if item.pk != ref_item.pk: + item.status = 'M' + if hasattr(ref_item, 'has_associated_marker'): + item.has_associated_marker = \ + ref_item.has_associated_marker + elif not item.ref_item: + # initialisation + item.ref_item = item + + # just submited + if not item.status: + item.status = 'S' + item.save() + + marker = item + if not isinstance(marker, Marker) \ + and hasattr(item, 'associated_marker') \ + and item.associated_marker.count(): + marker = item.associated_marker.all()[0] + if marker: + # manage multimedia items + for f in formset_multi: + f.save(marker) + + for f in formset_picture: + f.save(marker) + base_uri = get_base_uri(request) + notifySubmission(base_uri, item) + response_dct = get_base_response(request, area_name) + return redirect( + redirect_url + '-item', + area_name + '/' if area_name else '', + item.ref_item.pk, 'submited'), None, subcategories + else: + response_dct['error_message'] = _( + u"There are missing field(s)" + u" and/or errors in the submited form.") + else: + form = item_form(instance=init_item, subcategories=listed_subcats, + area_name=area_name) + formset_multi = multimediafile_formset(initial=init_multi, + prefix='multimedia') + formset_picture = picturefile_formset(initial=init_picture, + prefix='picture') + return None, (item_id, init_item, response_dct, form, formset_multi, + formset_picture), subcategories + return func + + +def action_do_redirect(action_name, available_actions): + # redirect to an edit + is_edit = 'edit' in action_name + redir = None + for action, subactions in available_actions: + if action.id == action_name: + return + if not redir and action.id != action_name: + redir = action.url + for subaction in subactions: + if subaction.id == action_name: + return + if is_edit and 'edit' not in redir \ + and 'edit' in subaction.id: + redir = subaction.url + return redir + +get_edit_marker = get_edit_page('chimere:editmarker', Marker, MarkerForm) + + +def editMarker(request, area_name="", item_id=None, submited=False): + """ + Edition page + """ + response, values, sub_categories = get_edit_marker(request, area_name, + item_id, ['M', 'B']) + if response: + return response + item_id, init_item, response_dct, form, formset_multi, formset_picture = \ + values + + # verify action is available + current_actions = actions(response_dct['area_name']) + redir = action_do_redirect('edit-marker', current_actions) + if redir: + return redirect(redir) + + # get the "manualy" declared_fields. Ie: properties + querys = PropertyModel.getAvailable(area_name=area_name) + declared_fields, filtered_properties = [], [] + for query in querys: + declared_fields += query.all() + filtered_properties += query.filter( + subcategories__id__isnull=False).all() + point_value = init_item.point if init_item else None + if request.POST and request.POST.get('point'): + point_value = request.POST.get('point') + response_dct.update({ + 'actions': current_actions, + 'action_selected': ('contribute', 'edit-marker'), + 'map_layer': settings.CHIMERE_DEFAULT_MAP_LAYER, + 'form': form, + 'formset_multi': formset_multi, + 'formset_picture': formset_picture, + 'dated': settings.CHIMERE_DAYS_BEFORE_EVENT, + 'extra_head': form.media, + 'marker_id': item_id, + 'sub_categories': sub_categories, + 'point_widget': PointChooserWidget().render( + 'point', point_value, area_name=response_dct['area_name']), + 'properties': declared_fields, + 'filtered_properties': filtered_properties, + 'submited': submited + }) + # manualy populate the custom widget + if 'subcategory' in form.data and form.data['subcategory']: + response_dct['current_category'] = int(form.data['subcategory']) + return render_to_response('chimere/edit.html', response_dct, + context_instance=RequestContext(request)) + + +def uploadFile(request, category_id='', area_name=''): + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + Form = FileForm if not category_id else FullFileForm + category = None + if category_id: + try: + category = SubCategory.objects.get(pk=category_id) + response_dct['category'] = unicode(category) + except: + pass + # If the form has been submited + if request.method == 'POST': + form = Form(request.POST, request.FILES) + # All validation rules pass + if form.is_valid(): + raw_file = form.cleaned_data['raw_file'] + name = raw_file.name.split('.')[0] + file_type = raw_file.name.split('.')[-1][0].upper() + routefile = RouteFile(raw_file=raw_file, name=name, + file_type=file_type) + routefile.save() + if not category_id: + response_dct['gpx_id'] = routefile.pk + return render_to_response( + 'chimere/upload_file.html', response_dct, + context_instance=RequestContext(request)) + routefile.process() + if not routefile.route: + response_dct['errors'] = _( + u"Bad file. Please check it with an external software.") + response_dct.update({'form': form}) + return render_to_response( + 'chimere/upload_file.html', response_dct, + context_instance=RequestContext(request)) + route = Route(name=form.cleaned_data['name'], + route=routefile.route, associated_file=routefile, + status='S') + route.save() + route.categories.add(category) + route.save() + response_dct['thanks'] = True + form = Form() + else: + # An unbound form + form = Form() + response_dct.update({'form': form}) + return render_to_response('chimere/upload_file.html', response_dct, + context_instance=RequestContext(request)) + + +def processRouteFile(request, area_name='', file_id=None): + if file_id: + try: + route_file = RouteFile.objects.get(pk=file_id) + route_file.process() + route = route_file.route + if not route: + return HttpResponse(status=500) + return HttpResponse('(' + json.dumps({'wkt': route, + 'file_id': file_id}) + ')', + 'application/javascript', status=200) + except OSError as e: + return HttpResponse(e.strerror, status=500) + else: + return HttpResponse(status=400) + +get_edit_route = get_edit_page('chimere:editroute', Route, RouteForm) + + +def editRoute(request, area_name="", item_id=None, submited=False): + """ + Route edition page + """ + response, values, sub_categories = get_edit_route(request, area_name, + item_id, ['R', 'B']) + if response: + return response + item_id, init_item, response_dct, form, formset_multi, formset_picture = \ + values + + # verify action is available + current_actions = actions(response_dct['area_name']) + redir = action_do_redirect('edit-route', current_actions) + if redir: + return redirect(redir) + + # get the "manualy" declared_fields. Ie: properties + declared_fields = form.declared_fields.keys() + if 'description' in declared_fields: + declared_fields.pop(declared_fields.index('description')) + route_value = init_item.route if init_item else None + if request.POST and request.POST.get('route'): + route_value = request.POST.get('route') + response_dct.update({ + 'actions': current_actions, + 'action_selected': ('contribute', 'edit-route'), + 'error_message': '', + 'map_layer': settings.CHIMERE_DEFAULT_MAP_LAYER, + 'form': form, + 'formset_multi': formset_multi, + 'formset_picture': formset_picture, + 'dated': settings.CHIMERE_DAYS_BEFORE_EVENT, + 'extra_head': form.media, + 'sub_categories': sub_categories, + 'route_widget': RouteChooserWidget().render( + 'route', route_value, area_name=response_dct['area_name'], + routefile_id='',), + 'properties': declared_fields, + 'submited': submited + }) + # manualy populate the custom widget + if 'subcategory' in form.data and form.data['subcategory']: + response_dct['current_category'] = int(form.data['subcategory']) + return render_to_response('chimere/edit_route.html', response_dct, + context_instance=RequestContext(request)) + +get_edit_polygon = get_edit_page('chimere:editpolygon', Polygon, PolygonForm) + + +def editPolygon(request, area_name="", item_id=None, submited=False): + """ + Polygon edition page + """ + response, values, sub_categories = get_edit_polygon(request, area_name, + item_id, ['P']) + if response: + return response + item_id, init_item, response_dct, form, formset_multi, formset_picture = \ + values + + # verify action is available + current_actions = actions(response_dct['area_name']) + redir = action_do_redirect('edit-polygon', current_actions) + if redir: + return redirect(redir) + + # get the "manualy" declared_fields. Ie: properties + querys = PropertyModel.getAvailable(area_name=area_name) + declared_fields, filtered_properties = [], [] + for query in querys: + declared_fields += query.all() + filtered_properties += query.filter( + subcategories__id__isnull=False).all() + polygon_value = init_item.polygon if init_item else None + if request.POST and request.POST.get('polygon'): + polygon_value = request.POST.get('polygon') + response_dct.update({ + 'actions': current_actions, + 'action_selected': ('contribute', 'edit-polygon'), + 'error_message': '', + 'map_layer': settings.CHIMERE_DEFAULT_MAP_LAYER, + 'form': form, + 'formset_multi': formset_multi, + 'formset_picture': formset_picture, + 'dated': settings.CHIMERE_DAYS_BEFORE_EVENT, + 'extra_head': form.media, + 'sub_categories': sub_categories, + 'polygon_widget': PolygonChooserWidget().render( + 'polygon', polygon_value, area_name=response_dct['area_name'],), + 'properties': declared_fields, + 'filtered_properties': filtered_properties, + 'submited': submited + }) + # manualy populate the custom widget + if 'subcategory' in form.data and form.data['subcategory']: + response_dct['current_category'] = int(form.data['subcategory']) + return render_to_response('chimere/edit_polygon.html', response_dct, + context_instance=RequestContext(request)) + + +def submited(request, area_name="", action=""): + """ + Successful submission page + """ + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + dct = {'actions': actions(response_dct['area_name']), + 'action_selected': action} + if hasattr(settings, 'CONTACT_EMAIL') and settings.CONTACT_EMAIL: + response_dct['contact_email'] = settings.CONTACT_EMAIL + response_dct.update(dct) + return render_to_response('chimere/submited.html', response_dct, + context_instance=RequestContext(request)) + + +def charte(request, area_name=""): + """ + Affichage de la charte + """ + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + response_dct.update({'actions': actions(response_dct['area_name']), + 'action_selected': ('charte',)}) + return render_to_response('chimere/charte.html', response_dct, + context_instance=RequestContext(request)) + + +def contactus(request, area_name=""): + """ + Contact page + """ + form = None + msg = '' + # If the form has been submited + if request.method == 'POST': + form = ContactForm(request.POST) + # All validation rules pass + if form.is_valid(): + response = notifyStaff( + _(u"Comments/request on the map"), + form.cleaned_data['content'], form.cleaned_data['email']) + if response: + msg = _(u"Thank you for your contribution. It will be taken " + u"into account. If you have left your email you may " + u"be contacted soon for more details.") + else: + msg = _(u"Temporary error. Renew your message later.") + else: + form = ContactForm() + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + response_dct.update({'actions': actions(response_dct['area_name']), + 'action_selected': ('contact',), + 'contact_form': form, 'message': msg}) + return render_to_response('chimere/contactus.html', response_dct, + context_instance=RequestContext(request)) + + +def extraPage(request, area_name="", page_id=""): + """ + Extra dynamic pages + """ + try: + page = Page.objects.get(available=True, mnemonic=page_id) + except ObjectDoesNotExist: + return redirect(reverse('chimere:index')) + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + response_dct.update({'actions': actions(response_dct['area_name']), + 'action_selected': (page_id,), + 'content': page.content, + 'title': page.title}) + tpl = page.template_path if page.template_path \ + else 'chimere/default_extra_page.html' + return render_to_response(tpl, response_dct, + context_instance=RequestContext(request)) + + +def getDetailUndefined(request, area_name): + return HttpResponse('') + + +def getDetail(request, area_name, key): + ''' + Get the detail of a geographic item + ''' + cls = Marker + pk = key + if '-' in key: + geo_type, pk = key.split('-') + if geo_type == 'route': + cls = Route + elif geo_type == 'aggroute': + cls = AggregatedRoute + elif geo_type == 'polygon': + cls = Polygon + try: + marker = cls.objects.filter(id=int(pk), + status__in=['A', 'S'])[0] + except (ValueError, IndexError): + return HttpResponse('no results') + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + response_dct['marker'] = marker + if request.method == 'GET': + if 'simple' in request.GET and request.GET['simple']: + response_dct['simple'] = True + response_dct['share_networks'], net_dct = \ + getShareNetwork(request, response_dct['area_name'], marker) + response_dct['share_url'] = net_dct['url'] + net_dct['to'] = settings.CONTACT_EMAIL + if net_dct['to']: + net_dct["body"] = _(settings.CHIMERE_MODIF_EMAIL) + response_dct['modif_by_email'] = 'mailto:?to=%(to)s&subject='\ + '%(text)s&body=%(body)s%(url)s' % net_dct + # to be sure there is unique IDs during a browsing + response_dct['time_now'] = datetime.datetime.now().strftime('%H%M%S') + response_dct['dated'] = settings.CHIMERE_DAYS_BEFORE_EVENT \ + and marker.start_date + response_dct['routing_enabled'] = settings.CHIMERE_ENABLE_ROUTING + response_dct['properties'] = marker.getProperties(area_name=area_name) + return render_to_response('chimere/detail.html', response_dct, + context_instance=RequestContext(request)) + + +def getDescriptionDetail(request, area_name, category_id): + ''' + Get the description for a category + ''' + try: + category = Category.objects.filter(id=int(category_id))[0] + except (ValueError, IndexError): + return HttpResponse('no results') + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + response_dct['category'] = category + return render_to_response('chimere/category_detail.html', response_dct, + context_instance=RequestContext(request)) + + +def checkDate(q): + """ + Filter a queryset to manage dates + """ + if not settings.CHIMERE_DAYS_BEFORE_EVENT: + return q + today = datetime.date.today() + after = today + datetime.timedelta(settings.CHIMERE_DAYS_BEFORE_EVENT) + + q = q & (Q(start_date__isnull=True) | Q(start_date__gte=today, + start_date__lte=after) | Q( + start_date__lte=today, end_date__gte=today)) + return q + + +def _getGeoObjects(area_name, category_ids, status='A', getjson=True, + item_types=('Marker', 'Route', 'Polygon'), + bounding_box=None, zoom_level=None): + ''' + Get geo objects + ''' + zoom_need_reload = None + items = [] + empty = [] if not getjson else {} + subcategories = list( + SubCategory.getAvailable(None, area_name, public=True, + instance=True).all()) + aggregated_category_ids = [] + try: + zoom_level = int(zoom_level) + except ValueError: + zoom_level = None + if zoom_level: + # pop from main category list + len_subcats = len(subcategories) + for idx, subcat in enumerate(reversed(subcategories)): + if not subcat.min_zoom: + continue + if zoom_level < subcat.min_zoom: + if not zoom_need_reload or zoom_need_reload > subcat.min_zoom: + zoom_need_reload = subcat.min_zoom + aggregated_category_ids.append( + subcategories.pop(len_subcats - 1 - idx).pk) + + category_ids = [subcat.pk for subcat in subcategories] + + # marker + if 'Marker' in item_types: + try: + q = checkDate(Q(status__in=status, categories__in=category_ids)) + query = Marker.objects.filter(q) + if bounding_box: + query = query.filter(point__contained=bounding_box) + query = query.distinct('pk').order_by('-pk') + except: + return empty, zoom_need_reload + + if getjson: + for geo_object in list(query): + items += json.loads(geo_object.getGeoJSON(category_ids)) + else: + items += list(query) + + # polygon + if 'Polygon' in item_types: + try: + q = checkDate(Q(status__in=status, categories__in=category_ids)) + query = Polygon.objects.filter(q) + if bounding_box: + query = query.filter(polygon__contained=bounding_box) + query = query.distinct('pk').order_by('-pk') + except: + return empty, zoom_need_reload + + if aggregated_category_ids: + query = AggregatedPolygon.objects.filter( + status__in=status, + subcategory__in=aggregated_category_ids).order_by( + 'subcategory', '-pk') + # no bounding box filter + if getjson: + for poly in query.all(): + items.append(json.loads(poly.getGeoJSON())) + else: + items += list(query) + else: + if getjson: + current_cat, colors, idx = None, None, 0 + items += Polygon.getGeoJSONs( + query, limit_to_categories=category_ids) + else: + items += list(query) + + # routes + if 'Route' in item_types: + # TODO: manage non aggregated and bounding box in non-aggregated + query = AggregatedRoute.objects.filter( + status__in=status, subcategory__in=category_ids).order_by( + 'subcategory', '-pk') + if getjson: + current_cat, colors, idx = None, None, 0 + for route in query.all(): + color = "" + # aggregated view has no color and no categories + if hasattr(route, 'color') and route.color: + color = route.color + elif hasattr(route, 'categories'): + c_cat = None + for cat in route.categories.all(): + if cat.id in category_ids: + c_cat = cat + break + if c_cat and not current_cat or current_cat != c_cat: + idx = 0 + current_cat = c_cat + colors = list(Color.objects.filter( + color_theme=c_cat.color_theme)) + if colors: + color = colors[idx % len(colors)].code + idx += 1 + else: + color = "#000" + items.append(json.loads(route.getGeoJSON(color=color))) + else: + items += list(query) + + if not items: + return empty, zoom_need_reload + return items, zoom_need_reload + + +def getGeoObjects(request, area_name, category_ids, status): + ''' + Get the JSON for markers and routes + ''' + if not status: + status = 'A' + status = status.split('_') + category_ids = unicode(category_ids).split('_') + + bounding_box = [] + for attr in ['min_lon', 'min_lat', 'max_lon', 'max_lat']: + value = request.GET.get(attr, None) + if not value: + bounding_box = None + break + bounding_box.append(value) + if bounding_box: + bounding_box = GEOSPolygon.from_bbox(bounding_box) + zoom_level = request.GET.get('zoom_level', None) + + jsons, zoom_need_reload = _getGeoObjects( + area_name, category_ids, status, bounding_box=bounding_box, + zoom_level=zoom_level) + if not jsons: + return HttpResponse("[]", content_type="application/json") + data = json.dumps({"type": "FeatureCollection", "features": jsons, + "zoom_need_reload": zoom_need_reload or ''}) + return HttpResponse(data, content_type="application/json") + + +def getMarker(request, area_name, pk): + q = Marker.objects.filter(pk=pk, status='A') + if not q.count(): + return HttpResponse('{}') + data = q.all()[0].getGeoJSON() + return HttpResponse(data, content_type="application/json") + + +def get_all_categories(request, area_name=None): + ''' + Get all available categories in JSON + ''' + context_data, redir = get_base_response(request, area_name) + area = context_data["area"] + subcategories = [] + if area: + subcategories = list(area.getCategories( + 'A', area_name=context_data['area_name'])) + else: + categories = SubCategory.getAvailable() + for cat, subcats in categories: + subcategories += subcats + subcats = [subcat.getJSONDict() for subcat in subcategories] + jsons = json.dumps({'categories': subcats}) + return HttpResponse(jsons) + + +def get_available_categories(request, area_name=None, area=None, status='A', + force=None): + ''' + Get category menu for a designed area + ''' + context_data, redir = get_base_response(request, area_name) + area = context_data["area"] + if redir: + return redir + if area and area.dynamic_categories and \ + "current_extent" not in request.GET: + context_data['sub_categories'] = [] + return render_to_response( + 'chimere/blocks/categories.html', context_data, + context_instance=RequestContext(request)) + if not area or not area.dynamic_categories: + # Categories are not updated dynamicaly when the user move the map + # so we return ALL the categories + subcategories = SubCategory.getAvailable( + area_name=context_data['area_name']) + context_data['sub_categories'] = subcategories + return render_to_response( + 'chimere/blocks/categories.html', context_data, + context_instance=RequestContext(request)) + default_message = "<p class='warning'>%s</p>" % \ + _("No category available in this area.") + if "status" not in request.GET: # there must be a status + status = 'A' + try: + status = status.split('_') + current_extent = request.GET["current_extent"].replace('M', '-')\ + .replace('D', '.') + area = SimpleArea([float(pt) for pt in current_extent.split('_')]) + except: + # bad extent format + return HttpResponse(default_message) + # if not force and area.isIn(SimpleArea(cookie.AREA):return + categories = area.getCategories( + status, area_name=context_data['area_name']) + if not categories: + return HttpResponse(default_message) + get_cat = lambda subcat: subcat.category + get_cat_order = lambda subcat: (subcat.category.order, subcat.category.pk, + subcat.order) + categories = sorted(categories, key=get_cat_order) + subcategories = [(cat, list(subcats)) + for cat, subcats in groupby(categories, get_cat)] + context_data['sub_categories'] = subcategories + return render_to_response('chimere/blocks/categories.html', context_data, + context_instance=RequestContext(request)) + + +def getCategory(request, area_name='', category_id=0): + ''' + Get the JSON for a category (mainly in order to get the description) + ''' + try: + category = SubCategory.objects.get(pk=category_id) + except ObjectDoesNotExist: + return HttpResponse('[]', content_type="application/json") + return HttpResponse(category.getJSON(), content_type="application/json") + + +def getTinyfiedUrl(request, parameters, area_name=''): + ''' + Get the tinyfied version of parameters + ''' + data = {"urn": "", "url": "", "text": ""} + try: + urn = TinyUrl.getUrnByParameters(parameters) + except: + return {} + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + url = reverse('chimere:tiny', args=[(response_dct['area_name'] + if response_dct['area_name'] else '') + '/', urn]) + if not url.startswith('http'): + url = get_base_uri(request) + url + url = re.sub("([^:])\/\/", "\g<1>/", url) + text = settings.PROJECT_NAME + if 'current_feature' in parameters: + for item in parameters.split('&'): + if 'current_feature' in item: + try: + text = unicode(Marker.objects.get(id=item.split('=')[1])) + except (IndexError, Marker.DoesNotExist): + pass + data["urn"] = urlquote(urn) + data["url"] = urlquote(url) + data["text"] = urlquote(text) + return data + + +def redirectFromTinyURN(request, area_name='', tiny_urn=''): + """ + Redirect from a tiny Urn + """ + parameters = '?' + TinyUrl.getParametersByUrn(tiny_urn) + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + return HttpResponseRedirect(response_dct['extra_url'] + parameters) + + +class CategoryDirectoryView(ListView): + template_name = "chimere/category_directory.html" + + def get_queryset(self): + self.area_name = self.kwargs.get('area_name', None) + if self.area_name: + self.area_name = self.area_name.split('/')[0] + area = get_object_or_404(Area, urn=self.area_name, available=True) + q = area.subcategories.filter( + available=True, category__available=True).order_by( + 'category__order', 'category__id', 'order') + if q.count(): + return q + return SubCategory.objects.filter( + available=True, category__available=True).order_by( + 'category__order', 'category__id', 'order') + + def get_context_data(self, *args, **kwargs): + context = super(CategoryDirectoryView, self).get_context_data( + *args, **kwargs) + new_context, redirect = get_base_response(self.request, self.area_name) + context.update(new_context) + context.update({ + 'actions': actions(self.area_name), + 'action_selected': ('categories',), + }) + return context + + +class CategoryView(TemplateView): + template_name = "chimere/category_directory_detail.html" + + def get_geo_items(self): + # TODO: simplify on v2.3 when slug are available + category_slug = self.kwargs.get('category_slug') + self.area_name = self.kwargs.get('area_name', None) + q = None + if self.area_name: + self.area_name = self.area_name.split('/')[0] + area = get_object_or_404(Area, urn=self.area_name, available=True) + q = area.subcategories.filter(available=True, + category__available=True) + if not q.count(): + q = None + if not q: + q = SubCategory.objects.filter(available=True, + category__available=True) + self.category = None + for subcat in q: + if defaultfilters.slugify(subcat.name) == category_slug: + self.category = subcat + break + if not self.category: + raise Http404(_("Category does not exist")) + + items, zoom_need_reload = _getGeoObjects( + self.area_name, [unicode(self.category.pk)], getjson=False, + item_types=('Marker',)) + return items + + def get_context_data(self, *args, **kwargs): + context = super(CategoryView, self).get_context_data( + *args, **kwargs) + self.items = self.get_geo_items() + new_context, redirect = get_base_response(self.request, self.area_name) + context.update(new_context) + context.update({ + 'actions': actions(self.area_name), + 'action_selected': ('categories',), + 'category': self.category, + 'items': self.items + }) + return context + + +def route(request, area_name, lon1, lat1, lonlat_steps, lon2, lat2, + transport='foot', speed=''): + ''' + Get the JSON for a route + ''' + try: + lon1, lat1 = float(lon1), float(lat1) + lon2, lat2 = float(lon2), float(lat2) + steps = [float(lonlat) for lonlat in lonlat_steps.split('_') if lonlat] + # regroup by 2 + steps = [(steps[i * 2], steps[i * 2 + 1]) + for i in range(len(steps) / 2)] + except ValueError: + return HttpResponse('no results') + + # prevent incoherent transport and speed + if transport not in dict(settings.CHIMERE_ROUTING_TRANSPORT): + transport = settings.CHIMERE_ROUTING_TRANSPORT[0][0] + if speed: + speed = unicode(speed) + available_speed = [ + unicode(sp) + for sp, lbl in settings.CHIMERE_ROUTING_SPEEDS[transport]] + if speed not in available_speed: + speed = None + if not speed: + speed = settings.CHIMERE_ROUTING_SPEEDS[transport][0][0] + + jsons, desc, total = router.route(lon1, lat1, lon2, lat2, steps=steps, + transport=transport, speed=speed) + if not jsons: + return HttpResponse('no results') + jsonencoder = json.JSONEncoder() + total = jsonencoder.encode(total) + desc = jsonencoder.encode(desc) + + # get associated POIs + try: + route = GEOSGeometry(jsons[0]) + except OGRException: + return HttpResponse(_(u"Bad geometry"), status=500) + cats = SubCategory.objects.filter(routing_warn=True) + message = '' + if cats.count(): + st_string = '{"type":"Feature", "geometry":{ "type": "Point", '\ + '"coordinates": [ %f, %f ] }, "properties":{"icon_path":"%s",'\ + '"icon_width":%d, "icon_height":%d}}' + points = [(m.point, m.categories.all()[0].icon) + for m in list(Marker.objects.filter( + status='A', categories__in=cats, + point__distance_lte=(route, D(m=15))).all())] + for pt, icon in points: + st = st_string % (pt.x, pt.y, icon.image.url, icon.image.width, + icon.image.height) + jsons.append(st) + routes = Route.objects.filter(status='A', categories__in=cats, + route__crosses=route) + intersect = False + for rout in routes.intersection(route): + pts = rout.intersection + icon = rout.categories.all()[0].icon + if hasattr(pts, 'x'): + pts = [pts] + if pts: + pt = pts[0] + st = st_string % (pt.x, pt.y, icon.image.url, icon.image.width, + icon.image.height) + jsons.append(st) + if points or intersect: + message = getattr(settings, 'CHIMERE_ROUTING_WARN_MESSAGE', '') + if message: + message = ', "message":%s' % jsonencoder.encode( + "%s" % _(message)) + else: + message = '' + data = '{"properties":{"transport":%s, "total":%s, "description":%s}, '\ + '"type": "FeatureCollection", "features":[%s]%s}' % ( + jsonencoder.encode(transport), total, desc, ",".join(jsons), + message) + return HttpResponse(data) + + +def rss(request, area_name=''): + ''' + Redirect to RSS subscription page + ''' + response_dct, redir = get_base_response(request, area_name) + if redir: + return redir + response_dct.update({'actions': actions(response_dct['area_name']), + 'action_selected': ('rss',), + 'category_rss_feed': ''}) + # If the form has been submited + if request.method == "POST": + # User has defined the kind of POI he is interested in : POI in a area + # (GET method is used for the link with RSS icon in the browser) + if 'rss_category' in request.POST: + # User wants to follow all the new POI + if request.POST['rss_category'] == 'global': + feeds_link = reverse('chimere:feeds-global') + return redirect(feeds_link) + # User wants to follow all the new POI by category or subcategory + elif request.POST['rss_category'] == 'poi': + response_dct['category_rss_feed'] = 'category' + response_dct['sub_categories'] = SubCategory.getAvailable() + return render_to_response( + 'chimere/feeds/rss.html', response_dct, + context_instance=RequestContext(request)) + # User wants to follow all the new POI situated in a defined area + elif request.POST['rss_category'] == 'area': + # An unbound form + form = AreaForm() + area_widget = AreaWidget().render('area', None) + response_dct.update({ + 'map_layer': settings.CHIMERE_DEFAULT_MAP_LAYER, + 'extra_head': form.media, + 'form': form, + 'category_rss_feed': 'area', + 'area_id': Area.getAvailable(), + 'area_widget': area_widget + }) + return render_to_response( + 'chimere/feeds/rss.html', response_dct, + context_instance=RequestContext(request)) + # Error when submitting the form + else: + error = _("Incorrect choice in the list") + response_dct.update({ + 'error_message': error, + 'category_rss_feed': '', + 'sub_categories': SubCategory.getAvailable()}) + return render_to_response( + 'chimere/feeds/rss.html', response_dct, + context_instance=RequestContext(request)) + + # User has specified the category or subcategory he wants to follow => + # we redirect him towards the related rss feed + if 'subcategory' in request.POST and request.POST['subcategory'] != '': + cat_id = request.POST['subcategory'] + if cat_id.find("cat_") != -1: + cat_id = cat_id.split('_')[1] + feeds_link = reverse('chimere:feeds-cat', + kwargs={'category_id': cat_id}) + return redirect(feeds_link) + + else: + feeds_link = reverse('chimere:feeds-subcat', + kwargs={'category_id': cat_id}) + return redirect(feeds_link) + + # User has specified the ID of the area he wants to follow + if 'id_area' in request.POST and request.POST['id_area'] != '': + feeds_link = reverse('chimere:feeds-areaid', + kwargs={'area_id': request.POST['id_area']}) + return redirect(feeds_link) + + # User has specified the area he wants to follow => we redirect him + # towards the related rss feed (using upper left and lower right + # coordinates) + elif 'upper_left_lat' in request.POST and \ + request.POST['upper_left_lat'] != '' and \ + 'upper_left_lon' in request.POST and \ + request.POST['upper_left_lon'] != '' and \ + 'lower_right_lon' in request.POST and \ + request.POST['lower_right_lon'] != '' and \ + 'lower_right_lat' in request.POST and \ + request.POST['lower_right_lat'] != '': + coords = request.POST['upper_left_lat'] + '_' + \ + request.POST['upper_left_lon'] + '_' + \ + request.POST['lower_right_lat'] + '_' + \ + request.POST['lower_right_lon'] + feeds_link = reverse('chimere:feeds-area', + kwargs={'area': coords}) + return redirect(feeds_link) + + # GET method is used for linking with the RSS icon in the browser when user + # wants to choose a category to follow + elif request.method == "GET" and 'rss_category' in request.GET: + if request.GET['rss_category'] == 'global': + feeds_link = reverse('chimere:feeds-global') + return redirect(feeds_link) + if request.GET['rss_category'] == 'poi': + response_dct['category_rss_feed'] = 'category' + response_dct['sub_categories'] = SubCategory.getAvailable( + ['M', 'B']) + return render_to_response('chimere/feeds/rss.html', response_dct, + context_instance=RequestContext(request)) + if request.GET['rss_category'] == 'area': + # An unbound form + form = AreaForm() + response_dct.update({ + 'map_layer': settings.MAP_LAYER, + 'extra_head': form.media, + 'form': form, + 'category_rss_feed': 'area', + 'area_id': Area.getAvailable(), + 'area_widget': AreaWidget().render('area', None)}) + return render_to_response('chimere/feeds/rss.html', response_dct, + context_instance=RequestContext(request)) + + # User access to the RSS tab + else: + return render_to_response('chimere/feeds/rss.html', response_dct, + context_instance=RequestContext(request)) + +# from django.core.paginator import Paginator, InvalidPage + + +def property_choice_list(request, area_name='', property_slug=''): + data = {} + try: + pm = PropertyModel.objects.get(slug=property_slug, available=True) + except PropertyModel.DoesNotExist: + return HttpResponse(json.dumps(data), + content_type="application/json") + if not request.GET or not request.GET.get('term') or \ + pm.areas.count() and not pm.areas.filter(urn=area_name).count(): + return HttpResponse(json.dumps(data), + content_type="application/json") + fltr = {'propertymodel': pm, 'available': True, + 'value__icontains': request.GET.get('term')} + q = PropertyModelChoice.objects.filter(**fltr).order_by('value') + data = [{"id": p.pk, "value": p.value} for p in q.all()] + return HttpResponse(json.dumps(data), + content_type="application/json") + + +SearchView = None +autocomplete = None +if hasattr(settings, 'CHIMERE_SEARCH_ENGINE') \ + and settings.CHIMERE_SEARCH_ENGINE: + from haystack.views import SearchView as HaystackSearchView + from haystack.query import SearchQuerySet + + class SearchView(HaystackSearchView): + def extra_context(self, *args, **kwargs): + context = super(SearchView, self).extra_context(*args, **kwargs) + context["autocomplete"] = \ + settings.HAYSTACK_AUTOCOMPLETE \ + if hasattr(settings, 'HAYSTACK_AUTOCOMPLETE') else False + return context + + def autocomplete(request): + sqs = SearchQuerySet().autocomplete( + content_auto=request.GET.get('q', ''))[:5] + suggestions = [result.object.name for result in sqs if result.object] + spelling = [] + if not suggestions: + spelling = SearchQuerySet().spelling_suggestion( + request.GET.get('q', '')) or [] + # convert to list spelling... + # make sure it returns a JSON object, not a bare list. + # otherwise, it could be vulnerable to an XSS attack. + the_data = json.dumps({ + 'results': suggestions, + 'spelling': spelling, + }) + return HttpResponse(the_data, content_type='application/json') |
