From b9b767af4dec1bfd13f09fac4c863c80db450090 Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Thu, 22 Nov 2012 00:26:57 +0100 Subject: Admin import: xapi specific interface (refs #449, refs #458) --- chimere/forms.py | 13 +- chimere/models.py | 5 +- chimere/static/chimere/css/forms.css | 16 ++- chimere/static/chimere/js/importer_interface.js | 66 +++++++--- chimere/widgets.py | 158 +++++++++++++++++++----- 5 files changed, 210 insertions(+), 48 deletions(-) (limited to 'chimere') diff --git a/chimere/forms.py b/chimere/forms.py index 8fafaba..63e8c52 100644 --- a/chimere/forms.py +++ b/chimere/forms.py @@ -34,8 +34,8 @@ from django.core.mail import EmailMessage, BadHeaderError from chimere.models import Marker, Route, PropertyModel, Property, Area,\ News, Category, SubCategory, RouteFile, MultimediaFile, MultimediaType, \ PictureFile, Importer, IMPORTER_CHOICES -from chimere.widgets import AreaField, PointField, TextareaWidget, \ - TextareaAdminWidget, DatePickerWidget, ImporterChoicesWidget +from chimere.widgets import AreaField, PointField, TextareaWidget, ImportFiltrWidget,\ + TextareaAdminWidget, DatePickerWidget, ImporterChoicesWidget, RE_XAPI from datetime import timedelta, datetime, tzinfo @@ -98,6 +98,7 @@ class NewsAdminForm(forms.ModelForm): model = News class ImporterAdminForm(forms.ModelForm): + filtr = forms.CharField(widget=ImportFiltrWidget, required=False) importer_type = forms.ChoiceField(widget=ImporterChoicesWidget, choices=[('', '--')]+list(IMPORTER_CHOICES)) class Meta: @@ -112,6 +113,14 @@ class ImporterAdminForm(forms.ModelForm): 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 "\ + u"provide a filter. Select an 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 "\ + u"provide a filter. Select an 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 "\ diff --git a/chimere/models.py b/chimere/models.py index 924fd54..fe38e11 100644 --- a/chimere/models.py +++ b/chimere/models.py @@ -229,13 +229,12 @@ class Importer(models.Model): ''' importer_type = models.CharField(_(u"Importer type"), max_length=4, choices=IMPORTER_CHOICES) - # URL of a KML file or a XAPI service for OSM + filtr = models.CharField(_(u"Filter"), max_length=200, + blank=True, null=True) source = models.CharField(_(u"Source"), max_length=200, blank=True, null=True) source_file = models.FileField(_(u"Source file"), upload_to='import_files', blank=True, null=True) - filtr = models.CharField(_(u"Filter"), max_length=200, - blank=True, null=True) default_name = models.CharField(_(u"Name by default"), max_length=200, blank=True, null=True) srid = models.IntegerField(_(u"SRID"), blank=True, null=True) diff --git a/chimere/static/chimere/css/forms.css b/chimere/static/chimere/css/forms.css index 46acdb9..b75f947 100644 --- a/chimere/static/chimere/css/forms.css +++ b/chimere/static/chimere/css/forms.css @@ -39,7 +39,7 @@ div.bottomform{ margin:0; border: 1px solid black; width:100%; - height:500px; + height:350px; } @@ -129,3 +129,17 @@ div.bottomform{ overflow:hidden; height:16px; } + +.form-row.field-filtr.field-map{ + float:right; + width:50%; +} + +#id_filtr{ + width:400px; +} + +.aligned .input-osm label{ + display:inline; + float:none; +} diff --git a/chimere/static/chimere/js/importer_interface.js b/chimere/static/chimere/js/importer_interface.js index 4eaacb7..fc52b4c 100644 --- a/chimere/static/chimere/js/importer_interface.js +++ b/chimere/static/chimere/js/importer_interface.js @@ -1,8 +1,9 @@ django.jQuery(function($) { var importer_form_filter = { - OSM:new Array('field-source', 'field-default_name', 'field-categories'), + OSM:new Array('field-filtr', 'field-default_name', 'field-categories', + 'field-source'), KML:new Array('field-source', 'field-source_file', 'field-default_name', - 'filed-filtr', 'field-zipped', 'field-origin', + 'field-filtr', 'field-zipped', 'field-origin', 'field-license', 'field-categories'), SHP:new Array('field-source', 'field-source_file', 'field-default_name', 'field-zipped', 'field-origin', 'field-srid', @@ -13,26 +14,63 @@ django.jQuery(function($) { 'field-origin', 'field-srid', 'field-license', 'field-categories') } -/* - field-source - field-source_file - field-filtr - field-default_name - field-srid - field-zipped - field-origin - field-license - field-categories -*/ + var map_initialized; function refresh_importer_form(){ $('.form-row').not('.field-importer_type').hide(); - var importer_val = $('.field-importer_type select').val() + var importer_val = $('.field-importer_type select').val(); if (!importer_val) return; var form_filters = importer_form_filter[importer_val]; for (k=0; k\n"\ u"\n" % ( upper_left_lat, upper_left_lon, lower_right_lat, lower_right_lon) help_msg = _(u"Hold CTRL, click and drag to select area on the map") - tpl += u"

%s

\n" % help_msg + tpl += u"

%s

\n" % help_msg tpl += u"\n
\n" + tpl += u"}\n" + if initialized: + tpl += u"$(document).ready(function($) {init_map_form()});\n" + tpl += u"\n" tpl += u"
\n" return mark_safe(tpl) @@ -455,6 +470,93 @@ class AreaWidget(forms.TextInput): values.append(value) return values +RE_XAPI = re.compile('(node|way)\[(.*=.*)\]\[bbox='\ + '(-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*)\]') + +class ImportFiltrWidget(AreaWidget): + """ + Manage the edition of the import source field + """ + class Media: + css = { + "all": settings.OSM_CSS_URLS + \ + ["%schimere/css/forms.css" % settings.STATIC_URL,] + } + js = settings.OSM_JS_URLS + [ + "%schimere/js/edit_area.js" % settings.STATIC_URL, + "%schimere/js/base.js" % settings.STATIC_URL,] + + def render(self, name, value, attrs=None): + """ + Render a map + """ + tpl = super(ImportFiltrWidget, self).render(name, value, attrs, + initialized=False) + tpl += u"

" + vals = {'lbl':_(u"Type:"), 'name':name, 'node':_(u"Node"), + 'way':_(u"Way")} + vals['way_selected'] = ' checked="checked"'\ + if self.xapi_type == 'way' else '' + vals['node_selected'] = ' checked="checked"'\ + if self.xapi_type == 'node' else '' + tpl += u"
"\ + u"
" % vals + help_msg = _(u"Enter an OSM \"tag=value\" string such as "\ + u"\"amenity=pub\". A list of common tag is available "\ + u"here.") + tpl += u"

%s

\n" % help_msg + tpl += u"
"\ + u"
" % ( + name, _(u"Tag:"), name, self.xapi_tag) + tpl += u"\n" + help_msg = _(u"Don't forget to refresh before submit") + tpl += u"

%s

\n" % help_msg + if not value: + value = '' + tpl += u"
" % (name, name, value, name, + _(u"Refresh")) + return mark_safe(tpl) + + def value_from_datadict(self, data, files, name): + """ + Return the appropriate values + """ + return data.get('id_'+name, None) + + def get_bounding_box_from_value(self, value): + ''' + Return upper left lat/lon, lower lat/lon from the input value. + Get also xapi type and xapi tag + ''' + upper_left_lat, upper_left_lon = 0, 0 + lower_right_lat, lower_right_lon = 0, 0 + self.xapi_type, self.xapi_tag, self.bounding_box = None, None, None + xapi_m = RE_XAPI.match(value) + if not xapi_m: + return upper_left_lat, upper_left_lon, lower_right_lat, \ + lower_right_lon + # as the regexp pass, we could be pretty confident + self.xapi_type, self.xapi_tag, self.bounding_box = xapi_m.groups() + upper_left_lon, upper_left_lat, lower_right_lon, lower_right_lat = \ + self.bounding_box.split(',') + return float(upper_left_lat), float(upper_left_lon), \ + float(lower_right_lat), float(lower_right_lon) + class AreaField(forms.MultiValueField): ''' Set the widget for the form field -- cgit v1.2.3