From 95d01d904eb18b8ec60f494150cbdced277dbf7d Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Wed, 15 Oct 2014 01:14:31 +0200 Subject: Dashboard: implement a form to choose criteria for the graph display --- ishtar_common/widgets.py | 658 ++++++++++++++++++++++++----------------------- 1 file changed, 332 insertions(+), 326 deletions(-) (limited to 'ishtar_common/widgets.py') diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py index 71d88d062..e0556a7b7 100644 --- a/ishtar_common/widgets.py +++ b/ishtar_common/widgets.py @@ -1,326 +1,332 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (C) 2010-2013 Étienne Loks -# Copyright (C) 2007 skam -# (http://djangosnippets.org/snippets/233/) - -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero 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 Affero General Public License for more details. - -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -# See the file COPYING for details. - -from django import forms -from django.conf import settings -from django.core.urlresolvers import resolve, reverse -from django.db.models import fields -from django.forms import ClearableFileInput -from django.forms.widgets import flatatt -from django.template import Context, loader -from django.utils.encoding import smart_unicode -from django.utils.functional import lazy -from django.utils.html import escape -from django.utils.safestring import mark_safe -from django.utils.simplejson import JSONEncoder -from django.utils.translation import ugettext_lazy as _ - -import models - -reverse_lazy = lazy(reverse, unicode) - -class MultipleAutocompleteField(forms.MultipleChoiceField): - def __init__(self, *args, **kwargs): - model = None - if 'model' in kwargs: - model = kwargs.pop('model') - if 'choices' not in kwargs and model: - kwargs['choices'] = [(i.pk, unicode(i))for i in model.objects.all()] - new = kwargs.pop('new') if 'new' in kwargs else None - if 'widget' not in kwargs and model: - kwargs['widget'] = JQueryAutoComplete(reverse_lazy( - 'autocomplete-'+model.__name__.lower()), - associated_model=model, new=new, - multiple=True) - super(MultipleAutocompleteField, self).__init__(*args, **kwargs) - - def clean(self, value): - if value: - # clean JS messup with values - try: - if type(value) not in (list, tuple): - value = [value] - else: - val = value - value = [] - for v in val: - v = unicode(v).strip('[').strip(']' - ).strip('u').strip("'").strip('"') - value += [int(v.strip()) - for v in list(set(v.split(','))) - if v.strip()] - except (TypeError, ValueError): - value = [] - else: - value = [] - return super(MultipleAutocompleteField, self).clean(value) - -class DeleteWidget(forms.CheckboxInput): - def render(self, name, value, attrs=None): - final_attrs = flatatt(self.build_attrs(attrs, name=name, - value='1')) - output = [''] - output.append(u"%s" % (final_attrs, _("Delete"))) - output.append('') - return mark_safe('\n'.join(output)) - -class ImageFileInput(ClearableFileInput): - template_with_initial = u'%(initial)s'\ - u' %(clear_template)s
%(input_text)s: %(input)s' - -class SquareMeterWidget(forms.TextInput): - def render(self, name, value, attrs=None): - if not value: - value = u"" - final_attrs = flatatt(self.build_attrs(attrs, name=name, value=value)) - dct = {'final_attrs':final_attrs, - 'unit':settings.SURFACE_UNIT_LABEL, - 'id':attrs['id'], - "safe_id":attrs['id'].replace('-', '_')} - t = loader.get_template('blocks/SquareMeterWidget.html') - rendered = t.render(Context(dct)) - return mark_safe(rendered) - -AreaWidget = forms.TextInput -if settings.SURFACE_UNIT == 'square-metre': - global AreaWidget - AreaWidget = SquareMeterWidget - -class JQueryDate(forms.TextInput): - def __init__(self, *args, **kwargs): - super(JQueryDate, self).__init__(*args, **kwargs) - if 'class' not in self.attrs: - self.attrs['class'] = '' - self.attrs['class'] = 'date-pickup' - - def render(self, name, value=None, attrs=None): - rendered = super(JQueryDate, self).render(name, value, attrs) - # use window.onload to be sure that datepicker don't interfere - # with autocomplete fields - rendered += """ - -""" % {"name":name, "country":settings.COUNTRY} - return rendered - -class JQueryAutoComplete(forms.TextInput): - def __init__(self, source, associated_model=None, options={}, attrs={}, - new=False, multiple=False, limit={}): - """ - Source can be a list containing the autocomplete values or a - string containing the url used for the request. - """ - self.options = None - self.attrs = {} - self.source = source - self.associated_model = associated_model - if len(options) > 0: - self.options = JSONEncoder().encode(options) - self.attrs.update(attrs) - self.new = new - self.multiple = multiple - self.limit = limit - - def value_from_datadict(self, data, files, name): - if self.multiple: - return data.getlist(name, None) - else: - return data.get(name, None) - - def render_js(self, field_id): - if isinstance(self.source, list): - source = JSONEncoder().encode(self.source) - elif isinstance(self.source, str) or isinstance(self.source, unicode): - source = "'%s'" % escape(self.source) - else: - try: - source = "'" + unicode(self.source) + "'" - except: - raise ValueError('source type is not valid') - dct = {'source':mark_safe(source), - 'field_id':field_id} - if self.options: - dct['options'] = mark_safe('%s' % self.options) - - js = "" - tpl = 'blocks/JQueryAutocomplete.js' - if self.multiple: - tpl = 'blocks/JQueryAutocompleteMultiple.js' - t = loader.get_template(tpl) - js = t.render(Context(dct)) - return js - - def render(self, name, value=None, attrs=None): - attrs_hidden = self.build_attrs(attrs, name=name) - attrs_select = self.build_attrs(attrs) - if value: - hiddens = [] - selects = [] - values = value - 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) - if self.associated_model: - try: - selects[-1] = unicode( - self.associated_model.objects.get(pk=v)) - except (self.associated_model.DoesNotExist, ValueError): - selects.pop() - hiddens.pop() - if self.multiple: - attrs_hidden['value'] = ", ".join(hiddens) - if selects: - selects.append("") - attrs_select['value'] = ", ".join(selects) - else: - 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' - new = '' - if self.new: - model_name = self.associated_model._meta.object_name.lower() - limits = [] - for k in self.limit: - limits.append(k + "__" + "-".join( - [unicode(v) for v in self.limit[k]])) - args = [attrs_select['id']] - if limits: - args.append(';'.join(limits)) - url_new = reverse('new-' + model_name, args=args) - new = u' +' % url_new - html = u'''%(new)s\ -\ - - ''' % { - 'attrs_select' : flatatt(attrs_select), - 'attrs_hidden' : flatatt(attrs_hidden), - 'js' : self.render_js(name), - 'new':new - } - return html - -class JQueryJqGrid(forms.RadioSelect): - COL_TPL = "{name:'%(idx)s', index:'%(idx)s', sortable:true}" - class Media: - js = ['%s/js/i18n/grid.locale-%s.js' % (settings.STATIC_URL, - settings.COUNTRY), - '%s/js/jquery.jqGrid.min.js' % settings.STATIC_URL, - ] - css = {'all':['%s/media/ui.jqgrid.css' % settings.STATIC_URL, - ]} - - def __init__(self, source, form, associated_model, attrs={}, - table_cols='TABLE_COLS', multiple=False, multiple_cols=[2], new=False, - new_message="", source_full=None): - self.source = source - self.form = form - self.attrs = attrs - self.associated_model = associated_model - self.table_cols = table_cols - self.multiple = multiple - self.multiple_cols = multiple_cols - self.new, self.new_message = new, new_message - self.source_full = source_full - - def render(self, name, value=None, attrs=None): - t = loader.get_template('blocks/form_snippet.html') - form = self.form() - rendered = t.render(Context({'form':form})) - dct = {} - if self.new: - model_name = self.associated_model._meta.object_name.lower() - dct['url_new'] = reverse('new-' + model_name, args=['0']) - dct['new_message'] = self.new_message - extra_cols = [] - col_names, col_idx = [], [] - for k in form.get_input_ids(): - #field = form.fields[k] - col_idx.append(u'"%s"' % k) - for field_name in getattr(self.associated_model, self.table_cols): - field = self.associated_model - keys = field_name.split('.') - field_verbose_name = "" - for key in keys: - if hasattr(field, 'rel'): - field = field.rel.to - try: - field = field._meta.get_field(key) - field_verbose_name = field.verbose_name - field_name = field.name - except fields.FieldDoesNotExist: - if hasattr(field, key + '_lbl'): - field_name = key - field_verbose_name = getattr(field, key + '_lbl') - else: - continue - col_names.append(u'"%s"' % field_verbose_name) - extra_cols.append(self.COL_TPL % {'idx':field_name}) - col_names = col_names and ", ".join(col_names) or "" - col_idx = col_idx and ", ".join(col_idx) or "" - extra_cols = extra_cols and ", ".join(extra_cols) or "" - dct['encoding'] = settings.ENCODING or 'utf-8' - dct['source'] = unicode(self.source) - if unicode(self.source_full) and unicode(self.source_full) != 'None': - dct['source_full'] = unicode(self.source_full) - dct.update({'name':name, - 'col_names':col_names, - 'extra_cols':extra_cols, - 'source':unicode(self.source), - 'col_idx':col_idx, - 'no_result':unicode(_("No results")), - 'loading':unicode(_("Loading...")), - 'remove':unicode(_(u"Remove")), - 'sname':name.replace('-', ''), - 'multiple':self.multiple, - 'multi_cols': ",".join((u'"%d"' % col \ - for col in self.multiple_cols)) - }) - t = loader.get_template('blocks/JQueryJqGrid.html') - rendered += t.render(Context(dct)) - return mark_safe(rendered) +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2010-2014 Étienne Loks +# Copyright (C) 2007 skam +# (http://djangosnippets.org/snippets/233/) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +# See the file COPYING for details. + +from django import forms +from django.conf import settings +from django.core.urlresolvers import resolve, reverse +from django.db.models import fields +from django.forms import ClearableFileInput +from django.forms.widgets import flatatt +from django.template import Context, loader +from django.utils.encoding import smart_unicode +from django.utils.functional import lazy +from django.utils.html import escape +from django.utils.safestring import mark_safe +from django.utils.simplejson import JSONEncoder +from django.utils.translation import ugettext_lazy as _ + +import models + +reverse_lazy = lazy(reverse, unicode) + +class MultipleAutocompleteField(forms.MultipleChoiceField): + def __init__(self, *args, **kwargs): + model = None + if 'model' in kwargs: + model = kwargs.pop('model') + if 'choices' not in kwargs and model: + kwargs['choices'] = [(i.pk, unicode(i))for i in model.objects.all()] + new = kwargs.pop('new') if 'new' in kwargs else None + if 'widget' not in kwargs and model: + kwargs['widget'] = JQueryAutoComplete(reverse_lazy( + 'autocomplete-'+model.__name__.lower()), + associated_model=model, new=new, + multiple=True) + super(MultipleAutocompleteField, self).__init__(*args, **kwargs) + + def clean(self, value): + if value: + # clean JS messup with values + try: + if type(value) not in (list, tuple): + value = [value] + else: + val = value + value = [] + for v in val: + v = unicode(v).strip('[').strip(']' + ).strip('u').strip("'").strip('"') + value += [int(v.strip()) + for v in list(set(v.split(','))) + if v.strip()] + except (TypeError, ValueError): + value = [] + else: + value = [] + return super(MultipleAutocompleteField, self).clean(value) + +class DeleteWidget(forms.CheckboxInput): + def render(self, name, value, attrs=None): + final_attrs = flatatt(self.build_attrs(attrs, name=name, + value='1')) + output = [''] + output.append(u"%s" % (final_attrs, _("Delete"))) + output.append('') + return mark_safe('\n'.join(output)) + +class ImageFileInput(ClearableFileInput): + template_with_initial = u'%(initial)s'\ + u' %(clear_template)s
%(input_text)s: %(input)s' + +class SquareMeterWidget(forms.TextInput): + def render(self, name, value, attrs=None): + if not value: + value = u"" + final_attrs = flatatt(self.build_attrs(attrs, name=name, value=value)) + dct = {'final_attrs':final_attrs, + 'unit':settings.SURFACE_UNIT_LABEL, + 'id':attrs['id'], + "safe_id":attrs['id'].replace('-', '_')} + t = loader.get_template('blocks/SquareMeterWidget.html') + rendered = t.render(Context(dct)) + return mark_safe(rendered) + +AreaWidget = forms.TextInput +if settings.SURFACE_UNIT == 'square-metre': + #global AreaWidget + AreaWidget = SquareMeterWidget + +class JQueryDate(forms.TextInput): + def __init__(self, *args, **kwargs): + super(JQueryDate, self).__init__(*args, **kwargs) + if 'class' not in self.attrs: + self.attrs['class'] = '' + self.attrs['class'] = 'date-pickup' + + def render(self, name, value=None, attrs=None): + # very specific... + if settings.COUNTRY == 'fr' and value and '/' in value: + values = value.split('/') + if len(values) == 3: + value = "%s-%s-%s" % (values[2], values[1], values[0]) + rendered = super(JQueryDate, self).render(name, value, attrs) + # use window.onload to be sure that datepicker don't interfere + # with autocomplete fields + rendered += """ + +""" % {"name":name, "country":settings.COUNTRY} + return rendered + +class JQueryAutoComplete(forms.TextInput): + def __init__(self, source, associated_model=None, options={}, attrs={}, + new=False, multiple=False, limit={}): + """ + Source can be a list containing the autocomplete values or a + string containing the url used for the request. + """ + self.options = None + self.attrs = {} + self.source = source + self.associated_model = associated_model + if len(options) > 0: + self.options = JSONEncoder().encode(options) + self.attrs.update(attrs) + self.new = new + self.multiple = multiple + self.limit = limit + + def value_from_datadict(self, data, files, name): + if self.multiple: + return data.getlist(name, None) + else: + return data.get(name, None) + + def render_js(self, field_id): + if isinstance(self.source, list): + source = JSONEncoder().encode(self.source) + elif isinstance(self.source, str) or isinstance(self.source, unicode): + source = "'%s'" % escape(self.source) + else: + try: + source = "'" + unicode(self.source) + "'" + except: + raise ValueError('source type is not valid') + dct = {'source':mark_safe(source), + 'field_id':field_id} + if self.options: + dct['options'] = mark_safe('%s' % self.options) + + js = "" + tpl = 'blocks/JQueryAutocomplete.js' + if self.multiple: + tpl = 'blocks/JQueryAutocompleteMultiple.js' + t = loader.get_template(tpl) + js = t.render(Context(dct)) + return js + + def render(self, name, value=None, attrs=None): + attrs_hidden = self.build_attrs(attrs, name=name) + attrs_select = self.build_attrs(attrs) + if value: + hiddens = [] + selects = [] + values = value + 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) + if self.associated_model: + try: + selects[-1] = unicode( + self.associated_model.objects.get(pk=v)) + except (self.associated_model.DoesNotExist, ValueError): + selects.pop() + hiddens.pop() + if self.multiple: + attrs_hidden['value'] = ", ".join(hiddens) + if selects: + selects.append("") + attrs_select['value'] = ", ".join(selects) + else: + 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' + new = '' + if self.new: + model_name = self.associated_model._meta.object_name.lower() + limits = [] + for k in self.limit: + limits.append(k + "__" + "-".join( + [unicode(v) for v in self.limit[k]])) + args = [attrs_select['id']] + if limits: + args.append(';'.join(limits)) + url_new = reverse('new-' + model_name, args=args) + new = u' +' % url_new + html = u'''%(new)s\ +\ + + ''' % { + 'attrs_select' : flatatt(attrs_select), + 'attrs_hidden' : flatatt(attrs_hidden), + 'js' : self.render_js(name), + 'new':new + } + return html + +class JQueryJqGrid(forms.RadioSelect): + COL_TPL = "{name:'%(idx)s', index:'%(idx)s', sortable:true}" + class Media: + js = ['%s/js/i18n/grid.locale-%s.js' % (settings.STATIC_URL, + settings.COUNTRY), + '%s/js/jquery.jqGrid.min.js' % settings.STATIC_URL, + ] + css = {'all':['%s/media/ui.jqgrid.css' % settings.STATIC_URL, + ]} + + def __init__(self, source, form, associated_model, attrs={}, + table_cols='TABLE_COLS', multiple=False, multiple_cols=[2], new=False, + new_message="", source_full=None): + self.source = source + self.form = form + self.attrs = attrs + self.associated_model = associated_model + self.table_cols = table_cols + self.multiple = multiple + self.multiple_cols = multiple_cols + self.new, self.new_message = new, new_message + self.source_full = source_full + + def render(self, name, value=None, attrs=None): + t = loader.get_template('blocks/form_snippet.html') + form = self.form() + rendered = t.render(Context({'form':form})) + dct = {} + if self.new: + model_name = self.associated_model._meta.object_name.lower() + dct['url_new'] = reverse('new-' + model_name, args=['0']) + dct['new_message'] = self.new_message + extra_cols = [] + col_names, col_idx = [], [] + for k in form.get_input_ids(): + #field = form.fields[k] + col_idx.append(u'"%s"' % k) + for field_name in getattr(self.associated_model, self.table_cols): + field = self.associated_model + keys = field_name.split('.') + field_verbose_name = "" + for key in keys: + if hasattr(field, 'rel'): + field = field.rel.to + try: + field = field._meta.get_field(key) + field_verbose_name = field.verbose_name + field_name = field.name + except fields.FieldDoesNotExist: + if hasattr(field, key + '_lbl'): + field_name = key + field_verbose_name = getattr(field, key + '_lbl') + else: + continue + col_names.append(u'"%s"' % field_verbose_name) + extra_cols.append(self.COL_TPL % {'idx':field_name}) + col_names = col_names and ", ".join(col_names) or "" + col_idx = col_idx and ", ".join(col_idx) or "" + extra_cols = extra_cols and ", ".join(extra_cols) or "" + dct['encoding'] = settings.ENCODING or 'utf-8' + dct['source'] = unicode(self.source) + if unicode(self.source_full) and unicode(self.source_full) != 'None': + dct['source_full'] = unicode(self.source_full) + dct.update({'name':name, + 'col_names':col_names, + 'extra_cols':extra_cols, + 'source':unicode(self.source), + 'col_idx':col_idx, + 'no_result':unicode(_("No results")), + 'loading':unicode(_("Loading...")), + 'remove':unicode(_(u"Remove")), + 'sname':name.replace('-', ''), + 'multiple':self.multiple, + 'multi_cols': ",".join((u'"%d"' % col \ + for col in self.multiple_cols)) + }) + t = loader.get_template('blocks/JQueryJqGrid.html') + rendered += t.render(Context(dct)) + return mark_safe(rendered) -- cgit v1.2.3