summaryrefslogtreecommitdiff
path: root/ishtar_common
diff options
context:
space:
mode:
Diffstat (limited to 'ishtar_common')
-rw-r--r--ishtar_common/models.py30
-rw-r--r--ishtar_common/static/media/style.css75
-rw-r--r--ishtar_common/templates/base.html2
-rw-r--r--ishtar_common/templates/ishtar/dashboards/dashboard_main.html6
-rw-r--r--ishtar_common/templates/ishtar/dashboards/dashboard_main_detail.html110
-rw-r--r--ishtar_common/templates/ishtar/dashboards/dashboard_main_detail_users.html8
-rw-r--r--ishtar_common/views.py55
-rw-r--r--ishtar_common/widgets.py658
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)