Parcourir la source

add date-range filter

Timur Orudzhov il y a 8 ans
Parent
commit
19a3c0469f

+ 2 - 1
gulpfile.js

@@ -61,7 +61,8 @@ gulp.task('vendor-styles', function() {
     merge(
         gulp.src([
             './node_modules/select2/dist/css/select2.css',
-            './node_modules/timepicker/jquery.ui.timepicker.css'
+            './node_modules/timepicker/jquery.ui.timepicker.css',
+            './node_modules/webui-popover/src/jquery.webui-popover.css'
         ]),
         gulp.src([
             './node_modules/jquery-ui/themes/base/all.css'

+ 120 - 4
jet/filters.py

@@ -1,20 +1,44 @@
-from django.contrib.admin import RelatedFieldListFilter
+from __future__ import unicode_literals
+import datetime
+from collections import OrderedDict
+
+try:
+    import pytz
+except ImportError:
+    pytz = None
+
+from django import forms
+from django.conf import settings
+from django.contrib import admin
+from django.contrib.admin.widgets import AdminDateWidget
+from django.template.defaultfilters import slugify
+from django.utils.translation import ugettext as _
 from django.utils.encoding import smart_text
 from django.utils.html import format_html
 from django.core.urlresolvers import reverse
 
 try:
     from django.contrib.admin.utils import get_model_from_relation
-except ImportError: # Django 1.6
+except ImportError:  # Django 1.6
     from django.contrib.admin.util import get_model_from_relation
 
 try:
     from django.forms.utils import flatatt
-except ImportError: # Django 1.6
+except ImportError:  # Django 1.6
     from django.forms.util import flatatt
 
 
-class RelatedFieldAjaxListFilter(RelatedFieldListFilter):
+def make_dt_aware(dt):
+    if pytz is not None and settings.USE_TZ:
+        timezone = pytz.timezone(settings.TIME_ZONE)
+        if dt.tzinfo is not None:
+            dt = timezone.normalize(dt)
+        else:
+            dt = timezone.localize(dt)
+    return dt
+
+
+class RelatedFieldAjaxListFilter(admin.RelatedFieldListFilter):
     ajax_attrs = None
 
     def has_output(self):
@@ -43,3 +67,95 @@ class RelatedFieldAjaxListFilter(RelatedFieldListFilter):
 
         queryset = model._default_manager.filter(**{rel_name: self.lookup_val}).all()
         return [(x._get_pk_val(), smart_text(x)) for x in queryset]
+
+
+class DateRangeFilter(admin.filters.FieldListFilter):
+    def __init__(self, field, request, params, model, model_admin, field_path):
+        self.lookup_kwarg_gte = '{}__gte'.format(field_path)
+        self.lookup_kwarg_lte = '{}__lte'.format(field_path)
+
+        super(DateRangeFilter, self).__init__(
+            field, request, params, model, model_admin, field_path)
+
+        self.form = self.get_form(request)
+
+    def choices(self, cl):
+        yield {
+            'system_name': slugify(self.title),
+            'query_string': cl.get_query_string(
+                {}, remove=[self.lookup_kwarg_gte, self.lookup_kwarg_lte]
+            )
+        }
+
+    def expected_parameters(self):
+        return [self.lookup_kwarg_gte, self.lookup_kwarg_lte]
+
+    def queryset(self, request, queryset):
+        if self.form.is_valid():
+            validated_data = dict(self.form.cleaned_data.items())
+            if validated_data:
+                return queryset.filter(
+                    **self._make_query_filter(validated_data)
+                )
+        return queryset
+
+    def _make_query_filter(self, validated_data):
+        query_params = {}
+        date_value_gte = validated_data.get(self.lookup_kwarg_gte, None)
+        date_value_lte = validated_data.get(self.lookup_kwarg_lte, None)
+
+        if date_value_gte:
+            query_params['{0}__gte'.format(self.field_path)] = make_dt_aware(
+                datetime.datetime.combine(date_value_gte, datetime.time.min)
+            )
+        if date_value_lte:
+            query_params['{0}__lte'.format(self.field_path)] = make_dt_aware(
+                datetime.datetime.combine(date_value_lte, datetime.time.max)
+            )
+        return query_params
+
+    def get_template(self):
+        return 'rangefilter/date_filter.html'
+
+    template = property(get_template)
+
+    def get_form(self, request):
+        form_class = self._get_form_class()
+        return form_class(self.used_parameters)
+
+    def _get_form_class(self):
+        fields = self._get_form_fields()
+
+        form_class = type(
+            str('DateRangeForm'),
+            (forms.BaseForm,),
+            {'base_fields': fields}
+        )
+        form_class.media = self._get_media()
+
+        return form_class
+
+    def _get_form_fields(self):
+        return OrderedDict((
+            (self.lookup_kwarg_gte, forms.DateField(
+                label='',
+                widget=AdminDateWidget(attrs={'placeholder': _('From date')}),
+                localize=True,
+                required=False
+            )),
+            (self.lookup_kwarg_lte, forms.DateField(
+                label='',
+                widget=AdminDateWidget(attrs={'placeholder': _('To date')}),
+                localize=True,
+                required=False
+            )),
+        ))
+
+    @staticmethod
+    def _get_media():
+        css = [
+            'style.css',
+        ]
+        return forms.Media(
+            css={'all': ['range_filter/css/%s' % path for path in css]}
+        )

+ 16 - 0
jet/static/jet/js/src/layout-updaters/toolbar.js

@@ -1,4 +1,5 @@
 var $ = require('jquery');
+var popover = require('webui-popover');
 
 var ToolbarUpdater = function($changelist) {
     this.$changelist = $changelist;
@@ -70,6 +71,21 @@ ToolbarUpdater.prototype = {
                 }
 
                 filterName = null;
+            } else if ($element.prop('tagName') == 'DIV') {
+                var $wrapper = $('<span>')
+                    .addClass('changelist-filter-select-wrapper')
+                    .append($element);
+
+                if ($search.length) {
+                    $wrapper.insertAfter($search);
+                } else {
+                    $toolbar.append($wrapper);
+                }
+
+                $wrapper.find('#filter-title').webuiPopover({
+                    url: '#filter-content',
+                    width: 200,
+                });
             }
         });
 

+ 7 - 0
jet/static/range_filter/css/style.css

@@ -0,0 +1,7 @@
+#filter-content fieldset.module {
+	padding: 0px !important;
+}
+
+#filter-content fieldset.module .form-row {
+	padding-left:  0px !important;
+}

+ 30 - 0
jet/templates/rangefilter/date_filter.html

@@ -0,0 +1,30 @@
+{% load i18n admin_static %}
+
+<script>
+    function datefilter_apply(event, qs_name, form_name){
+        event.preventDefault();
+        var query_string = django.jQuery('input#'+qs_name).val();
+        var form_data = django.jQuery('#'+form_name).serialize();
+        window.location = window.location.pathname + query_string + '&' + form_data;
+    }
+    function datefilter_reset(qs_name){
+        var query_string = django.jQuery('input#'+qs_name).val();
+        window.location = window.location.pathname + query_string;
+    }
+</script>
+<div>
+    <input id="filter-title" type="text" value="{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}" readonly>
+    <div id="filter-content" style="display: none">
+        <form method="GET" action="." id="{{ choices.0.system_name }}-form">
+            {{ spec.form.media }}
+            {{ spec.form|as_p }}
+            {% for choice in choices %}
+                <input type="hidden" id="{{ choice.system_name }}-query-string" value="{{ choice.query_string }}">
+            {% endfor %}
+            <div class="controls">
+                <input type="submit" value="{% trans "Apply" %}" onclick="datefilter_apply(event, '{{ choices.0.system_name }}-query-string', '{{ choices.0.system_name }}-form')">
+                <input type="reset" class="button" value="{% trans "Reset" %}" onclick="datefilter_reset('{{ choices.0.system_name }}-query-string')">
+            </div>
+        </form>
+    </div>
+</div>

+ 2 - 1
package.json

@@ -24,6 +24,7 @@
     "select2": "4.0.0",
     "timepicker": "git://github.com/geex-arts/timepicker",
     "vinyl-buffer": "1.0.0",
-    "vinyl-source-stream": "1.1.0"
+    "vinyl-source-stream": "1.1.0",
+    "webui-popover": "^1.2.16"
   }
 }