summaryrefslogtreecommitdiff
path: root/ishtar_common
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@proxience.com>2014-12-30 16:59:44 +0100
committerÉtienne Loks <etienne.loks@proxience.com>2015-05-06 15:38:32 +0200
commitfea9c2d83c910d890ee1600545a61d88e9df6851 (patch)
tree52e8f0c1333377628b60f0feefad64861980519f /ishtar_common
parentfa7d0e715228ccec391f6cc87b69abd50f442ef9 (diff)
downloadIshtar-fea9c2d83c910d890ee1600545a61d88e9df6851.tar.bz2
Ishtar-fea9c2d83c910d890ee1600545a61d88e9df6851.zip
Work on new town field (with state and department) - work on new UI for files
Diffstat (limited to 'ishtar_common')
-rw-r--r--ishtar_common/forms_common.py8
-rw-r--r--ishtar_common/static/media/style.css4
-rw-r--r--ishtar_common/tasks.py35
-rw-r--r--ishtar_common/templates/blocks/JQueryAdvancedTown.html99
-rw-r--r--ishtar_common/templates/blocks/JQueryAdvancedTown.js1
-rw-r--r--ishtar_common/urls.py4
-rw-r--r--ishtar_common/views.py39
-rw-r--r--ishtar_common/widgets.py101
-rw-r--r--ishtar_common/wizards.py7
9 files changed, 292 insertions, 6 deletions
diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py
index e1a0e9063..23d7126c9 100644
--- a/ishtar_common/forms_common.py
+++ b/ishtar_common/forms_common.py
@@ -54,6 +54,14 @@ def get_town_field(label=_(u"Town"), required=True):
validators=[models.valid_id(models.Town)], label=label,
help_text=mark_safe(help_text), required=required)
+def get_advanced_town_field(label=_(u"Town"), required=True):
+ # !FIXME hard_link, reverse_lazy doen't seem to work with formsets
+ return forms.IntegerField(
+ widget=widgets.JQueryTown("/" + settings.URL_PATH + \
+ 'autocomplete-advanced-town'),
+ validators=[models.valid_id(models.Town)], label=label,
+ required=required)
+
def get_person_field(label=_(u"Person"), required=True, person_types=[]):
# !FIXME hard_link, reverse_lazy doen't seem to work with formsets
widget = None
diff --git a/ishtar_common/static/media/style.css b/ishtar_common/static/media/style.css
index 55bb95e30..a1b084002 100644
--- a/ishtar_common/static/media/style.css
+++ b/ishtar_common/static/media/style.css
@@ -906,6 +906,10 @@ a.remove{
width:60px;
}
+.small, .small input{
+ width:60px;
+}
+
#progress{
display:none;
position:fixed;
diff --git a/ishtar_common/tasks.py b/ishtar_common/tasks.py
index a9db26087..a8db97bb1 100644
--- a/ishtar_common/tasks.py
+++ b/ishtar_common/tasks.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-# Copyright (C) 2013 Étienne Loks <etienne.loks_AT_peacefrogsDOTnet>
+# Copyright (C) 2013-2014 É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 Affero General Public License as
@@ -17,13 +17,15 @@
# See the file COPYING for details.
+import sys
+
from django.conf import settings
from django.db.models import Q
-from geodjangofla.models import Commune
-from ishtar_common.models import Town
+from ishtar_common.models import Town, Department
def load_towns():
+ from geodjangofla.models import Commune
q = None
for dpt_number in settings.ISHTAR_DPTS:
query = Q(insee_com__istartswith=dpt_number)
@@ -51,3 +53,30 @@ def load_towns():
setattr(town, k, defaults[k])
town.save()
return nb, updated
+
+def update_towns():
+ nb, updated = 0, 0
+ dpts = dict([(dpt.number, dpt) for dpt in Department.objects.all()])
+ q = Town.objects.filter(numero_insee__isnull=False)
+ total = q.count()
+ for idx, town in enumerate(q.all()):
+ sys.stdout.write('\rProcessing... %s/%d' % (
+ str(idx+1).zfill(len(str(total))), total))
+ if len(town.numero_insee) < 2:
+ continue
+ dpt_code = town.numero_insee[:2]
+ if dpt_code.startswith('9') and int(dpt_code) > 95:
+ dpt_code = town.numero_insee[:3]
+ if dpt_code not in dpts:
+ sys.stdout.write('Missing department with INSEE code: %s' % dpt_code)
+ continue
+ if town.departement == dpts[dpt_code]:
+ continue
+ if town.departement:
+ updated += 1
+ else:
+ nb += 1
+ town.departement = dpts[dpt_code]
+ town.save()
+ sys.stdout.write('\n')
+ return nb, updated
diff --git a/ishtar_common/templates/blocks/JQueryAdvancedTown.html b/ishtar_common/templates/blocks/JQueryAdvancedTown.html
new file mode 100644
index 000000000..78d2d7831
--- /dev/null
+++ b/ishtar_common/templates/blocks/JQueryAdvancedTown.html
@@ -0,0 +1,99 @@
+{% load i18n %}{% load url from future %}</td></tr>
+<tr>
+ <td>{% trans "State" context "Région" %}</td>
+ <td>
+ <select id='current-state'>
+ <option value=''>--------</option>{% for state in states %}
+ <option value='{{state.number}}'{% if state.number == selected_state %}selected='selected'{% endif %}>{{state}}</option>
+ {% endfor %}</select>
+ </td>
+</tr>
+<tr>
+ <td>{% trans "Department" %}</td>
+ <td>
+ <select id='current-department'>
+ </select>
+ </td>
+</tr>
+<tr class='required'>
+ <th><label>{% trans "Town" %}</label></th>
+ <td><input{{attrs_select}}/>
+<input type="hidden"{{attrs_hidden}}/>
+<script type="text/javascript"><!--//
+ selected_department = "{{selected_department}}";
+ var empty_select = "<option value=''>--------</option>";
+
+ function update_department_field(){
+ var selected_state = $("#current-state").val();
+ if (!selected_state){
+ $("#current-department").html("<option value=''>"+"{% trans 'Choose a state first' %}"+"</option>");
+ $("#current-department").prop('disabled', true);
+ $('#id_select_{{field_id}}').prop('disabled', true);
+ return;
+ }
+ $.ajax({
+ url: "{% url 'department-by-state' %}" + selected_state,
+ type: 'get',
+ dataType: 'json',
+ success: function(data) {
+ var html = "";
+ for (idx in data){
+ dpt = data[idx];
+ html += "<option value='" + dpt.number + "'";
+ if (String(dpt.number) == String(selected_department)){
+ html += " selected='selected'";
+ }
+ html += ">" + dpt.value + "</option>";
+ }
+ $("#current-department").html(html);
+ $("#current-department").prop('disabled', false);
+ update_search_town();
+ }
+ });
+ }
+
+ function update_search_town(){
+ selected_department = $("#current-department").val();
+ if (selected_department){
+ $("#id_select_{{field_id}}").autocomplete( "option", "source", {{source}}+selected_department);
+ $('#id_select_{{field_id}}').prop('disabled', false);
+ } else {
+ $('#id_select_{{field_id}}').prop('disabled', true);
+ }
+ }
+
+ function empty_town(){
+ $('#id_{{field_id}}').val(null);
+ $('#id_select_{{field_id}}').val(null);
+ }
+
+ $(function() {
+ update_department_field();
+
+ $("#current-state").change(function(){
+ empty_town();
+ update_department_field();
+ });
+
+ $("#current-department").change(function(){
+ empty_town();
+ update_search_town();
+ });
+
+ $("#id_select_{{field_id}}").autocomplete({
+ source: {{source}},
+ select: function( event, ui ) {
+ if(ui.item){
+ $('#id_{{field_id}}').val(ui.item.id);
+ } else {
+ $('#id_{{field_id}}').val(null);
+ }
+ },
+ minLength: 2{% if options %},
+ {{options}}
+ {% endif %}
+ });
+
+ $('#id_select_{{field_id}}').live('click', empty_town);
+
+});//--></script>
diff --git a/ishtar_common/templates/blocks/JQueryAdvancedTown.js b/ishtar_common/templates/blocks/JQueryAdvancedTown.js
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/ishtar_common/templates/blocks/JQueryAdvancedTown.js
@@ -0,0 +1 @@
+
diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py
index d31d0d359..0c4059eeb 100644
--- a/ishtar_common/urls.py
+++ b/ishtar_common/urls.py
@@ -98,8 +98,12 @@ urlpatterns += patterns('ishtar_common.views',
name='get-person'),
url(r'show-person(?:/(?P<pk>.+))?/(?P<type>.+)?$',
'show_person', name='show-person'),
+ url(r'department-by-state/(?P<state_id>.+)?$', 'department_by_state',
+ name='department-by-state'),
url(r'autocomplete-town/?$', 'autocomplete_town',
name='autocomplete-town'),
+ url(r'autocomplete-advanced-town/(?P<department_id>[0-9]+[ABab]?)?$',
+ 'autocomplete_advanced_town', name='autocomplete-advanced-town'),
url(r'autocomplete-department/?$', 'autocomplete_department',
name='autocomplete-department'),
url(r'new-author/(?:(?P<parent_name>[^/]+)/)?(?:(?P<limits>[^/]+)/)?$',
diff --git a/ishtar_common/views.py b/ishtar_common/views.py
index a8ab91fb9..66b488254 100644
--- a/ishtar_common/views.py
+++ b/ishtar_common/views.py
@@ -202,6 +202,45 @@ def autocomplete_town(request):
for town in towns])
return HttpResponse(data, mimetype='text/plain')
+def autocomplete_advanced_town(request, department_id=None, state_id=None):
+ if not request.GET.get('term'):
+ return HttpResponse(mimetype='text/plain')
+ q = request.GET.get('term')
+ q = unicodedata.normalize("NFKD", q).encode('ascii','ignore')
+ query = Q()
+ for q in q.split(' '):
+ extra = Q(name__icontains=q)
+ if settings.COUNTRY == 'fr':
+ extra = extra | Q(numero_insee__istartswith=q)
+ if not department_id:
+ extra = extra | Q(departement__label__istartswith=q)
+ query = query & extra
+ if department_id:
+ query = query & Q(departement__number__iexact=department_id)
+ if state_id:
+ query = query & Q(departement__state__number__iexact=state_id)
+ limit = 20
+ towns = models.Town.objects.filter(query)[:limit]
+ result = []
+ for town in towns:
+ val = town.name
+ if hasattr(town, 'numero_insee'):
+ val += " (%s)" % town.numero_insee
+ result.append({'id':town.pk, 'value':val})
+ data = json.dumps(result)
+ return HttpResponse(data, mimetype='text/plain')
+
+def department_by_state(request, state_id=''):
+ if not state_id:
+ data = []
+ else:
+ departments = models.Department.objects.filter(state__number=state_id)
+ data = json.dumps([{'id':department.pk, 'number':department.number,
+ 'value':unicode(department)}
+ for department in departments])
+ return HttpResponse(data, mimetype='text/plain')
+
+
from types import NoneType
def format_val(val):
diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py
index 91594edbd..ba7e61e46 100644
--- a/ishtar_common/widgets.py
+++ b/ishtar_common/widgets.py
@@ -34,7 +34,7 @@ from django.utils.safestring import mark_safe
from django.utils.simplejson import JSONEncoder
from django.utils.translation import ugettext_lazy as _
-import models
+from ishtar_common import models
reverse_lazy = lazy(reverse, unicode)
@@ -283,6 +283,105 @@ class JQueryAutoComplete(forms.TextInput):
}
return html
+class JQueryTown(forms.TextInput):
+ """
+ Town fields whith state and department pre-selections
+ """
+
+ def __init__(self, source, options={},
+ attrs={}, new=False, limit={}):
+ self.options = None
+ self.attrs = {}
+ self.source = source
+ if len(options) > 0:
+ self.options = JSONEncoder().encode(options)
+ self.attrs.update(attrs)
+ self.new = new
+ self.limit = limit
+
+ @classmethod
+ def encode_source(cls, source):
+ encoded_src = ''
+ if isinstance(source, list):
+ encoded_src = JSONEncoder().encode(source)
+ elif isinstance(source, str) \
+ or isinstance(source, unicode):
+ src = escape(source)
+ if not src.endswith('/'):
+ src += "/"
+ encoded_src = "'%s'" % src
+ else:
+ try:
+ src = unicode(source)
+ if not src.endswith('/'):
+ src += "/"
+ encoded_src = "'%s'" % src
+ except:
+ raise ValueError('source type is not valid')
+ return encoded_src
+
+ def render(self, name, value=None, attrs=None):
+ attrs_hidden = self.build_attrs(attrs, name=name)
+ attrs_select = self.build_attrs(attrs)
+ attrs_select['placeholder'] = _(u"Search...")
+ selected = ''
+ selected_state = ''
+ selected_department = ''
+ if value:
+ hiddens = []
+ selects = []
+ if type(value) not in (list, tuple):
+ values = unicode(escape(smart_unicode(value)))
+ values = values.replace('[', '').replace(']', '')
+ values = values.split(',')
+ else:
+ values = []
+ for v in value:
+ values += v.split(',')
+ for v in values:
+ if not v:
+ continue
+ hiddens.append(v)
+ selects.append(v)
+ try:
+ item = models.Town.objects.get(pk=v)
+ selects[-1] = unicode(item)
+ if item.departement:
+ selected_department = item.departement.number
+ if item.departement.state:
+ selected_state = item.departement.state.number
+ selected = item.pk
+ except (models.Town.DoesNotExist, ValueError):
+ selects.pop()
+ hiddens.pop()
+ if hiddens and selects:
+ attrs_hidden['value'] = hiddens[0]
+ attrs_select['value'] = selects[0]
+ if not self.attrs.has_key('id'):
+ attrs_hidden['id'] = 'id_%s' % name
+ attrs_select['id'] = 'id_select_%s' % name
+ if 'class' not in attrs_select:
+ attrs_select['class'] = 'autocomplete'
+
+ source = self.encode_source(self.source)
+ dct = {'source':mark_safe(source),
+ 'selected':selected,
+ 'safe_field_id':slugify(name).replace('-', '_'),
+ 'field_id':name}
+ if self.options:
+ dct['options'] = mark_safe('%s' % self.options)
+
+ dct.update({'attrs_select':mark_safe(flatatt(attrs_select)),
+ 'attrs_hidden':mark_safe(flatatt(attrs_hidden)),
+ 'name':name,
+ 'states':models.State.objects.all().order_by('label'),
+ 'selected_department':selected_department,
+ 'selected_state':selected_state
+ })
+ html = loader.get_template('blocks/JQueryAdvancedTown.html').render(
+ Context(dct))
+ return html
+
class JQueryPersonOrganization(forms.TextInput):
"""
Complex widget which manage:
diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py
index 190a7fc86..2ad3635d7 100644
--- a/ishtar_common/wizards.py
+++ b/ishtar_common/wizards.py
@@ -95,8 +95,11 @@ class Wizard(NamedUrlWizardView):
def get_template_names(self):
templates = ['ishtar/wizard/default_wizard.html']
current_step = self.steps.current
- if current_step in self.wizard_templates:
- templates = [self.wizard_templates[current_step]] + templates
+ wizard_templates = dict([
+ (key % {'url_name':self.url_name}, self.wizard_templates[key])
+ for key in self.wizard_templates])
+ if current_step in wizard_templates:
+ templates = [wizard_templates[current_step]] + templates
elif current_step == self.steps.last:
templates = ['ishtar/wizard/confirm_wizard.html'] + templates
return templates