diff options
Diffstat (limited to 'chimere/forms.py')
| -rw-r--r-- | chimere/forms.py | 855 | 
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 | 
