Created
March 25, 2016 16:47
-
-
Save tarkatronic/e777e1330d84c40aa5f0 to your computer and use it in GitHub Desktop.
django-filters TimeSpanFilter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
import time | |
from datetime import datetime, timedelta | |
import django_filters | |
from django.utils import timezone | |
from .lookups import Now | |
class TimeSpanFilter(django_filters.Filter): | |
def filter(self, qs, value): | |
value = value.lower().strip() | |
now = timezone.localtime(timezone.now()) | |
if value == 'today': | |
# FIXME: https://code.djangoproject.com/ticket/9596 | |
# Fixed in Django 1.9: https://github.com/django/django/commit/44f3ee77166bd5c0e8a4604f2d96015268dce100 | |
this_morning = now.replace(hour=0, minute=0, second=0) | |
tonight = now.replace(hour=23, minute=59, second=59) | |
return qs.filter(**{'%s__range' % self.name: (this_morning, tonight)}) | |
elif value == 'tomorrow': | |
tomorrow = now + timedelta(days=1) | |
tomorrow_morning = tomorrow.replace(hour=0, minute=0, second=0) | |
tomorrow_night = tomorrow.replace(hour=23, minute=59, second=59) | |
return qs.filter(**{'%s__range' % self.name: (tomorrow_morning, tomorrow_night)}) | |
elif value == 'yesterday': | |
yesterday = now - timedelta(days=1) | |
yesterday_morning = yesterday.replace(hour=0, minute=0, second=0) | |
yesterday_night = yesterday.replace(hour=23, minute=59, second=59) | |
return qs.filter(**{'%s__range' % self.name: (yesterday_morning, yesterday_night)}) | |
elif value in ('week', 'this_week'): | |
return qs.filter(**{'%s__year' % self.name: now.year, | |
'%s__week' % self.name: Now()}) # FIXME: https://code.djangoproject.com/ticket/25240 | |
elif value == 'next_week': | |
last_sunday = now - timedelta(days=(now.weekday() + 1)) | |
next_sunday = last_sunday + timedelta(weeks=1) | |
begin_next_week = next_sunday.replace(hour=0, minute=0, second=0) | |
end_next_week = (next_sunday + timedelta(days=6)).replace(hour=23, minute=59, second=59) | |
return qs.filter(**{'%s__range' % self.name: (begin_next_week, end_next_week)}) | |
elif value == 'last_week': | |
last_sunday = now - timedelta(days=(now.weekday() + 1)) | |
begin_last_week = (last_sunday - timedelta(weeks=1)).replace(hour=0, minute=0, second=0) | |
end_last_week = (begin_last_week + timedelta(days=6)).replace(hour=23, minute=59, second=59) | |
return qs.filter(**{'%s__range' % self.name: (begin_last_week, end_last_week)}) | |
elif value in ('month', 'this_month'): | |
return qs.filter(**{'%s__year' % self.name: now.year, | |
'%s__month' % self.name: now.month}) | |
elif value in ('year', 'this_year'): | |
return qs.filter(**{'%s__year' % self.name: now.year}) | |
elif value == 'past': | |
return qs.filter(**{'%s__lte' % self.name: now}) | |
elif value == 'future': | |
return qs.filter(**{'%s__gte' % self.name: now}) | |
else: | |
try: # Specific month filtering | |
ts = time.strptime(value, '%Y-%m') | |
return qs.filter(**{'%s__year' % self.name: ts.tm_year, | |
'%s__month' % self.name: ts.tm_mon}) | |
except ValueError: | |
pass | |
try: # Specific day filtering | |
ts = time.strptime(value, '%Y-%m-%d') | |
that_day = datetime.datetime(ts.tm_year, ts.tm_mon, ts.tm_mday) | |
that_morning = that_day.replace(hour=0, minute=0, second=0) | |
that_night = that_day.replace(hour=23, minute=59, second=59) | |
return qs.filter(**{'%s__range' % self.name: (that_morning, that_night)}) | |
except ValueError: | |
pass | |
# Specific week matching | |
ts = re.match('(?P<year>\d{4})-week-(?P<week>\d{1,2})', value) | |
if ts: | |
match_year = datetime(int(ts.group('year')), 1, 1) | |
match_week = match_year + timedelta(weeks=int(ts.group('week'))) | |
begin_match_week = (match_week - timedelta(days=(match_week.weekday() + 1))).replace(hour=0, minute=0, | |
second=0) | |
end_match_week = (begin_match_week + timedelta(days=6)).replace(hour=23, minute=59, second=59) | |
return qs.filter(**{'%s__range' % self.name: (begin_match_week, end_match_week)}) | |
return qs # Invalid value; ignore it |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db.models.functions import Func | |
# FIXME: https://code.djangoproject.com/ticket/25240 | |
class Now(Func): | |
template = 'CURRENT_TIMESTAMP' | |
def __init__(self, output_field=None, **extra): | |
if output_field is None: | |
output_field = DateTimeField() | |
super(Now, self).__init__(output_field=output_field, **extra) | |
def as_postgresql(self, compiler, connection): | |
# Postgres' CURRENT_TIMESTAMP means "the time at the start of the | |
# transaction". We use STATEMENT_TIMESTAMP to be cross-compatible with | |
# other databases. | |
self.template = 'STATEMENT_TIMESTAMP()' | |
return self.as_sql(compiler, connection) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment