diff options
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/models.py | 30 | ||||
-rw-r--r-- | ishtar_common/static/media/style.css | 75 | ||||
-rw-r--r-- | ishtar_common/templates/base.html | 2 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/dashboards/dashboard_main.html | 6 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/dashboards/dashboard_main_detail.html | 110 | ||||
-rw-r--r-- | ishtar_common/templates/ishtar/dashboards/dashboard_main_detail_users.html | 8 | ||||
-rw-r--r-- | ishtar_common/views.py | 55 | ||||
-rw-r--r-- | ishtar_common/widgets.py | 658 |
8 files changed, 514 insertions, 430 deletions
diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 5c6e208d7..39bb4aeb0 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -561,9 +561,9 @@ class UserDashboard: .order_by('person__person_types') class Dashboard: - def __init__(self, model): + def __init__(self, model, slice='year', fltr={}): self.model = model - self.total_number = model.get_total_number() + self.total_number = model.get_total_number(fltr) history_model = self.model.history.model # last edited - created self.recents, self.lasts = [], [] @@ -586,14 +586,26 @@ class Dashboard: obj.history_date = idx['hd'] last_lst.append(obj) # years - self.years = model.get_years() - self.years.sort() - if not self.total_number or not self.years: + self.periods = model.get_periods(slice=slice, fltr=fltr) + self.periods = list(set(self.periods)) + self.periods.sort() + if not self.total_number or not self.periods: return - self.values = [('year', _(u"Year"), reversed(self.years))] # numbers - self.numbers = [model.get_by_year(year).count() for year in self.years] - self.values += [('number', _(u"Number"), reversed(self.numbers))] + if slice == 'year': + self.values = [('year', _(u"Year"), reversed(self.periods))] + self.numbers = [model.get_by_year(year, fltr=fltr).count() + for year in self.periods] + self.values += [('number', _(u"Number"), reversed(self.numbers))] + if slice == 'month': + self.numbers = [model.get_by_month(*period, fltr=fltr).count() + for period in self.periods] + periods = reversed(self.periods) + self.periods = ["%d-%s-01" % (period[0], ('0'+str(period[1])) + if len(str(period[1])) == 1 else period[1]) + for period in periods] + self.values = [('month', _(u"Month"), self.periods)] + self.values += [('number', _(u"Number"), reversed(self.numbers))] # calculate self.average = self.get_average() self.variance = self.get_variance() @@ -647,7 +659,7 @@ class Dashboard: def get_mode(self, vals={}): if not vals: - vals = dict(zip(self.years, self.numbers)) + vals = dict(zip(self.periods, self.numbers)) mx = max(vals.values()) for v in vals: if vals[v] == mx: diff --git a/ishtar_common/static/media/style.css b/ishtar_common/static/media/style.css index 32e957b74..341308926 100644 --- a/ishtar_common/static/media/style.css +++ b/ishtar_common/static/media/style.css @@ -426,9 +426,9 @@ div.form { margin-left:auto; margin-right:auto; margin-bottom:40px; - padding:1em; + padding:20px; display:block; - width:740px; + width:720px; text-align:center; } @@ -543,6 +543,25 @@ table.confirm tr.spacer td:last-child{ background-image:none; } +.ui-widget-header { + background:none; +} + +.ui-tabs .ui-tabs-nav li a{ + color: #D14; +} + +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { + background:none; + background-color: #F1F2F6; + border: 1px solid #CCC; + border-radius-top: 4px; +} + +.ui-tabs .ui-tabs-nav li.ui-tabs-active a{ + background-color:#fff; +} + .sheet{ width:760px; position:fixed; @@ -573,25 +592,6 @@ table.confirm tr.spacer td:last-child{ right:135px; } -.ui-widget-header { - background:none; -} - -.ui-tabs .ui-tabs-nav li a{ - color: #D14; -} - -.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { - background:none; - background-color: #F1F2F6; - border: 1px solid #CCC; - border-radius-top: 4px; -} - -.ui-tabs .ui-tabs-nav li.ui-tabs-active a{ - background-color:#fff; -} - #dashboard{ padding-top: 20px; } @@ -638,13 +638,22 @@ table.confirm tr.spacer td:last-child{ padding:0 10px; } +.dashboard div.form{ + padding:20px 10px; + width:740px; +} + +.dashboard select { + max-width: 400px; +} + #window .table, .dashboard .table{ padding:10px; width:730px; overflow:auto; } -#window table, .dashboard table{ +#window table, .dashboard table.resume{ font-size:0.9em; margin:10px 0; border-collapse:collapse; @@ -661,7 +670,7 @@ table.confirm tr.spacer td:last-child{ } -#window table th, .dashboard table th{ +#window table th, .dashboard table.resume th{ background-color:#922; border:1px solid #f1f2f6; color:#FFF; @@ -677,33 +686,33 @@ table.confirm tr.spacer td:last-child{ margin-right:auto; } -.dashboard table th{ +.dashboard table.resume th{ text-align:center; padding:0.5em; } -#window table th.sub, .dashboard table th.sub, .dashboard table td.sub{ +#window table th.sub, .dashboard table.resume th.sub, .dashboard table.resume td.sub{ background-color:#994242; border:1px solid #f1f2f6; color:#FFF; padding:0 1em; } -#window table th.sub, .dashboard table th.sub{ +#window table.resume th.sub, .dashboard table.resume th.sub{ text-align:left; } -#window table td, .dashboard table td{ +#window table td, .dashboard table.resume td{ text-align:right; padding:0 1em; border:1px solid #f1f2f6; } -#window table td.string, .dashboard table td.string{ +#window table td.string, .dashboard table.resume td.string{ text-align:left; } -#window table td.ref, .dashboard table td.ref{ +#window table td.ref, .dashboard table.resume td.ref{ text-align:left; white-space:nowrap; font-family:monospace; @@ -820,6 +829,10 @@ a.remove{ margin:0 6px; } +.dashboard .jqplot-target table{ + width:auto; +} + .dashboard table.jqplot-table-legend{ width:default; border-collapse:default; @@ -835,11 +848,11 @@ a.remove{ padding:0; } -.dashboard table{ +.dashboard table.resume{ width:100%; border-collapse:yes; } -.dashboard table th, .dashboard table td{ +.dashboard table.resume th, .dashboard table.resume td{ border:1px solid; } diff --git a/ishtar_common/templates/base.html b/ishtar_common/templates/base.html index bfacee069..1ebebd815 100644 --- a/ishtar_common/templates/base.html +++ b/ishtar_common/templates/base.html @@ -2,7 +2,7 @@ {% load url from future%} <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="{{LANGUAGE_CODE}}" lang="{{LANGUAGE_CODE}}"> <head> <link rel="shortcut icon" href="{{STATIC_URL}}/media/images/favicon.png"/> diff --git a/ishtar_common/templates/ishtar/dashboards/dashboard_main.html b/ishtar_common/templates/ishtar/dashboards/dashboard_main.html index 06b671343..8822875b5 100644 --- a/ishtar_common/templates/ishtar/dashboards/dashboard_main.html +++ b/ishtar_common/templates/ishtar/dashboards/dashboard_main.html @@ -8,6 +8,7 @@ <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.canvasAxisLabelRenderer.min.js"></script> <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.highlighter.min.js"></script> <script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.pieRenderer.min.js"></script> +<script language="javascript" type="text/javascript" src="{{STATIC_URL}}js/jqplot/plugins/jqplot.dateAxisRenderer.min.js"></script> <link rel="stylesheet" href="{{STATIC_URL}}js/jqplot/jquery.jqplot.min.css" /> {% endblock %} {% block content %} @@ -18,8 +19,9 @@ $(function() { $( "#dash-tabs" ).tabs(); }); <div id="dash-tabs"> <ul> {% for label, app in app_list %} - <li><a href="{% url 'dashboard-main-detail' app %}">{{label}}</a></li> - {% endfor %}</ul> + <li><a href="{% url 'dashboard-main-detail' app %}">{{label}}</a></li>{% endfor %} + <li><a href="{% url 'dashboard-main-detail' 'users' %}">{% trans "Users" %}</a></li> + </ul> </div> </div> {% endblock %} diff --git a/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail.html b/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail.html index 6a99c4fdd..610457ae3 100644 --- a/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail.html +++ b/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail.html @@ -1,38 +1,21 @@ {% load i18n %} -<div class='dashboard'> -<script language="javascript" type="text/javascript"> -$(document).ready(function(){ -var values_{{item_name}} = []; - -{% for idx, lbl, values in dashboard.values %} {% for value in values %} -{% ifequal forloop.parentloop.counter0 0 %}values_{{item_name}}.push([{{value}}, 0]); -{% else %}values_{{item_name}}[{{forloop.counter0}}][1] = {{value}};{% endifequal %}{% endfor%}{% endfor%} - -var plot_{{item_name}} = $.jqplot('chart_{{item_name}}', - [values_{{item_name}}], { - series:[{showMarker:false}], - axes:{ - xaxis:{ - label:'{% trans "Year" %}' - }, - yaxis:{ - label:'{% trans "Number"%}', - min:0 - } - }, - highlighter: { - show: true, - sizeAdjust: 7.5 - } - }); -}); -</script> - <h3>{{lbl}}</h3> +{% load url from future %} +<div class='dashboard' id="{{unique_id}}-tab"> <div> <h4>{% trans "Numbers" %}</h4> <p><strong>{% trans "Total:" %}</strong> {{dashboard.total_number}}</p> <div class='table'> - <div id="chart_{{item_name}}" style="height:400px; width:700px;"></div> + <div id="chart_{{unique_id}}" style="height:400px; width:700px;"></div> + {% if form %} + <div class='form'> + <form method='post' action="{% url 'dashboard-main-detail' item_name %}" id='{{unique_id}}_form'> + <table>{% csrf_token %} + {{form}} + </table> + <button id="search_{{unique_id}}" class="submit">{% trans "Change" %}</button> + </form> + </div> + {% endif %} {% comment %} <table> {% for idx, lbl, values in dashboard.values %} @@ -43,7 +26,7 @@ var plot_{{item_name}} = $.jqplot('chart_{{item_name}}', {% endfor%} </table>{% endcomment %} </div> - {% if dashboard.years %} + {% if dashboard.periods %} <h4>{% trans "By years" %}</h4> <ul> <li><strong>{% trans "Average:" %}</strong> {{dashboard.average}}</li> @@ -65,7 +48,7 @@ var plot_{{item_name}} = $.jqplot('chart_{{item_name}}', {% endif %} <h4>{% trans "Created last" %}</h4> <div class='table'> - <table> + <table class='resume'> <tr><th>{{lbl}}</th><th>{% trans "Created" %}</th><th></th></tr> {% for item in dashboard.lasts %}<tr> <td class='ref'>{{item}}</td> @@ -76,7 +59,7 @@ var plot_{{item_name}} = $.jqplot('chart_{{item_name}}', </div> <h4>{% trans "Recent changes" %}</h4> <div class='table'> - <table> + <table class='resume'> <tr><th>{{lbl}}</th><th>{% trans "Modified" %}</th><th></th></tr> {% for item in dashboard.recents %}<tr> <td class='ref'>{{item}}</td> @@ -86,3 +69,64 @@ var plot_{{item_name}} = $.jqplot('chart_{{item_name}}', </table> </div> </div> +<script language="javascript" type="text/javascript"> +$(document).ready(function(){ +var values_{{unique_id}} = []; + + +{% for idx, lbl, values in dashboard.values %} {% for value in values %}{% ifequal forloop.parentloop.counter0 0 %} +values_{{unique_id}}.push([{{VALUE_QUOTE|safe}}{{value}}{{VALUE_QUOTE|safe}}, 0]); +{% else %}values_{{unique_id}}[{{forloop.counter0}}][1] = {{value}};{% endifequal %}{% endfor%}{% endfor%} + +if (values_{{unique_id}}.length > 0){ + +var showmarker = false; +if (values_{{unique_id}}.length < 25){ + var showmarker = true; +} + +var plot_{{unique_id}} = $.jqplot('chart_{{unique_id}}', + [values_{{unique_id}}], { + series:[{showMarker:showmarker}], + axes:{ {%ifequal slicing 'year'%} + xaxis:{ + label:'{% trans "Year" %}', + tickOptions: { + formatString: "%d" + } + },{%endifequal%}{%ifequal slicing 'month'%} + xaxis:{ + label:'{% trans "Month" %}', + renderer:$.jqplot.DateAxisRenderer, + tickOptions:{formatString:'%b %Y'}, + },{%endifequal%} + yaxis:{ + label:'{% trans "Number"%}', + min:0 + } + }, + highlighter: { + show: true, + sizeAdjust: 7.5 + } + }); + +} else { +$('#chart_{{unique_id}}').html("<p class='alert'>{% trans 'No data for theses criteria.' %}</p"); +} + +$('#search_{{unique_id}}').click(function (){ + $.post("{% url 'dashboard-main-detail' item_name %}", + $("#{{unique_id}}_form").serialize(), + function(data){ + $("#{{unique_id}}-tab").parent().html(data); + }); + return false; +}); + +{% ifequal item_name 'files' %} +load_jquerydate_after(); +load_jquerydate_before(); +{% endifequal %} +}); +</script> diff --git a/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail_users.html b/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail_users.html index 6602d5193..6ffd8c3c9 100644 --- a/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail_users.html +++ b/ishtar_common/templates/ishtar/dashboards/dashboard_main_detail_users.html @@ -1,5 +1,5 @@ {% load i18n %} - <h3>{% trans "Users" %}</h3> +<div class='dashboard'> <div> <script language="javascript" type="text/javascript"> $(document).ready(function(){ @@ -15,14 +15,14 @@ var plot_users = jQuery.jqplot ('user_chart', [values_users], showDataLabels: true } }, - legend: { show:true, location: 's' } + legend: { show:true, location: 'e' } } ); }); </script> - <div id="user_chart" style="height:400px; width:700px;"></div> + <div id="user_chart" style="height:300px; width:700px;"></div> <div class='table'> - <table> + <table class='resume'> <tr><th>{% trans "User type" %}</th><th>{% trans "Number" %}</th></tr> {% for user_type in ishtar_users.types %} <tr> diff --git a/ishtar_common/views.py b/ishtar_common/views.py index 9f193fdec..e51704794 100644 --- a/ishtar_common/views.py +++ b/ishtar_common/views.py @@ -703,35 +703,23 @@ def dashboard_main(request, dct, obj_id=None, *args, **kwargs): """ Main dashboard """ - """ - items = [] - if 'archaeological_files' in settings.INSTALLED_APPS: - from archaeological_files.models import File - items.append((_(u"Archaeological files"), - models.Dashboard(File))) - from archaeological_operations.models import Operation - items.append((_(u"Operations"), models.Dashboard(Operation))) - if 'archaeological_context_records' in settings.INSTALLED_APPS: - from archaeological_context_records.models import ContextRecord - items.append((_(u"Context records"), models.Dashboard(ContextRecord))) - if 'archaeological_finds' in settings.INSTALLED_APPS: - from archaeological_finds.models import Find - items.append((_(u"Finds"), models.Dashboard(Find))) - dct = {'items':items, - 'ishtar_users':models.UserDashboard()} - """ app_list = [] if 'archaeological_files' in settings.INSTALLED_APPS: app_list.append((_(u"Archaeological files"), 'files')) app_list.append((_(u"Operations"), 'operations')) if 'archaeological_context_records' in settings.INSTALLED_APPS: - app_list.append((_(u"Context records"), 'context-records')) + app_list.append((_(u"Context records"), 'contextrecords')) if 'archaeological_finds' in settings.INSTALLED_APPS: app_list.append((_(u"Finds"), 'finds')) dct = {'app_list':app_list} return render_to_response('ishtar/dashboards/dashboard_main.html', dct, context_instance=RequestContext(request)) +DASHBOARD_FORMS = {} +if 'archaeological_files' in settings.INSTALLED_APPS: + from archaeological_files.forms import DashboardForm as DashboardFormFile + DASHBOARD_FORMS['files'] = DashboardFormFile + def dashboard_main_detail(request, item_name): """ Specific tab of the main dashboard @@ -741,25 +729,44 @@ def dashboard_main_detail(request, item_name): return render_to_response( 'ishtar/dashboards/dashboard_main_detail_users.html', dct, context_instance=RequestContext(request)) + form = None + slicing, fltr = 'year', {} + if item_name in DASHBOARD_FORMS: + if request.method == 'POST': + form = DASHBOARD_FORMS[item_name](request.POST) + if form.is_valid(): + slicing = form.cleaned_data['slicing'] + fltr = form.get_filter() + else: + form = DASHBOARD_FORMS[item_name]() lbl, dashboard = None, None if item_name == 'files' and \ 'archaeological_files' in settings.INSTALLED_APPS: from archaeological_files.models import File - lbl, dashboard = (_(u"Archaeological files"), models.Dashboard(File)) + lbl, dashboard = (_(u"Archaeological files"), models.Dashboard(File, + slice=slicing, fltr=fltr)) if item_name == 'operations': from archaeological_operations.models import Operation - lbl, dashboard = (_(u"Operations"), models.Dashboard(Operation)) - if item_name == 'context-records' and \ + lbl, dashboard = (_(u"Operations"), models.Dashboard(Operation, + slice=slicing, fltr=fltr)) + if item_name == 'contextrecords' and \ 'archaeological_context_records' in settings.INSTALLED_APPS: from archaeological_context_records.models import ContextRecord - lbl, dashboard = (_(u"Context records"), models.Dashboard(ContextRecord)) + lbl, dashboard = (_(u"Context records"), models.Dashboard(ContextRecord, + slice=slicing, fltr=fltr)) if item_name == 'finds' and \ 'archaeological_finds' in settings.INSTALLED_APPS: from archaeological_finds.models import Find - lbl, dashboard = (_(u"Finds"), models.Dashboard(Find)) + lbl, dashboard = (_(u"Finds"), models.Dashboard(Find, + slice=slicing, fltr=fltr)) if not lbl: raise Http404 dct = {'lbl':lbl, 'dashboard':dashboard, - 'item_name':item_name.replace('-', '_')} + 'item_name':item_name.replace('-', '_'), + 'VALUE_QUOTE': '' if slicing == "year" else "'", + 'form':form, 'slicing':slicing} + n = datetime.datetime.now() + dct['unique_id'] = dct['item_name'] + "_" + \ + '%d_%d_%d' % (n.minute, n.second, n.microsecond) return render_to_response('ishtar/dashboards/dashboard_main_detail.html', dct, context_instance=RequestContext(request)) 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 <etienne.loks_AT_peacefrogsDOTnet>
-# Copyright (C) 2007 skam <massimo dot scamarcia at gmail.com>
-# (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 <http://www.gnu.org/licenses/>.
-
-# 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 = ['<tr class="delete"><td colspan="2">']
- output.append(u"<button%s>%s</button>" % (final_attrs, _("Delete")))
- output.append('</td></tr>')
- return mark_safe('\n'.join(output))
-
-class ImageFileInput(ClearableFileInput):
- template_with_initial = u'<span class="prettyPhoto">%(initial)s</span>'\
- u' %(clear_template)s<br />%(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 += """
-<script type="text/javascript"><!--//
- $(window).load(function() {
- $(".date-pickup").datepicker($.datepicker.regional["%(country)s"]);
- var val = $("#id_%(name)s").val();
- if(val){
- var dt = $.datepicker.parseDate('yy-mm-dd', val);
- val = $.datepicker.formatDate(
- $.datepicker.regional["%(country)s"]['dateFormat'],
- dt);
- $("#id_%(name)s").val(val);
- }
- });
-//--></script>
-""" % {"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' <a href="#" class="add-button" '\
- u'onclick="open_window(\'%s\');">+</a>' % url_new
- html = u'''<input%(attrs_select)s/>%(new)s\
-<input type="hidden"%(attrs_hidden)s/>\
- <script type="text/javascript"><!--//
- $(function() {%(js)s});//--></script>
- ''' % {
- '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 <etienne.loks_AT_peacefrogsDOTnet> +# Copyright (C) 2007 skam <massimo dot scamarcia at gmail.com> +# (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 <http://www.gnu.org/licenses/>. + +# 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 = ['<tr class="delete"><td colspan="2">'] + output.append(u"<button%s>%s</button>" % (final_attrs, _("Delete"))) + output.append('</td></tr>') + return mark_safe('\n'.join(output)) + +class ImageFileInput(ClearableFileInput): + template_with_initial = u'<span class="prettyPhoto">%(initial)s</span>'\ + u' %(clear_template)s<br />%(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 += """ +<script type="text/javascript"><!--// + function load_jquerydate_%(name)s(){ + $(".date-pickup").datepicker($.datepicker.regional["%(country)s"]); + var val = $("#id_%(name)s").val(); + if(val){ + var dt = $.datepicker.parseDate('yy-mm-dd', val); + val = $.datepicker.formatDate( + $.datepicker.regional["%(country)s"]['dateFormat'], + dt); + $("#id_%(name)s").val(val); + } + } + $(window).load(load_jquerydate_%(name)s); +//--></script> +""" % {"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' <a href="#" class="add-button" '\ + u'onclick="open_window(\'%s\');">+</a>' % url_new + html = u'''<input%(attrs_select)s/>%(new)s\ +<input type="hidden"%(attrs_hidden)s/>\ + <script type="text/javascript"><!--// + $(function() {%(js)s});//--></script> + ''' % { + '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) |