diff --git a/aircox/models/diffusion.py b/aircox/models/diffusion.py index 972d0dc..2949885 100644 --- a/aircox/models/diffusion.py +++ b/aircox/models/diffusion.py @@ -165,7 +165,7 @@ class Diffusion(Rerun): def save_rerun(self): self.episode = self.initial.episode - self.program = self.episode.program + super().save_rerun() def save_initial(self): self.program = self.episode.program diff --git a/aircox/models/rerun.py b/aircox/models/rerun.py index a6dd9a2..59b7c20 100644 --- a/aircox/models/rerun.py +++ b/aircox/models/rerun.py @@ -64,24 +64,6 @@ class Rerun(models.Model): class Meta: abstract = True - def save(self, *args, **kwargs): - if self.initial is not None: - self.initial = self.initial.get_initial() - if self.initial == self: - self.initial = None - - if self.is_rerun: - self.save_rerun() - else: - self.save_initial() - super().save(*args, **kwargs) - - def save_rerun(self): - pass - - def save_initial(self): - pass - @property def is_initial(self): return self.initial is None @@ -96,7 +78,29 @@ class Rerun(models.Model): def clean(self): super().clean() - if self.initial is not None and self.initial.start >= self.start: + if ( + hasattr(self, "start") + and self.initial is not None + and self.initial.start >= self.start + ): raise ValidationError( {"initial": _("rerun must happen after original")} ) + + def save_rerun(self): + self.program = self.initial.program + + def save_initial(self): + pass + + def save(self, *args, **kwargs): + if self.initial is not None: + self.initial = self.initial.get_initial() + if self.initial == self: + self.initial = None + + if self.is_rerun: + self.save_rerun() + else: + self.save_initial() + super().save(*args, **kwargs) diff --git a/aircox/models/schedule.py b/aircox/models/schedule.py index b350ac8..ef1388d 100644 --- a/aircox/models/schedule.py +++ b/aircox/models/schedule.py @@ -75,8 +75,8 @@ class Schedule(Rerun): self.time.strftime("%H:%M"), ) - def save_rerun(self, *args, **kwargs): - self.program = self.initial.program + def save_rerun(self): + super().save_rerun() self.duration = self.initial.duration self.frequency = self.initial.frequency @@ -107,23 +107,6 @@ class Schedule(Rerun): .capitalize() ) - # initial cached data - __initial = None - - def changed(self, fields=["date", "duration", "frequency", "timezone"]): - initial = self._Schedule__initial - - if not initial: - return - - this = self.__dict__ - - for field in fields: - if initial.get(field) != this.get(field): - return True - - return False - def normalize(self, date): """Return a datetime set to schedule's time for the provided date, handling timezone (based on schedule's timezone).""" @@ -245,10 +228,3 @@ class Schedule(Rerun): end=date + duration, ) return episodes.values(), diffusions.values() - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - # TODO/FIXME: use validators? - if self.initial is not None and self.date > self.date: - raise ValueError("initial must be later") diff --git a/aircox/tests/conftest.py b/aircox/tests/conftest.py new file mode 100644 index 0000000..ed26e75 --- /dev/null +++ b/aircox/tests/conftest.py @@ -0,0 +1,63 @@ +from datetime import time, timedelta +import itertools + +import pytest +from model_bakery import baker + +from aircox.models import Diffusion + + +@pytest.fixture +def stations(): + return baker.make("aircox.station", quantity=2) + + +@pytest.fixture +def programs(stations): + return list( + itertools.chain( + *( + baker.make("aircox.program", quantity=3, station=station) + for station in stations + ) + ) + ) + + +@pytest.fixture +def sched_initials(programs): + # use concrete class + return [ + baker.make("aircox.schedule", program=program, time=time(16, 00)) + for program in programs + ] + + +@pytest.fixture +def sched_reruns(initials): + # use concrete class + return [ + baker.make( + "aircox.schedule", + initial=initial, + date=initial.date, + time=(initial.start + timedelta(hours=1)).time(), + ) + for initial in initials + ] + + +@pytest.fixture +def schedules(sched_initials, sched_reruns): + return sched_initials + sched_reruns + + +@pytest.fixture +def episodes(programs): + items = [] + for program in programs: + items += [ + baker.make("aircox.episode", parent=program, type=type) + for type, _ in Diffusion.TYPE_CHOICES + ] + return items diff --git a/aircox/tests/models/test_diffusion.py b/aircox/tests/models/test_diffusion.py new file mode 100644 index 0000000..26b177b --- /dev/null +++ b/aircox/tests/models/test_diffusion.py @@ -0,0 +1,19 @@ +import pytest + + +class TestDiffusionQuerySet: + @pytest.mark.django_db + def test_episode_by_obj(self, episodes): + pass + + @pytest.mark.django_db + def test_episode_by_id(self, episodes): + pass + + @pytest.mark.django_db + def test_on_air(self, episodes): + pass + + @pytest.mark.django_db + def test_now(self, episodes): + pass diff --git a/aircox/tests/models/test_rerun.py b/aircox/tests/models/test_rerun.py new file mode 100644 index 0000000..7f6529e --- /dev/null +++ b/aircox/tests/models/test_rerun.py @@ -0,0 +1,117 @@ +from datetime import timedelta + +import pytest + +from django.core.exceptions import ValidationError + +# we use Schedule as concrete class (Rerun is abstract) +from aircox.models import Schedule + + +class TestRerunQuerySet: + @pytest.mark.django_db + def test_station_by_obj(self, stations): + for station in stations: + queryset = ( + Schedule.objects.station(station) + .distinct() + .value_list("station", flat=True) + ) + assert queryset.count() == 1 + assert queryset.first() == station + + @pytest.mark.django_db + def test_station_by_id(self, stations): + for station in stations: + queryset = ( + Schedule.objects.station(id=station.pk) + .distinct() + .value_list("station", flat=True) + ) + assert queryset.count() == 1 + assert queryset.first() == station + + @pytest.mark.django_db + def test_program_by_obj(self, programs): + for program in programs: + queryset = ( + Schedule.objects.program(program) + .distinct() + .value_list("program", flat=True) + ) + assert queryset.count() == 1 + assert queryset.first() == program + + @pytest.mark.django_db + def test_program_by_id(self, programs): + for program in programs: + queryset = ( + Schedule.objects.program(id=program.pk) + .distinct() + .value_list("program", flat=True) + ) + assert queryset.count() == 1 + assert queryset.first() == program + + @pytest.mark.django_db + def test_rerun(self, sched_reruns): + queryset = Schedule.objects.rerun().value_list("initial", flat=True) + assert None not in queryset + + @pytest.mark.django_db + def test_initial(self, sched_initials): + queryset = ( + Schedule.objects.rerun() + .distinct() + .value_list("initial", flat=True) + ) + assert queryset.count() == 1 + assert queryset.first() is None + + +class TestRerun: + @pytest.mark.django_db + def test_is_initial_true(self, sched_initials): + assert all(r.is_initial for r in sched_initials) + + @pytest.mark.django_db + def test_is_initial_false(self, sched_reruns): + assert all(not r.is_initial for r in sched_reruns) + + @pytest.mark.django_db + def test_is_rerun_true(self, sched_reruns): + assert all(r.is_rerun for r in sched_reruns) + + @pytest.mark.django_db + def test_is_rerun_false(self, sched_initials): + assert all(not r.is_rerun for r in sched_initials) + + @pytest.mark.django_db + def test_get_initial_of_initials(self, sched_initials): + assert all(r.get_initial() is r for r in sched_initials) + + @pytest.mark.django_db + def test_get_initial_of_reruns(self, sched_reruns): + assert all(r.get_initial() is r.initial for r in sched_reruns) + + @pytest.mark.django_db + def test_clean_success(self, sched_reruns): + for rerun in sched_reruns: + rerun.clean() + + @pytest.mark.django_db + def test_clean_fails(self, sched_reruns): + for rerun in sched_reruns: + rerun.time = (rerun.initial.start - timedelta(hours=2)).time() + with pytest.raises(ValidationError): + rerun.clean() + + @pytest.mark.django_db + def test_save_rerun(self, sched_reruns): + for rerun in sched_reruns: + rerun.program = None + rerun.save_rerun() + assert rerun.program == rerun.initial.program + + # TODO: save() + # save_initial is empty, thus not tested diff --git a/aircox/tests/models/test_schedule.py b/aircox/tests/models/test_schedule.py new file mode 100644 index 0000000..bde431e --- /dev/null +++ b/aircox/tests/models/test_schedule.py @@ -0,0 +1,74 @@ +import pytest +from datetime import datetime + +from aircox import utils + + +class TestSchedule: + @pytest.mark.django_db + def test_save_rerun(self, sched_reruns): + for schedule in sched_reruns: + schedule.duration = None + schedule.frequency = None + schedule.save_rerun() + assert schedule.program == schedule.initial.program + assert schedule.duration == schedule.initial.duration + assert schedule.frequency == schedule.initial.frequency + + @pytest.mark.django_db + def test_tz(self, schedules): + for schedule in schedules: + assert schedule.timezone == schedule.tz.zone + + @pytest.mark.django_db + def test_start(self, schedules): + for schedule in schedules: + assert schedule.start.date() == schedule.date + assert schedule.start.time() == schedule.time + + @pytest.mark.django_db + def test_end(self, schedules): + for schedule in schedules: + delta = utils.to_timedelta(schedule.duration) + assert schedule.end - schedule.start == delta + + # def test_get_frequency_verbose(self): + # pass + + @pytest.mark.django_db + def test_normalize(self, schedules): + for schedule in schedules: + dt = datetime.combine(schedule.date, schedule.time) + assert schedule.normalize(dt).tzinfo.zone == schedule.timezone.zone + + @pytest.mark.django_db + def test_dates_of_month_ponctual(self): + pass + + @pytest.mark.django_db + def test_dates_of_month_n_day_of_month(self): + pass + + @pytest.mark.django_db + def test_dates_of_month_first_and_third(self): + pass + + @pytest.mark.django_db + def test_dates_of_month_second_and_fourth(self): + pass + + @pytest.mark.django_db + def test_dates_of_month_every(self): + pass + + @pytest.mark.django_db + def test_dates_of_month_one_on_two(self): + pass + + @pytest.mark.django_db + def test__exclude_existing_date(self): + pass + + @pytest.mark.django_db + def test_diffusions_of_month(self): + pass diff --git a/requirements_tests.txt b/requirements_tests.txt index d18e0aa..9007f2b 100644 --- a/requirements_tests.txt +++ b/requirements_tests.txt @@ -1,2 +1,3 @@ pytest~=7.2 pytest-django~=4.5 +model_bakery~=1.10