Improve grid filters for datetime fields.
Hopefully this makes these filters more intuitive, by allowing user to provide a date value but interpret in a datetime context.
This commit is contained in:
parent
68f7c418d6
commit
cd461aef51
|
@ -100,12 +100,14 @@ class AlchemyGrid(Grid):
|
||||||
factory = filters.AlchemyNumericFilter
|
factory = filters.AlchemyNumericFilter
|
||||||
elif isinstance(column.type, sa.Boolean):
|
elif isinstance(column.type, sa.Boolean):
|
||||||
factory = filters.AlchemyBooleanFilter
|
factory = filters.AlchemyBooleanFilter
|
||||||
elif isinstance(column.type, (sa.Date, sa.DateTime)):
|
elif isinstance(column.type, sa.Date):
|
||||||
factory = filters.AlchemyDateFilter
|
factory = filters.AlchemyDateFilter
|
||||||
|
elif isinstance(column.type, sa.DateTime):
|
||||||
|
factory = filters.AlchemyDateTimeFilter
|
||||||
elif isinstance(column.type, GPCType):
|
elif isinstance(column.type, GPCType):
|
||||||
factory = filters.AlchemyGPCFilter
|
factory = filters.AlchemyGPCFilter
|
||||||
factory = kwargs.pop('factory', factory)
|
factory = kwargs.pop('factory', factory)
|
||||||
return factory(key, column=column, **kwargs)
|
return factory(key, column=column, config=self.request.rattail_config, **kwargs)
|
||||||
|
|
||||||
def iter_filters(self):
|
def iter_filters(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -26,6 +26,9 @@ Grid Filters
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from edbob.util import prettify
|
from edbob.util import prettify
|
||||||
|
@ -33,12 +36,16 @@ from edbob.util import prettify
|
||||||
from rattail.gpc import GPC
|
from rattail.gpc import GPC
|
||||||
from rattail.util import OrderedDict
|
from rattail.util import OrderedDict
|
||||||
from rattail.core import UNSPECIFIED
|
from rattail.core import UNSPECIFIED
|
||||||
|
from rattail.time import localtime, make_utc
|
||||||
|
|
||||||
from pyramid_simpleform import Form
|
from pyramid_simpleform import Form
|
||||||
from pyramid_simpleform.renderers import FormRenderer
|
from pyramid_simpleform.renderers import FormRenderer
|
||||||
from webhelpers.html import HTML, tags
|
from webhelpers.html import HTML, tags
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FilterValueRenderer(object):
|
class FilterValueRenderer(object):
|
||||||
"""
|
"""
|
||||||
Base class for all filter renderers.
|
Base class for all filter renderers.
|
||||||
|
@ -350,6 +357,17 @@ class AlchemyDateFilter(AlchemyGridFilter):
|
||||||
"""
|
"""
|
||||||
value_renderer_factory = DateValueRenderer
|
value_renderer_factory = DateValueRenderer
|
||||||
|
|
||||||
|
verb_labels = {
|
||||||
|
'equal': "on",
|
||||||
|
'not_equal': "not on",
|
||||||
|
'greater_than': "after",
|
||||||
|
'greater_equal': "on or after",
|
||||||
|
'less_than': "before",
|
||||||
|
'less_equal': "on or before",
|
||||||
|
'is_null': "is null",
|
||||||
|
'is_not_null': "is not null",
|
||||||
|
}
|
||||||
|
|
||||||
def default_verbs(self):
|
def default_verbs(self):
|
||||||
"""
|
"""
|
||||||
Expose greater-than / less-than verbs in addition to core.
|
Expose greater-than / less-than verbs in addition to core.
|
||||||
|
@ -357,17 +375,106 @@ class AlchemyDateFilter(AlchemyGridFilter):
|
||||||
return ['equal', 'not_equal', 'greater_than', 'greater_equal',
|
return ['equal', 'not_equal', 'greater_than', 'greater_equal',
|
||||||
'less_than', 'less_equal', 'is_null', 'is_not_null']
|
'less_than', 'less_equal', 'is_null', 'is_not_null']
|
||||||
|
|
||||||
def filter_is_true(self, query, value):
|
def make_date(self, value):
|
||||||
"""
|
"""
|
||||||
Filter data with an "is true" query (alias for "is not null").
|
Convert user input to a proper ``datetime.date`` object.
|
||||||
"""
|
"""
|
||||||
return self.filter_is_not_null(query, value)
|
if value:
|
||||||
|
try:
|
||||||
|
dt = datetime.datetime.strptime(value, '%Y-%m-%d')
|
||||||
|
except ValueError:
|
||||||
|
log.warning("invalid date value: {}".format(value))
|
||||||
|
else:
|
||||||
|
return dt.date()
|
||||||
|
|
||||||
def filter_is_false(self, query, value):
|
|
||||||
|
class AlchemyDateTimeFilter(AlchemyDateFilter):
|
||||||
"""
|
"""
|
||||||
Filter data with an "is false" query (alias for "is null").
|
SQLAlchemy filter for datetime values.
|
||||||
"""
|
"""
|
||||||
return self.filter_is_null(query, value)
|
|
||||||
|
def filter_equal(self, query, value):
|
||||||
|
"""
|
||||||
|
Find all dateimes which fall on the given date.
|
||||||
|
"""
|
||||||
|
date = self.make_date(value)
|
||||||
|
if not date:
|
||||||
|
return query
|
||||||
|
|
||||||
|
start = datetime.datetime.combine(date, datetime.time(0))
|
||||||
|
start = make_utc(localtime(self.config, start))
|
||||||
|
|
||||||
|
stop = datetime.datetime.combine(date + datetime.timedelta(days=1), datetime.time(0))
|
||||||
|
stop = make_utc(localtime(self.config, stop))
|
||||||
|
|
||||||
|
return query.filter(self.column >= start)\
|
||||||
|
.filter(self.column < stop)
|
||||||
|
|
||||||
|
def filter_not_equal(self, query, value):
|
||||||
|
"""
|
||||||
|
Find all dateimes which do *not* fall on the given date.
|
||||||
|
"""
|
||||||
|
date = self.make_date(value)
|
||||||
|
if not date:
|
||||||
|
return query
|
||||||
|
|
||||||
|
start = datetime.datetime.combine(date, datetime.time(0))
|
||||||
|
start = make_utc(localtime(self.config, start))
|
||||||
|
|
||||||
|
stop = datetime.datetime.combine(date + datetime.timedelta(days=1), datetime.time(0))
|
||||||
|
stop = make_utc(localtime(self.config, stop))
|
||||||
|
|
||||||
|
return query.filter(sa.or_(
|
||||||
|
self.column < start,
|
||||||
|
self.column <= stop))
|
||||||
|
|
||||||
|
def filter_greater_than(self, query, value):
|
||||||
|
"""
|
||||||
|
Find all datetimes which fall after the given date.
|
||||||
|
"""
|
||||||
|
date = self.make_date(value)
|
||||||
|
if not date:
|
||||||
|
return query
|
||||||
|
|
||||||
|
time = datetime.datetime.combine(date + datetime.timedelta(days=1), datetime.time(0))
|
||||||
|
time = make_utc(localtime(self.config, time))
|
||||||
|
return query.filter(self.column >= time)
|
||||||
|
|
||||||
|
def filter_greater_equal(self, query, value):
|
||||||
|
"""
|
||||||
|
Find all datetimes which fall on or after the given date.
|
||||||
|
"""
|
||||||
|
date = self.make_date(value)
|
||||||
|
if not date:
|
||||||
|
return query
|
||||||
|
|
||||||
|
time = datetime.datetime.combine(date, datetime.time(0))
|
||||||
|
time = make_utc(localtime(self.config, time))
|
||||||
|
return query.filter(self.column >= time)
|
||||||
|
|
||||||
|
def filter_less_than(self, query, value):
|
||||||
|
"""
|
||||||
|
Find all datetimes which fall before the given date.
|
||||||
|
"""
|
||||||
|
date = self.make_date(value)
|
||||||
|
if not date:
|
||||||
|
return query
|
||||||
|
|
||||||
|
time = datetime.datetime.combine(date, datetime.time(0))
|
||||||
|
time = make_utc(localtime(self.config, time))
|
||||||
|
return query.filter(self.column < time)
|
||||||
|
|
||||||
|
def filter_less_equal(self, query, value):
|
||||||
|
"""
|
||||||
|
Find all datetimes which fall on or before the given date.
|
||||||
|
"""
|
||||||
|
date = self.make_date(value)
|
||||||
|
if not date:
|
||||||
|
return query
|
||||||
|
|
||||||
|
time = datetime.datetime.combine(date + datetime.timedelta(days=1), datetime.time(0))
|
||||||
|
time = make_utc(localtime(self.config, time))
|
||||||
|
return query.filter(self.column < time)
|
||||||
|
|
||||||
|
|
||||||
class AlchemyGPCFilter(AlchemyGridFilter):
|
class AlchemyGPCFilter(AlchemyGridFilter):
|
||||||
|
|
Loading…
Reference in a new issue