From 43c2d552e4e42a82f547a2fcfd67e79d89f405e8 Mon Sep 17 00:00:00 2001 From: bkfox Date: Wed, 23 Aug 2023 15:08:20 +0200 Subject: [PATCH] feat: test middleware; use zoneinfo; fix datetime warnings --- aircox/controllers/log_archiver.py | 2 +- aircox/controllers/sound_stats.py | 1 - aircox/middleware.py | 25 +++++++++---------- aircox/models/diffusion.py | 6 +++-- aircox/models/schedule.py | 12 ++++----- aircox/models/station.py | 2 +- aircox/tests/conftest.py | 24 +++++++++++++++--- aircox/tests/models/test_schedule.py | 6 ++--- .../management/commands/streamer.py | 4 +-- instance/settings/base.py | 4 +-- instance/settings/sample.py | 3 ++- 11 files changed, 53 insertions(+), 36 deletions(-) diff --git a/aircox/controllers/log_archiver.py b/aircox/controllers/log_archiver.py index 18a388e..4460180 100644 --- a/aircox/controllers/log_archiver.py +++ b/aircox/controllers/log_archiver.py @@ -80,7 +80,7 @@ class LogArchiver: def load_file(self, path): with gzip.open(path, "rb") as archive: data = archive.read() - logs = yaml.load(data) + logs = yaml.safe_load(data) # we need to preload diffusions, sounds and tracks rels = { diff --git a/aircox/controllers/sound_stats.py b/aircox/controllers/sound_stats.py index 2317348..3e2180d 100644 --- a/aircox/controllers/sound_stats.py +++ b/aircox/controllers/sound_stats.py @@ -84,7 +84,6 @@ class SoundStats: self.stats = [SoxStats(self.path)] position = 0 length = self.stats[0].get("length") - print(self.stats, "-----") if not self.sample_length: return diff --git a/aircox/middleware.py b/aircox/middleware.py index 4062826..3382ee4 100644 --- a/aircox/middleware.py +++ b/aircox/middleware.py @@ -1,4 +1,5 @@ -import pytz +from zoneinfo import ZoneInfo + from django.db.models import Q from django.utils import timezone as tz @@ -11,38 +12,36 @@ __all__ = ("AircoxMiddleware",) class AircoxMiddleware(object): """Middleware used to get default info for the given website. - Theses + It provide following request attributes: + - ``station``: current Station + This middleware must be set after the middleware 'django.contrib.auth.middleware.AuthenticationMiddleware', """ + timezone_session_key = "aircox.timezone" + def __init__(self, get_response): self.get_response = get_response def get_station(self, request): """Return station for the provided request.""" - expr = Q(default=True) | Q(hosts__contains=request.get_host()) - # case = Case(When(hosts__contains=request.get_host(), then=Value(0)), - # When(default=True, then=Value(32))) + host = request.get_host() + expr = Q(default=True) | Q(hosts=host) | Q(hosts__contains=host + "\n") return Station.objects.filter(expr).order_by("default").first() - # .annotate(resolve_priority=case) \ - # .order_by('resolve_priority').first() def init_timezone(self, request): # note: later we can use http://freegeoip.net/ on user side if # required timezone = None try: - timezone = request.session.get("aircox.timezone") + timezone = request.session.get(self.timezone_session_key) if timezone: - timezone = pytz.timezone(timezone) + timezone = ZoneInfo(timezone) + tz.activate(timezone) except Exception: pass - if not timezone: - timezone = tz.get_current_timezone() - tz.activate(timezone) - def __call__(self, request): self.init_timezone(request) request.station = self.get_station(request) diff --git a/aircox/models/diffusion.py b/aircox/models/diffusion.py index 2949885..db72f51 100644 --- a/aircox/models/diffusion.py +++ b/aircox/models/diffusion.py @@ -39,8 +39,10 @@ class DiffusionQuerySet(RerunQuerySet): def date(self, date=None, order=True): """Diffusions occuring date.""" date = date or datetime.date.today() - start = tz.datetime.combine(date, datetime.time()) - end = tz.datetime.combine(date, datetime.time(23, 59, 59, 999)) + start = tz.make_aware(tz.datetime.combine(date, datetime.time())) + end = tz.make_aware( + tz.datetime.combine(date, datetime.time(23, 59, 59, 999)) + ) # start = tz.get_current_timezone().localize(start) # end = tz.get_current_timezone().localize(end) qs = self.filter(start__range=(start, end)) diff --git a/aircox/models/schedule.py b/aircox/models/schedule.py index e867682..0a7a9a3 100644 --- a/aircox/models/schedule.py +++ b/aircox/models/schedule.py @@ -1,6 +1,6 @@ import calendar +import zoneinfo -import pytz from django.db import models from django.utils import timezone as tz from django.utils.functional import cached_property @@ -49,9 +49,9 @@ class Schedule(Rerun): ) timezone = models.CharField( _("timezone"), - default=lambda: tz.get_current_timezone().zone, + default=lambda: tz.get_current_timezone().key, max_length=100, - choices=[(x, x) for x in pytz.all_timezones], + choices=[(x, x) for x in zoneinfo.available_timezones()], help_text=_("timezone used for the date"), ) duration = models.TimeField( @@ -82,9 +82,7 @@ class Schedule(Rerun): @cached_property def tz(self): """Pytz timezone of the schedule.""" - import pytz - - return pytz.timezone(self.timezone) + return zoneinfo.ZoneInfo(self.timezone) @cached_property def start(self): @@ -110,7 +108,7 @@ class Schedule(Rerun): """Return a datetime set to schedule's time for the provided date, handling timezone (based on schedule's timezone).""" date = tz.datetime.combine(date, self.time) - return self.tz.normalize(self.tz.localize(date)) + return date.replace(tzinfo=self.tz) def dates_of_month(self, date): """Return normalized diffusion dates of provided date's month.""" diff --git a/aircox/models/station.py b/aircox/models/station.py index 72f0e57..e86ad64 100644 --- a/aircox/models/station.py +++ b/aircox/models/station.py @@ -60,7 +60,7 @@ class Station(models.Model): max_length=512, null=True, blank=True, - help_text=_("specify one url per line"), + help_text=_("specify one domain per line, without 'http://' prefix"), ) audio_streams = models.TextField( _("audio streams"), diff --git a/aircox/tests/conftest.py b/aircox/tests/conftest.py index 173949a..42fb8dd 100644 --- a/aircox/tests/conftest.py +++ b/aircox/tests/conftest.py @@ -2,6 +2,7 @@ from datetime import time, timedelta import itertools import logging +from django.conf import settings from django.contrib.auth.models import User from django.test import RequestFactory @@ -16,6 +17,12 @@ req_factory = RequestFactory() """Request Factory used among different tests.""" +settings.ALLOWED_HOSTS = list(settings.ALLOWED_HOSTS) + [ + "sub.server.org", + "server.org", +] + + @pytest.fixture def staff_user(): return baker.make(User, is_active=True, is_staff=True) @@ -31,12 +38,23 @@ def logger(): @pytest.fixture def station(): - return baker.make(models.Station, audio_streams="stream 1\nstream 2") + return baker.make( + models.Station, + hosts="server.org", + audio_streams="stream 1\nstream 2", + default=True, + ) @pytest.fixture -def stations(station): - return [station, baker.make(models.Station)] +def sub_station(): + """Non-default station.""" + return baker.make(models.Station, hosts="sub.server.org", default=False) + + +@pytest.fixture +def stations(station, sub_station): + return [station, sub_station] @pytest.fixture diff --git a/aircox/tests/models/test_schedule.py b/aircox/tests/models/test_schedule.py index 7730915..3ab4834 100644 --- a/aircox/tests/models/test_schedule.py +++ b/aircox/tests/models/test_schedule.py @@ -24,7 +24,7 @@ class TestSchedule: @pytest.mark.django_db def test_tz(self, schedules): for schedule in schedules: - assert schedule.timezone == schedule.tz.zone + assert schedule.timezone == schedule.tz.key @pytest.mark.django_db def test_start(self, schedules): @@ -45,7 +45,7 @@ class TestSchedule: def test_normalize(self, schedules): for schedule in schedules: dt = datetime.combine(schedule.date, schedule.time) - assert schedule.normalize(dt).tzinfo.zone == schedule.timezone + assert schedule.normalize(dt).tzinfo.key == schedule.timezone @pytest.mark.django_db def test_dates_of_month_ponctual(self): @@ -117,7 +117,7 @@ class TestSchedule: assert dt.month == at.month assert dt.weekday() == schedule.date.weekday() assert dt.time() == schedule.time - assert dt.tzinfo.zone == schedule.timezone + assert dt.tzinfo.key == schedule.timezone @pytest.mark.django_db def test_diffusions_of_month(self, sched_initials): diff --git a/aircox_streamer/management/commands/streamer.py b/aircox_streamer/management/commands/streamer.py index 9703f75..0130e3c 100755 --- a/aircox_streamer/management/commands/streamer.py +++ b/aircox_streamer/management/commands/streamer.py @@ -6,11 +6,11 @@ to: - cancels Diffusions that have an archive but could not have been played; - run Liquidsoap """ +from datetime import timezone import time from argparse import RawTextHelpFormatter -import pytz from django.core.management.base import BaseCommand from django.utils import timezone as tz @@ -19,7 +19,7 @@ from aircox_streamer.controllers import Monitor, Streamer # force using UTC -tz.activate(pytz.UTC) +tz.activate(timezone.UTC) class Command(BaseCommand): diff --git a/instance/settings/base.py b/instance/settings/base.py index c58739a..075ff67 100755 --- a/instance/settings/base.py +++ b/instance/settings/base.py @@ -1,7 +1,7 @@ import os import sys +from zoneinfo import ZoneInfo -import pytz from django.utils import timezone sys.path.insert(1, os.path.dirname(os.path.realpath(__file__))) @@ -65,7 +65,7 @@ USE_I18N = True USE_L10N = True USE_TZ = True -timezone.activate(pytz.timezone(TIME_ZONE)) +timezone.activate(ZoneInfo(TIME_ZONE)) try: import locale diff --git a/instance/settings/sample.py b/instance/settings/sample.py index 8339db8..cb90a5b 100644 --- a/instance/settings/sample.py +++ b/instance/settings/sample.py @@ -10,6 +10,7 @@ For Django settings see: https://docs.djangoproject.com/en/3.1/topics/settings/ https://docs.djangoproject.com/en/3.1/ref/settings/ """ +from zoneinfo import ZoneInfo from .prod import * # FOR dev: from .dev import * @@ -20,7 +21,7 @@ LANGUAGE_CODE = "fr-BE" LC_LOCALE = "fr_BE.UTF-8" TIME_ZONE = "Europe/Brussels" -timezone.activate(pytz.timezone(TIME_ZONE)) +timezone.activate(ZoneInfo(TIME_ZONE)) # Secret key: you MUST put a consistent secret key. You can generate one # at https://djecrety.ir/