#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2008-2012 Étienne Loks # 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 . # See the file COPYING for details. """ Views of the project """ import datetime from itertools import groupby from django.conf import settings from django.core import serializers from django.core.urlresolvers import reverse from django.db.models import Q from django.http import HttpResponseRedirect, HttpResponse from django.shortcuts import redirect, render_to_response from django.template import loader, RequestContext from django.utils import simplejson from django.utils.http import urlquote from django.utils.translation import ugettext as _ from chimere.actions import actions from chimere.models import Category, SubCategory, PropertyModel, \ Marker, Route, News, SimpleArea, Area, Color, TinyUrl, RouteFile from chimere.widgets import getMapJS, PointChooserWidget, \ RouteChooserWidget from chimere.forms import MarkerForm, RouteForm, ContactForm, FileForm, \ FullFileForm, MultimediaFileFormSet, PictureFileFormSet, notifySubmission,\ notifyStaff #TODO: convert to requestcontext def get_base_response(area_name=""): """ Get the base url """ base_response_dct = {'media_path':settings.MEDIA_URL,} base_url = reverse("chimere:index") # App urls must be included with namespace if not base_url.startswith('/'): base_url = '/' + base_url if area_name: if base_url[-1] != '/': base_url += '/' base_url += area_name + '/' base_response_dct['extra_url'] = base_url if settings.CHIMERE_CSS_AREAS and area_name: base_response_dct['css_area'] = area_name + ".css" base_response_dct['area_name'] = area_name base_response_dct['JQUERY_JS_URLS'] = settings.JQUERY_JS_URLS base_response_dct['JQUERY_CSS_URLS'] = settings.JQUERY_CSS_URLS return base_response_dct def index(request, area_name=None, default_area=None, simple=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 not 'last_visit' in request.session or \ request.session['last_visit'] != today: request.session['last_visit'] = today news_visible = True response_dct = get_base_response(area_name) # If the URL specify an area default_area = None if area_name: try: default_area = Area.objects.get(urn=area_name) except Area.DoesNotExist: # Do not return an error pass response_dct.update({'actions':actions, 'action_selected':('view',), 'error_message':'', 'default_area':default_area, 'news_visible': news_visible, 'areas_visible': settings.CHIMERE_DISPLAY_AREAS, 'map_layer':settings.CHIMERE_MAP_LAYER, 'dynamic_categories':settings.CHIMERE_DYNAMIC_CATEGORIES, }) tpl = 'chimere/main_map.html' if simple: tpl = 'chimere/main_map_simple.html' return render_to_response(tpl, response_dct, context_instance=RequestContext(request)) def get_edit_page(redirect_url, item_cls, item_form): """ Edition page """ def func(request, area_name="", item_id=None): response_dct = get_base_response(area_name) # 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 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] 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 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) formset_multi = MultimediaFileFormSet(request.POST, request.FILES, initial=init_multi, prefix='multimedia') formset_picture = PictureFileFormSet(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' elif not item.ref_item: # initialisation item.ref_item = item # just submited if not item.status: item.status = 'S' item.save() for f in formset_multi: f.save(item) for f in formset_picture: f.save(item) notifySubmission(item) response_dct = get_base_response(area_name) return redirect(redirect_url + '-item', area_name if area_name else '', item.ref_item.pk, 'submited'), None 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) formset_multi = MultimediaFileFormSet(initial=init_multi, prefix='multimedia') formset_picture = PictureFileFormSet(initial=init_picture, prefix='picture') return None, (item_id, init_item, response_dct, form, formset_multi, formset_picture) return func get_edit_marker = get_edit_page('chimere:edit', Marker, MarkerForm) def edit(request, area_name="", item_id=None, submited=False): """ Edition page """ response, values = get_edit_marker(request, area_name, item_id) if response: return response item_id, init_item, response_dct, form, formset_multi, formset_picture = \ values # get the "manualy" declared_fields. Ie: properties declared_fields = form.declared_fields.keys() response_dct.update({ 'actions':actions, 'action_selected':('contribute', 'edit'), 'map_layer':settings.CHIMERE_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':SubCategory.getAvailable(['M', 'B'], area_name), 'point_widget':PointChooserWidget().render('point', init_item.point if init_item else None, area_name=area_name), '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.html', response_dct, context_instance=RequestContext(request)) def uploadFile(request, category_id='', area_name=''): response_dct = get_base_response(area_name) 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) routefile.process() if not routefile.route: response_dct['errors'] = _(u"Bad file. Please check it with an " u"external software.") response_dct.update({'form':form}) return render_to_response('chimere/upload_file.html', response_dct) 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('('+simplejson.dumps({'wkt':route, 'file_id':file_id})+')', 'application/javascript', status=200) except: return HttpResponse(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 = get_edit_route(request, area_name, item_id) if response: return response item_id, init_item, response_dct, form, formset_multi, formset_picture = \ values # get the "manualy" declared_fields. Ie: properties declared_fields = form.declared_fields.keys() response_dct.update({ 'actions':actions, 'action_selected':('contribute', 'edit-route'), 'error_message':'', 'map_layer':settings.CHIMERE_MAP_LAYER, 'form':form, 'formset_multi':formset_multi, 'formset_picture':formset_picture, 'dated':settings.CHIMERE_DAYS_BEFORE_EVENT, 'extra_head':form.media, 'sub_categories':SubCategory.getAvailable(['R', 'B'], area_name), 'route_widget':RouteChooserWidget().render('route', init_item.route if init_item else None, area_name=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)) def welcome(request, display=None): """ Welcome string """ response_dct = {'display':display} news = list(News.objects.filter(available=True).all()) if settings.CHIMERE_DAYS_BEFORE_EVENT: q = checkDate(Q(status='A', start_date__isnull=False)) news += list(Marker.objects.filter(q).all()) news.sort(key=lambda x:x.date, reverse=True) response_dct['news_lst'] = news return loader.render_to_string('chimere/blocks/welcome.html', response_dct, context_instance=RequestContext(request)) def submited(request, area_name="", action=""): """ Successful submission page """ response_dct = get_base_response(area_name) response_dct.update({'actions':actions, 'action_selected':action,}) return render_to_response('chimere/submited.html', response_dct, context_instance=RequestContext(request)) def charte(request, area_name=""): """ Affichage de la charte """ response_dct = get_base_response(area_name) response_dct.update({'actions':actions, '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 = get_base_response(area_name) response_dct.update({'actions':actions, 'action_selected':('contact',), 'contact_form':form, 'message':msg}) return render_to_response('chimere/contactus.html', response_dct, context_instance=RequestContext(request)) def getDetail(request, area_name, marker_id): ''' Get the detail for a marker ''' try: marker = Marker.objects.filter(id=int(marker_id), status__in=['A', 'S'])[0] except (ValueError, IndexError): return HttpResponse('no results') response_dct = get_base_response() response_dct['marker'] = marker if request.method == 'GET': if 'simple' in request.GET and request.GET['simple']: response_dct['simple'] = True parameters = u'current_feature=%s' % marker_id parameters += u"&checked_categories=%s" % "_".join([str(m.id) \ for m in marker.categories.all()]) net_dct = getTinyfiedUrl(parameters, area_name) share_networks = [] for network in settings.SHARE_NETWORKS: share_networks.append((network[0], network[1] % net_dct, network[2])) response_dct['share_networks'] = share_networks response_dct['dated'] = settings.CHIMERE_DAYS_BEFORE_EVENT \ and marker.start_date 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 = get_base_response(area_name) 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(request, area_name, category_ids, status): ''' Get the JSON for markers and routes ''' if not status: status = 'A' status = status.split('_') category_ids = category_ids.split('_') try: q = checkDate(Q(status__in=status, categories__in=category_ids)) query = Route.objects.filter(q) except: return HttpResponse('no results') query.order_by('categories') routes = list(query) jsons = [] current_cat, colors, idx = None, None, 0 for route in routes: c_cat = route.categories.all()[0] if 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: jsons.append(route.getGeoJSON(color=colors[idx % len(colors)].code)) else: jsons.append(route.getGeoJSON(color='000')) idx += 1 try: q = checkDate(Q(status__in=status, categories__in=category_ids)) query = Marker.objects.filter(q).distinct('pk') except: return HttpResponse('no results') category_ids = [int(cat_id) for cat_id in category_ids] jsons += [geo_object.getGeoJSON(category_ids) for geo_object in list(query)] if not jsons: return HttpResponse('no results') data = '{"type": "FeatureCollection", "features":[%s]}' % ",".join(jsons) return HttpResponse(data) def get_available_categories(request, area_name=None, area=None, status='A', force=None): ''' Get categories for a designed area ''' if settings.CHIMERE_DYNAMIC_CATEGORIES and \ not "current_extent" in request.GET: return "" context_data = get_base_response(area_name) if not settings.CHIMERE_DYNAMIC_CATEGORIES: # Categories are not updated dynamicaly when the user move the map # so we return ALL the categories subcategories = SubCategory.getAvailable() context_data['sub_categories'] = subcategories return render_to_response('chimere/blocks/categories.html', context_data, context_instance=RequestContext(request)) default_message = "

%s

" % \ _("No category available in this area.") if not "status" 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) if not categories: return HttpResponse(default_message) get_cat = lambda subcat: subcat.category get_cat_order = lambda subcat: (subcat.category.order, subcat.category, 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 getTinyfiedUrl(parameters, area_name=''): ''' Get the tinyfied version of parameters ''' data = {"urn": "", "url":"", "text":""} try: urn = TinyUrl.getUrnByParameters(parameters) except: return {} response_dct = get_base_response(area_name) url = settings.SERVER_URL if url[-1] == '/': url = url[:-1] url += response_dct['extra_url'] + 'ty/' + urn 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 = get_base_response(area_name) return HttpResponseRedirect(response_dct['extra_url'] + parameters)