summaryrefslogtreecommitdiff
path: root/chimere/forms.py
diff options
context:
space:
mode:
Diffstat (limited to 'chimere/forms.py')
-rw-r--r--chimere/forms.py855
1 files changed, 855 insertions, 0 deletions
diff --git a/chimere/forms.py b/chimere/forms.py
new file mode 100644
index 0000000..8b86df1
--- /dev/null
+++ b/chimere/forms.py
@@ -0,0 +1,855 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Copyright (C) 2008-2016 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
+
+# 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.
+
+"""
+Forms
+"""
+
+from django import forms
+from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
+from django.core.urlresolvers import reverse
+from django.db.models import Q
+from django.forms.formsets import formset_factory
+from django.utils.translation import ugettext as _
+from django.contrib.auth.models import User, Permission, ContentType
+from django.core.mail import EmailMessage, BadHeaderError
+
+if hasattr(settings, 'CHIMERE_SEARCH_ENGINE') and \
+ settings.CHIMERE_SEARCH_ENGINE:
+ from haystack.forms import SearchForm as HaystackSearchForm
+
+from chimere.models import Marker, Route, PropertyModel, Area,\
+ News, Category, RouteFile, MultimediaFile, MultimediaType, \
+ PictureFile, Importer, PropertyModelChoice, Page, IMPORTER_CHOICES, \
+ get_areas_for_user, SubCategoryUserLimit, Polygon
+from chimere.widgets import AreaField, PointField, TextareaWidget, \
+ FullTextareaWidget, DatePickerWidget, ButtonSelectWidget, NominatimWidget,\
+ TextareaAdminWidget, ImportFiltrWidget, ImporterChoicesWidget, RE_XAPI
+
+from datetime import timedelta, datetime, tzinfo
+
+ZERO = timedelta(0)
+
+
+class UTC(tzinfo):
+ """UTC time zone"""
+
+ def utcoffset(self, dt):
+ return ZERO
+
+ def tzname(self, dt):
+ return settings.TIME_ZONE
+
+ def dst(self, dt):
+ return ZERO
+
+
+def getStaffEmails():
+ return [u.email for u in User.objects.filter(
+ is_superuser=True).exclude(email="").order_by('id')]
+
+
+def getRelevantModeratorEmails(geo_object):
+ categories = list(geo_object.categories.all())
+ # q = Q(is_staff=True) & (
+ # Q(subcategory_limit_to__count=0) |
+ # Q(subcategory_limit_to__subcategory__in=categories))
+ q1 = Q(is_staff=True) & (
+ Q(subcategory_limit_to__subcategory__in=categories))
+ qusers = list(User.objects.filter(q1).exclude(email="").all())
+ q2 = Q(is_staff=True)
+ qusers += list(User.objects.filter(q2).exclude(
+ email="").exclude(pk__in=[
+ sl.user.pk for sl in SubCategoryUserLimit.objects.all()]).all())
+ users = []
+ for user in qusers:
+ areas = get_areas_for_user(user)
+ if not areas:
+ users.append(user)
+ continue
+ contained = Q()
+ qs = geo_object.__class__.objects.filter(pk=geo_object.pk)
+ for area in areas:
+ if hasattr(geo_object, 'point'):
+ contained = contained | area.getIncludeMarker()
+ elif hasattr(geo_object, 'route'):
+ contained = contained | area.getIncludeRoute()
+ qs = qs.filter(contained)
+ if qs.count():
+ users.append(user)
+ lst = [u.email for u in users]
+ return lst
+
+
+def notifyStaff(subject, body, sender=None):
+ user_list = getStaffEmails()
+ notifyByEmail(subject, body, user_list, sender)
+
+
+def notifyByEmail(subject, body, user_list, sender=None):
+ if not settings.EMAIL_HOST or not user_list:
+ return
+ if settings.PROJECT_NAME:
+ subject = u'[%s] %s' % (settings.PROJECT_NAME, subject)
+ headers = {}
+ if sender:
+ headers['Reply-To'] = sender
+ email = EmailMessage(subject, body, user_list[0], user_list,
+ headers=headers)
+ try:
+ email.send()
+ except BadHeaderError:
+ return False
+ return True
+
+
+def notifySubmission(absolute_uri, geo_object):
+ category = u" - ".join([unicode(cat)
+ for cat in geo_object.categories.all()])
+ subject = u'%s %s' % (_(u"New submission for"), category)
+ message = _(u'The new item "%s" has been submited in the category: ') % \
+ geo_object.name + category
+ message += "\n\n" + _(u"To valid, precise or unvalid this item: ")
+ named_url = 'admin:chimere_%s_change' % \
+ geo_object.__class__.__name__.lower()
+ message += absolute_uri + reverse(named_url, args=(geo_object.pk,))
+ message += u"\n\n--\nChimère"
+
+ user_list = getStaffEmails() + getRelevantModeratorEmails(geo_object)
+ user_list = list(set(user_list))
+ return notifyByEmail(subject, message, user_list)
+
+
+class ContactForm(forms.Form):
+ """
+ Main form for categories
+ """
+ email = forms.EmailField(label=_("Email (optional)"), required=False)
+ content = forms.CharField(label=_("Object"), widget=forms.Textarea)
+
+
+class SubCategoryAdminForm(forms.ModelForm):
+ '''
+ Add a tinyMCE widget to fill description
+ '''
+ description = forms.CharField(widget=FullTextareaWidget, required=False)
+
+
+class PageAdminForm(forms.ModelForm):
+ """
+ Main form for extra pages
+ """
+ content = forms.CharField(widget=FullTextareaWidget)
+
+ class Meta:
+ model = Page
+
+
+class OSMForm(forms.Form):
+ """
+ OSM export form
+ """
+ username = forms.CharField(label=_("OSM user"))
+ password = forms.CharField(label=_(u"Password"),
+ widget=forms.PasswordInput(render_value=False))
+ # API URL are hardcoded: the day the API change Chimère will need
+ # adaptations not only on this portion...
+ api = forms.ChoiceField(
+ label=_(u"API"),
+ choices=(('', '--'),
+ ('api06.dev.openstreetmap.org',
+ _(u"Test API - %s") % 'api06.dev.openstreetmap.org'),
+ ('api.openstreetmap.org/api',
+ _(u"Main API - %s") % 'api.openstreetmap.org/api'),
+ ))
+
+
+class NewsAdminForm(forms.ModelForm):
+ """
+ Main form for news
+ """
+ content = forms.CharField(widget=TextareaAdminWidget)
+
+ class Meta:
+ model = News
+
+
+class ImporterAdminForm(forms.ModelForm):
+ filtr = forms.CharField(widget=ImportFiltrWidget, required=False)
+ importer_type = forms.ChoiceField(
+ widget=ImporterChoicesWidget,
+ choices=[('', '--')] + list(IMPORTER_CHOICES))
+ default_description = forms.CharField(widget=TextareaAdminWidget,
+ required=False)
+
+ class Meta:
+ model = Importer
+ widgets = {
+ 'source': forms.TextInput(attrs={'size': 80}),
+ 'filtr': forms.Textarea(attrs={'size': 80}),
+ }
+
+ def clean(self):
+ '''
+ Verify that only one type of source is provided
+ Verify that shapefiles are zipped
+ '''
+ if self.cleaned_data.get('importer_type') == 'OSM' and \
+ not self.cleaned_data.get('filtr'):
+ raise forms.ValidationError(
+ _(u"For OSM import you must be provide a filter. Select an "
+ u"area and node/way filter."))
+ if self.cleaned_data.get('importer_type') == 'OSM' and \
+ not RE_XAPI.match(self.cleaned_data.get('filtr')):
+ raise forms.ValidationError(
+ _(u"For OSM import you must be provide a filter. Select an "
+ u"area and node/way filter."))
+ if self.cleaned_data.get('importer_type') == 'SHP' and \
+ not self.cleaned_data.get('zipped'):
+ raise forms.ValidationError(_(u"Shapefiles must be provided in a "
+ u"zipped archive."))
+ if self.cleaned_data.get('importer_type') not in ('XSLT', 'XXLT') and \
+ self.cleaned_data.get('source') and \
+ self.cleaned_data.get('source_file'):
+ raise forms.ValidationError(_(u"You have to set \"source\" or "
+ u"\"source file\" but not both."))
+ if not self.cleaned_data.get('source') and \
+ not self.cleaned_data.get('source_file') and \
+ self.cleaned_data.get('importer_type') != 'OSM':
+ raise forms.ValidationError(_(u"You have to set \"source\" or "
+ u"\"source file\"."))
+ return self.cleaned_data
+
+
+class CategoryAdminForm(forms.ModelForm):
+ """
+ Main form for categories
+ """
+ description = forms.CharField(widget=TextareaAdminWidget, required=False)
+
+ class Media:
+ js = list(settings.JQUERY_JS_URLS) + [
+ '%schimere/js/menu-sort.js' % settings.STATIC_URL,
+ ]
+ css = {
+ 'all': ('chimere/css/admin.css',)
+ }
+
+ class Meta:
+ model = Category
+
+
+def get_properties(queryset):
+ # As we have dynamic fields, it's cleaner to make the class dynamic too
+ fields = {}
+ for prop in queryset:
+ key = "property_%d_%d" % (prop.order, prop.id)
+ if prop.type == 'C':
+ choices = PropertyModelChoice.objects.filter(propertymodel=prop,
+ available=True
+ ).order_by('value')
+ fields[key] = forms.ChoiceField(
+ label=prop.name, choices=[('', '--')] +
+ [(choice.pk, unicode(choice)) for choice in choices],
+ required=False)
+ elif prop.type == 'A':
+ widget = PropertyModel.TYPE_WIDGET[prop.type]
+ widget = widget(slug=prop.slug)
+ fields[key] = forms.CharField(label=prop.name, widget=widget,
+ required=False)
+ else:
+ widget = PropertyModel.TYPE_WIDGET[prop.type]
+ fields[key] = forms.CharField(
+ label=prop.name, widget=widget,
+ required=False)
+ return fields
+
+
+class MarkerAdminFormBase(forms.ModelForm):
+ """
+ Main form for marker
+ """
+ is_admin = True
+ name = forms.CharField(_(u"Name"), required=True)
+ description = forms.CharField(widget=TextareaAdminWidget, required=False)
+ _PROPERTY_FILTERS = {}
+
+ class Meta:
+ model = Marker
+
+ @classmethod
+ def _set_cls_fields(cls):
+ fields = get_properties(
+ PropertyModel.objects.filter(**cls._PROPERTY_FILTERS).all())
+ for key in fields:
+ print(key)
+ setattr(cls, key, fields[key])
+
+ def _set_fields(self):
+ fields = get_properties(
+ PropertyModel.objects.filter(**self._PROPERTY_FILTERS).all())
+ for key in fields:
+ self.fields[key] = fields[key]
+
+ def __init__(self, *args, **keys):
+ """
+ Custom initialization method in order to manage properties
+ """
+ area_name = None
+ if 'area_name' in keys:
+ area_name = keys.pop('area_name')
+ querys = PropertyModel.getAvailable(area_name=area_name)
+ self.pms = []
+ for query in querys:
+ self.pms += [pm for pm in query.all()]
+ if 'instance' in keys and keys['instance']:
+ instance = keys['instance']
+ property_dct = {}
+ for pm in self.pms:
+ property = instance.getProperty(pm)
+ if property:
+ property_dct[pm.getNamedId()] = property.value
+ if 'initial' in keys:
+ keys['initial'].update(property_dct)
+ else:
+ keys['initial'] = property_dct
+ subcategories = keys.pop('subcategories') \
+ if 'subcategories' in keys else []
+ super(MarkerAdminFormBase, self).__init__(*args, **keys)
+ self._set_fields()
+ if settings.CHIMERE_DAYS_BEFORE_EVENT:
+ self.fields['start_date'].widget = DatePickerWidget()
+ self.fields['end_date'].widget = DatePickerWidget()
+ if self.is_admin:
+ return
+ if subcategories:
+ self.fields['categories'].choices = subcategories
+ # auto select if there is only one category
+ choices = list(self.fields['categories'].choices)
+ self.fields['categories'].choices = choices
+ self.fields['categories'].label = ""
+ if (len(choices) == 1):
+ self.fields['categories'].widget = forms.MultipleHiddenInput()
+ choices = list(self.fields['categories'].choices)
+ if type(choices[0][1]) in (list, tuple):
+ # hierarchical choices
+ self.fields['categories'].label = u"{} / {}".format(
+ choices[0][0], choices[0][1][0][1])
+ self.fields['categories'].initial = \
+ [self.fields['categories'].choices[0][1][0][0]]
+ else:
+ self.fields['categories'].label = u"{}".format(
+ choices[0][1])
+ self.fields['categories'].initial = \
+ [self.fields['categories'].choices[0][0]]
+ if not settings.CHIMERE_SEARCH_ENGINE and 'keywords' in self.fields:
+ self.fields.pop('keywords')
+ if not settings.CHIMERE_DAYS_BEFORE_EVENT:
+ self.fields.pop('start_date')
+ self.fields.pop('end_date')
+ # not a clean way to filter properties...
+ # to do: change creation process
+ pms = [pm.getNamedId() for pm in self.pms]
+ for k in self.fields.keys():
+ if not k.startswith('property_') or \
+ k in pms:
+ continue
+ self.fields.pop(k)
+
+ def clean(self):
+ '''
+ Verify that a start date is provided when an end date is set
+ Verify the mandatory properties (to be check manualy because it depends
+ on the checked categories)
+ '''
+ start_date = self.cleaned_data.get('start_date')
+ end_date = self.cleaned_data.get('end_date')
+ if end_date and not start_date:
+ msg = _(u"End date has been set with no start date")
+ self._errors["end_date"] = self.error_class([msg])
+ del self.cleaned_data['end_date']
+ if end_date and start_date and start_date > end_date:
+ msg = _(u"End date can't be before start date")
+ self._errors["end_date"] = self.error_class([msg])
+ raise forms.ValidationError(msg)
+ for pm in self.pms:
+ if not pm.mandatory or self.cleaned_data[pm.getNamedId()]:
+ continue
+ pm_cats = pm.subcategories.all()
+ if not pm_cats or \
+ [submited_cat for submited_cat in
+ self.cleaned_data['categories']
+ if submited_cat in pm_cats]:
+ msg = _(u"This field is mandatory for the selected categories")
+ self._errors[pm.getNamedId()] = self.error_class([msg])
+ # raise forms.ValidationError()
+ return self.cleaned_data
+
+ def save(self, *args, **keys):
+ """
+ Custom save method in order to manage associated properties
+ """
+ new_marker = super(MarkerAdminFormBase, self).save(*args, **keys)
+ if 'status' not in self.cleaned_data and not new_marker.status:
+ new_marker.status = 'S'
+ if new_marker.status == 'A':
+ tz = UTC()
+ new_marker.available_date = datetime.replace(datetime.utcnow(),
+ tzinfo=tz)
+ new_marker.save()
+ # save properties
+ properties = dict(
+ [(k.split('_')[-1], self.cleaned_data[k])
+ for k in self.cleaned_data.keys() if k.startswith('property_')])
+ new_marker.saveProperties(properties)
+ return new_marker
+
+
+class MarkerAdminForm(MarkerAdminFormBase):
+ pass
+
+
+class MarkerBaseForm(MarkerAdminFormBase):
+ # in public form only visible fields are displayed
+ _PROPERTY_FILTERS = {'available': True}
+
+
+EXCLUDED_FIELDS = ['status']
+if not settings.CHIMERE_SEARCH_ENGINE:
+ EXCLUDED_FIELDS.append('keywords')
+
+
+class MarkerForm(MarkerBaseForm):
+ """
+ Form for the edit page
+ """
+ is_admin = False
+ ref_pk = forms.IntegerField(label=u" ", widget=forms.HiddenInput(),
+ required=False)
+ description = forms.CharField(widget=TextareaWidget, required=False)
+ keywords = forms.CharField(widget=TextareaWidget, max_length=200,
+ required=False)
+
+ class Meta:
+ model = Marker
+ exclude = EXCLUDED_FIELDS
+ widgets = {
+ 'description': TextareaWidget(),
+ }
+
+
+class RouteAdminForm(forms.ModelForm):
+ """
+ Main form for route
+ """
+ is_admin = True
+ name = forms.CharField(_(u"Name"), required=True)
+
+ class Meta:
+ model = Route
+
+ def __init__(self, *args, **keys):
+ """
+ Custom initialization method in order to manage properties
+ """
+ area_name = None
+ if 'area_name' in keys:
+ area_name = keys.pop('area_name')
+ querys = PropertyModel.getAvailable(area_name=area_name)
+ self.pms = []
+ for query in querys:
+ self.pms += [pm for pm in query.all()]
+ if 'instance' in keys and keys['instance']:
+ instance = keys['instance']
+ property_dct = {}
+ for pm in PropertyModel.objects.filter(available=True):
+ property = instance.getProperty(pm)
+ if property:
+ property_dct[pm.getNamedId()] = property.value
+ if 'initial' in keys:
+ keys['initial'].update(property_dct)
+ else:
+ keys['initial'] = property_dct
+ subcategories = keys.pop('subcategories') \
+ if 'subcategories' in keys else []
+ super(RouteAdminForm, self).__init__(*args, **keys)
+ if self.is_admin:
+ return
+ if not settings.CHIMERE_SEARCH_ENGINE and 'keywords' in self.fields:
+ self.fields.pop('keywords')
+ if settings.CHIMERE_DAYS_BEFORE_EVENT:
+ self.fields['start_date'].widget = DatePickerWidget()
+ self.fields['end_date'].widget = DatePickerWidget()
+ self.fields['categories'].choices = []
+ if subcategories:
+ self.fields['categories'].choices = subcategories
+ # not a clean way to filter properties...
+ # to do: change creation process
+ pms = [pm.getNamedId() for pm in self.pms]
+ for k in self.fields.keys():
+ if not k.startswith('property_') or \
+ k in pms:
+ continue
+ self.fields.pop(k)
+
+ def save(self, *args, **keys):
+ """
+ Custom save method in order to manage associated properties
+ """
+ new_route = super(RouteAdminForm, self).save(*args, **keys)
+ if 'status' not in self.cleaned_data and not new_route.status:
+ new_route.status = 'S'
+ new_route.save()
+ return new_route
+
+
+class RouteForm(RouteAdminForm):
+ """
+ Form for the edit page
+ """
+ is_admin = False
+ description = forms.CharField(widget=TextareaWidget, required=False)
+ point = forms.CharField(label=" ", required=False,
+ widget=forms.HiddenInput)
+ associated_file_id = forms.CharField(
+ label=" ", required=False, widget=forms.HiddenInput)
+ keywords = forms.CharField(widget=TextareaWidget, max_length=200,
+ required=False)
+
+ class Meta:
+ model = Route
+ exclude = EXCLUDED_FIELDS
+
+ def __init__(self, *args, **kwargs):
+ if kwargs.get('instance'):
+ try:
+ marker = Marker.objects.get(route=kwargs['instance'])
+ kwargs['initial'] = {
+ 'point': marker.point,
+ 'description': marker.description}
+ property_dct = {}
+ for pm in PropertyModel.objects.filter(available=True):
+ property = marker.getProperty(pm)
+ if property:
+ property_dct[pm.getNamedId()] = property.value
+ if 'initial' in kwargs:
+ kwargs['initial'].update(property_dct)
+ else:
+ kwargs['initial'] = property_dct
+ except:
+ pass
+ super(RouteForm, self).__init__(*args, **kwargs)
+
+ def save(self, *args, **keys):
+ """
+ Custom save method in order to manage associated marker and file
+ """
+ new_route = super(RouteForm, self).save(*args, **keys)
+ if new_route.status == 'S':
+ new_route.has_associated_marker = True
+ new_route.save()
+ # associate a route file
+ if 'associated_file_id' in self.cleaned_data and \
+ self.cleaned_data['associated_file_id']:
+ file_pk = int(self.cleaned_data['associated_file_id'])
+ new_route.associated_file = RouteFile.objects.get(pk=file_pk)
+ new_route.save()
+ # change the associated marker (if available)
+ q_new_marker = Marker.objects.filter(route=new_route)
+ if not q_new_marker.count():
+ return new_route
+ new_marker = q_new_marker.all()[0]
+ # save description
+ if self.cleaned_data['description']:
+ new_marker.description = self.cleaned_data['description']
+ new_marker.save()
+ # save properties
+ properties = dict(
+ [(k.split('_')[-1], self.cleaned_data[k])
+ for k in self.cleaned_data.keys() if k.startswith('property_')])
+ new_marker.saveProperties(properties)
+ return new_route
+
+
+class PolygonAdminForm(MarkerAdminForm):
+ """
+ Main form for polygon
+ """
+ class Meta:
+ model = Polygon
+
+
+class PolygonForm(PolygonAdminForm):
+ """
+ Form for the edit page
+ """
+ is_admin = False
+ description = forms.CharField(widget=TextareaWidget, required=False)
+
+ class Meta:
+ model = Polygon
+ exclude = EXCLUDED_FIELDS
+
+
+class BaseFileForm(forms.ModelForm):
+ id = forms.IntegerField(label=u"", widget=forms.HiddenInput(),
+ required=False)
+
+ def __init__(self, *args, **kwargs):
+ if not hasattr(self, '_related_name') or not self._related_name:
+ raise ImproperlyConfigured
+ super(BaseFileForm, self).__init__(*args, **kwargs)
+ self.fields.pop('marker')
+ self.fields.pop('polygon')
+
+ def save(self, associated_item):
+ if not hasattr(self, 'cleaned_data') or not self.cleaned_data:
+ return
+ instance = None
+ if self.cleaned_data.get('id'):
+ try:
+ instance = self._meta.model.objects.get(
+ pk=self.cleaned_data['id'])
+ except:
+ pass
+ self.cleaned_data.pop('id')
+ if self.cleaned_data.get('DELETE'):
+ if instance:
+ instance.delete()
+ return
+ self.cleaned_data.pop('DELETE')
+ if type(associated_item) == Marker:
+ self.cleaned_data['marker'] = associated_item
+ if type(associated_item) == Polygon:
+ self.cleaned_data['polygon'] = associated_item
+ if instance:
+ for k in self.cleaned_data:
+ setattr(instance, k, self.cleaned_data[k])
+ instance.save()
+ else:
+ instance = self._meta.model.objects.create(**self.cleaned_data)
+
+
+class MultimediaFileAdminForm(forms.ModelForm):
+ class Meta:
+ model = MultimediaFile
+
+ class Media:
+ js = list(settings.JQUERY_JS_URLS) + [
+ '%schimere/js/menu-sort.js' % settings.STATIC_URL,
+ ]
+
+ def __init__(self, *args, **kwargs):
+ super(MultimediaFileAdminForm, self).__init__(*args, **kwargs)
+ self.fields['multimedia_type'].widget.choices = \
+ MultimediaType.get_tuples()
+
+
+class MultimediaFileForm(BaseFileForm):
+ """
+ Form for a multimedia file
+ """
+ _related_name = 'multimedia_files'
+
+ class Meta:
+ model = MultimediaFile
+ exclude = ('order',)
+
+ def __init__(self, *args, **kwargs):
+ super(MultimediaFileForm, self).__init__(*args, **kwargs)
+ self.fields['multimedia_type'].widget.choices = \
+ MultimediaType.get_tuples()
+ # this can be auto detect
+ self.fields['multimedia_type'].required = False
+
+MultimediaFileFormSet = formset_factory(MultimediaFileForm, can_delete=True)
+
+
+class PictureFileAdminForm(forms.ModelForm):
+ class Meta:
+ model = PictureFile
+
+ class Media:
+ js = list(settings.JQUERY_JS_URLS) + [
+ '%schimere/js/menu-sort.js' % settings.STATIC_URL,
+ ]
+
+
+class PictureFileForm(BaseFileForm):
+ """
+ Form for a picture file
+ """
+ _related_name = 'pictures'
+
+ class Meta:
+ model = PictureFile
+ exclude = ('order', 'height', 'width', 'thumbnailfile',
+ 'thumbnailfile_height', 'thumbnailfile_width')
+
+PictureFileFormSet = formset_factory(PictureFileForm, can_delete=True)
+
+
+class FileForm(forms.Form):
+ raw_file = forms.FileField(label=_(u"File"))
+
+ def clean_raw_file(self):
+ data = self.cleaned_data['raw_file']
+ if '.' not in data.name or \
+ data.name.split('.')[-1].lower() not in ('kml', 'gpx'):
+ raise forms.ValidationError(_(u"Bad file format: this must be a "
+ u"GPX or KML file"))
+ return data
+
+
+class FullFileForm(FileForm):
+ name = forms.CharField(label=_(u"Name"), max_length=150)
+
+ def __init__(self, *args, **kwargs):
+ super(FullFileForm, self).__init__(*args, **kwargs)
+ self.fields.keyOrder = ['name', 'raw_file']
+
+
+class AreaAdminForm(forms.ModelForm):
+ """
+ Admin page to create an area
+ """
+ area = AreaField(label=_("Area"), fields=(PointField(), PointField()))
+ welcome_message = forms.CharField(widget=TextareaAdminWidget,
+ required=False)
+
+ class Meta:
+ model = Area
+
+ def __init__(self, *args, **keys):
+ """
+ Custom initialization method in order to manage area
+ """
+ if args:
+ vals = args[0]
+ for k in ('upper_left_lat', 'upper_left_lon',
+ 'lower_right_lat', 'lower_right_lon'):
+ v = vals.get(k)
+ try:
+ v = float(v)
+ except ValueError:
+ v = None
+ if not v:
+ args[0][k] = None
+ if 'instance' in keys and keys['instance']:
+ instance = keys['instance']
+ dct = {'area': (instance.upper_left_corner,
+ instance.lower_right_corner)}
+ if 'initial' in keys:
+ keys['initial'].update(dct)
+ else:
+ keys['initial'] = dct
+ super(AreaAdminForm, self).__init__(*args, **keys)
+
+ def clean(self):
+ '''
+ Verify that the area is not empty
+ '''
+ if not self.cleaned_data.get('upper_left_lat') \
+ and not self.cleaned_data.get('upper_left_lon') \
+ and not self.cleaned_data.get('lower_right_lat') \
+ and not self.cleaned_data.get('lower_right_lon') \
+ and not self.cleaned_data.get('area'):
+ msg = _(u"No area selected.")
+ raise forms.ValidationError(msg)
+ if self.cleaned_data.get('order'):
+ q = Area.objects.filter(order=self.cleaned_data.get('order'))
+ if self.instance:
+ q = q.exclude(pk=self.instance.pk)
+ if q.count():
+ msg = _(u"The area \"%s\" has the same order, you need to"
+ u" choose another one.") % unicode(q.all()[0])
+ raise forms.ValidationError(msg)
+ return self.cleaned_data
+
+ def save(self, *args, **keys):
+ """
+ Custom save method in order to manage area
+ """
+ new_area = super(AreaAdminForm, self).save(*args, **keys)
+ area = self.cleaned_data['area']
+ new_area.upper_left_corner = 'POINT(%s %s)' % (area[0][0], area[0][1])
+ new_area.lower_right_corner = 'POINT(%s %s)' % (area[1][0],
+ area[1][1])
+ content_type = ContentType.objects.get(app_label="chimere",
+ model="area")
+ if new_area.urn:
+ mnemo = 'change_area_' + new_area.urn
+ perm = Permission.objects.filter(codename=mnemo)
+ if not perm:
+ perm = Permission(
+ name='Can change ' + new_area.name,
+ content_type_id=content_type.id, codename=mnemo)
+ perm.save()
+ else:
+ if 'urn' in self.initial:
+ mnemo = 'change_area_' + self.initial['urn']
+ perm = Permission.objects.filter(codename=mnemo)
+ if perm:
+ perm[0].delete()
+ return new_area
+
+
+class AreaForm(AreaAdminForm):
+ """
+ Form for the edit page
+ """
+ class Meta:
+ model = Area
+
+CHIMERE_ROUTING_TRANSPORT = []
+ROUTING_INIT = None
+if hasattr(settings, 'CHIMERE_ROUTING_TRANSPORT'):
+ CHIMERE_ROUTING_TRANSPORT = [
+ (idx, _(lbl)) for idx, lbl in settings.CHIMERE_ROUTING_TRANSPORT]
+ if CHIMERE_ROUTING_TRANSPORT:
+ ROUTING_INIT = CHIMERE_ROUTING_TRANSPORT[0][0]
+
+
+class RoutingForm(forms.Form):
+ transport = forms.ChoiceField(label='', widget=ButtonSelectWidget,
+ choices=CHIMERE_ROUTING_TRANSPORT,
+ initial=ROUTING_INIT)
+ start = forms.CharField(label=_(u"Start"), widget=NominatimWidget)
+ end = forms.CharField(label=_(u"Finish"), widget=NominatimWidget)
+ speed = forms.ChoiceField(label=_(u"Speed"), choices=[],
+ required=False, widget=forms.RadioSelect)
+
+ def __init__(self, *args, **kwargs):
+ super(RoutingForm, self).__init__(*args, **kwargs)
+ if not settings.CHIMERE_ROUTING_SPEEDS:
+ self.fields.pop('speed')
+ self.fields['speed'].widget.choices = []
+ for transport in settings.CHIMERE_ROUTING_SPEEDS:
+ for speed, lbl in settings.CHIMERE_ROUTING_SPEEDS[transport]:
+ self.fields['speed'].widget.choices.append(
+ ("%s_%d" % (transport, speed), _(lbl)))
+
+SearchForm = None
+
+
+if hasattr(settings, 'CHIMERE_SEARCH_ENGINE') \
+ and settings.CHIMERE_SEARCH_ENGINE:
+ class SearchForm(HaystackSearchForm):
+ pass