Skip to content

Commit

Permalink
fix problem with saw-like charts arount DST times, remove dependency …
Browse files Browse the repository at this point in the history
…on qsstats-magic
  • Loading branch information
PetrDlouhy committed Apr 3, 2020
1 parent 0ba356f commit 807c044
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 23 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Changelog
=========

0.18.0 (2020-03-03)
------------------
* fix problem with saw-like charts arount DST times
* remove dependency on qsstats-magic

0.17.0 (2020-02-20)
------------------
* fixes for DateField and timezones
Expand Down
1 change: 0 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ Dependencies
django-admin-charts is a django based application, the major requirements are:

- django-jsonfield
- django-qsstats-magic
- django-nvd3
- django-bower

Expand Down
41 changes: 20 additions & 21 deletions admin_tools_stats/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from cache_utils.decorators import cached

from dateutil.relativedelta import relativedelta
from dateutil.rrule import rrule, YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY

from django.apps import apps
from django.conf import settings
Expand All @@ -34,8 +34,6 @@

import jsonfield.fields

from qsstats.utils import get_bounds

logger = logging.getLogger(__name__)

operation = (
Expand Down Expand Up @@ -68,6 +66,13 @@
('months', 'Months'),
('years', 'Years'),
)
freqs = {
'years': YEARLY,
'months': MONTHLY,
'weeks': WEEKLY,
'days': DAILY,
'hours': HOURLY
}


class DashboardStatsCriteria(models.Model):
Expand Down Expand Up @@ -356,7 +361,7 @@ def get_time_series(self, dynamic_criteria, all_criteria, request, time_since, t
tzinfo = {'tzinfo': time_since.tzinfo}
else:
tzinfo = {}
qs = qs.annotate(d=Trunc(self.date_field_name, interval, **tzinfo))
qs = qs.annotate(d=Trunc(self.date_field_name, interval))
qs = qs.values_list('d')
qs = qs.order_by('d')
qs = qs.annotate(**aggregate_dict)
Expand All @@ -372,11 +377,8 @@ def get_multi_series_criteria(self, request_get):

def get_multi_time_series(self, configuration, time_since, time_until, interval, request=None):
current_tz = timezone.get_current_timezone()

if settings.USE_TZ:
time_since = current_tz.localize(time_since)
time_until = current_tz.localize(time_until)
time_until = time_until.replace(hour=23, minute=59)
time_since_tz = current_tz.localize(time_since)
time_until_tz = current_tz.localize(time_until).replace(hour=23, minute=59)

configuration = configuration.copy()
series = {}
Expand All @@ -395,7 +397,7 @@ def get_multi_time_series(self, configuration, time_since, time_until, interval,
names.append(name)
values.append(key)
configuration['select_box_dynamic_' + str(m2m.id)] = values
serie_map = self.get_time_series(configuration, all_criteria, request, time_since, time_until, interval)
serie_map = self.get_time_series(configuration, all_criteria, request, time_since_tz, time_until_tz, interval)
for tv in serie_map:
time = tv[0]
if time not in series:
Expand All @@ -405,27 +407,24 @@ def get_multi_time_series(self, configuration, time_since, time_until, interval,
i += 1
series[time][name] = tv[i]
else:
serie = self.get_time_series(configuration, all_criteria, request, time_since, time_until, interval)
serie = self.get_time_series(configuration, all_criteria, request, time_since_tz, time_until_tz, interval)
for time, value in serie:
series[time] = {'': value}
names = {'': ''}

# fill with zeros where the records are missing
interval_s = interval.rstrip('s')
start, _ = get_bounds(time_since, interval_s)
_, end = get_bounds(time_until, interval_s)
if self.get_date_field().__class__ == DateField:
start = start.date()
end = end.date()

time = start
while time <= end:
dates = list(rrule(freq=freqs[interval], dtstart=time_since, until=time_until))
for time in dates:
if self.get_date_field().__class__ == DateField:
time = time.date()
elif settings.USE_TZ:
time = current_tz.localize(time)

if time not in series:
series[time] = OrderedDict()
for key in names:
if key not in series[time]:
series[time][key] = 0
time = time + relativedelta(**{interval: 1})
return series

def get_control_form(self):
Expand Down
17 changes: 17 additions & 0 deletions admin_tools_stats/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,23 @@ def test_get_multi_series_date_tz(self):
}
self.assertDictEqual(serie, testing_data)

@override_settings(USE_TZ=True, TIME_ZONE='Europe/Prague')
def test_get_multi_series_change_dst(self):
"""Test function to check DashboardStats.get_multi_time_series() on edge of daylight saving time change """
current_tz = timezone.get_current_timezone()
mommy.make('User', date_joined=datetime.datetime(2019, 10, 28, tzinfo=current_tz))
time_since = datetime.datetime(2019, 10, 27, 0, 0)
time_until = datetime.datetime(2019, 10, 29, 0, 0)

interval = "days"
serie = self.stats.get_multi_time_series({}, time_since, time_until, interval)
testing_data = {
current_tz.localize(datetime.datetime(2019, 10, 27, 0, 0)): {'': 0},
current_tz.localize(datetime.datetime(2019, 10, 28, 0, 0)): {'': 1},
current_tz.localize(datetime.datetime(2019, 10, 29, 0, 0)): {'': 0},
}
self.assertDictEqual(serie, testing_data)

@override_settings(USE_TZ=False)
def test_get_multi_series_distinct_count(self):
"""Test function to check DashboardStats.get_multi_time_series() with distinct count."""
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
django-jsonfield>=0.9.2
django-qsstats-magic>=0.7.2
# django-qsstats-magic>=0.7.2
djcacheutils>=3.0.0
django-nvd3>=0.5.0
django-bower
Expand Down

0 comments on commit 807c044

Please sign in to comment.