From c35b802b08b88ab32c6a4aba7827c67a3f93670c Mon Sep 17 00:00:00 2001 From: juuso-j <68938778+juuso-j@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:12:12 +0300 Subject: [PATCH 1/7] Replace django.utils.timezone with datetime.timezone django.utils.timezone is deprecated in Django 5.0 --- .../management/commands/import_traffic_situations.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/exceptional_situations/management/commands/import_traffic_situations.py b/exceptional_situations/management/commands/import_traffic_situations.py index a936a1f9a..61ccf8e53 100644 --- a/exceptional_situations/management/commands/import_traffic_situations.py +++ b/exceptional_situations/management/commands/import_traffic_situations.py @@ -4,13 +4,12 @@ import logging from copy import deepcopy -from datetime import datetime +from datetime import datetime, timezone import requests from dateutil import parser from django.contrib.gis.geos import GEOSGeometry, Polygon from django.core.management import BaseCommand -from django.utils import timezone from munigeo.models import Municipality from exceptional_situations.models import ( @@ -148,7 +147,7 @@ def save_features(self, features): if release_time.microsecond != 0: release_time.replace(microsecond=0) - release_time = timezone.make_aware(release_time, timezone.utc) + release_time = release_time.replace(tzinfo=timezone.utc) type_name = properties.get("situationType", None) sub_type_name = properties.get("trafficAnnouncementType", None) From f8ffdff67e53bfb7ed45a491d00b456c6f989eaa Mon Sep 17 00:00:00 2001 From: juuso-j <68938778+juuso-j@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:14:19 +0300 Subject: [PATCH 2/7] Change announcement start_time to NOW --- exceptional_situations/tests/conftest.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exceptional_situations/tests/conftest.py b/exceptional_situations/tests/conftest.py index 18ebb85d0..36b0adba3 100644 --- a/exceptional_situations/tests/conftest.py +++ b/exceptional_situations/tests/conftest.py @@ -61,12 +61,12 @@ def locations(): def announcements(locations, municipalities): json_data = {"test_key": "test_value"} sa = SituationAnnouncement.objects.create( - title="two hours", - description="two hours long situation", + title="Twelve hours", + description="Twelve hours long situation", additional_info=json_data, location=locations[0], - start_time=NOW - timedelta(hours=1), - end_time=NOW + timedelta(hours=1), + start_time=NOW, + end_time=NOW + timedelta(hours=12), ) sa.municipalities.add(municipalities.filter(id="turku").first()) sa.municipalities.add(municipalities.filter(id="lieto").first()) From 1c26e2864bd817550ba1dc5379c4e67f0e1ca9ad Mon Sep 17 00:00:00 2001 From: juuso-j <68938778+juuso-j@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:15:49 +0300 Subject: [PATCH 3/7] Fix test_situation_filter_by_start_time Subtract 6 hours from start_time, otherwise the test can fail in cases where the tests takes long time to execute. --- exceptional_situations/tests/test_api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exceptional_situations/tests/test_api.py b/exceptional_situations/tests/test_api.py index cc32fc3f9..1278daffc 100644 --- a/exceptional_situations/tests/test_api.py +++ b/exceptional_situations/tests/test_api.py @@ -71,11 +71,12 @@ def test_situation_retrieve(api_client, situations): @pytest.mark.django_db def test_situation_filter_by_start_time(api_client, situations): - start_time = timezone.now() + start_time = timezone.now() - timedelta(hours=6) response = api_client.get( SITUATION_LIST_URL + f"?start_time__gt={datetime.strftime(start_time, DATETIME_FORMAT)}" ) + assert response.json()["count"] == 1 response = api_client.get( SITUATION_LIST_URL From 999d0476792a50e0586a5e4c3caeef4b74f24fd3 Mon Sep 17 00:00:00 2001 From: juuso-j <68938778+juuso-j@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:24:08 +0300 Subject: [PATCH 4/7] Set now = timezone.now() inside functions This removes errors that occurs of how fast tests are run --- exceptional_situations/tests/test_models.py | 46 +++++++++++---------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/exceptional_situations/tests/test_models.py b/exceptional_situations/tests/test_models.py index db1c38fcd..0561ce4a4 100644 --- a/exceptional_situations/tests/test_models.py +++ b/exceptional_situations/tests/test_models.py @@ -5,15 +5,15 @@ from exceptional_situations.models import Situation, SituationAnnouncement -NOW = timezone.now() - @pytest.mark.django_db def test_situation_is_active(situation_types): - announcement_1 = SituationAnnouncement.objects.create(start_time=NOW, title="test1") - announcement_2 = SituationAnnouncement.objects.create(start_time=NOW, title="test2") + now = timezone.now() + + announcement_1 = SituationAnnouncement.objects.create(start_time=now, title="test1") + announcement_2 = SituationAnnouncement.objects.create(start_time=now, title="test2") situation = Situation.objects.create( - release_time=NOW, situation_type=situation_types.first(), situation_id="TestID" + release_time=now, situation_type=situation_types.first(), situation_id="TestID" ) assert situation.is_active is False @@ -21,38 +21,39 @@ def test_situation_is_active(situation_types): situation.announcements.add(announcement_2) assert situation.is_active is True - announcement_1.start_time = NOW - timedelta(days=2) - announcement_1.end_time = NOW - timedelta(days=1) + announcement_1.start_time = now - timedelta(days=2) + announcement_1.end_time = now - timedelta(days=1) announcement_1.save() - announcement_2.start_time = NOW - timedelta(hours=2) - announcement_2.end_time = NOW - timedelta(hours=1) + announcement_2.start_time = now - timedelta(hours=2) + announcement_2.end_time = now - timedelta(hours=1) announcement_2.save() assert situation.is_active is False - announcement_2.start_time = NOW - timedelta(hours=2) - announcement_2.end_time = NOW + timedelta(hours=1) + announcement_2.start_time = now - timedelta(hours=2) + announcement_2.end_time = now + timedelta(hours=1) announcement_2.save() assert situation.is_active is True # Test that returns False if all start times are in future - announcement_1.start_time = NOW + timedelta(days=2) - announcement_1.end_time = NOW + timedelta(days=3) + announcement_1.start_time = now + timedelta(days=2) + announcement_1.end_time = now + timedelta(days=3) announcement_1.save() - announcement_2.start_time = NOW + timedelta(hours=2) - announcement_2.end_time = NOW + timedelta(hours=3) + announcement_2.start_time = now + timedelta(hours=2) + announcement_2.end_time = now + timedelta(hours=3) announcement_2.save() assert situation.is_active is False @pytest.mark.django_db def test_situation_start_time(situation_types): + now = timezone.now() announcement_1 = SituationAnnouncement.objects.create( - start_time=NOW, title="starts now" + start_time=now, title="starts now" ) announcement_2 = SituationAnnouncement.objects.create( - start_time=NOW - timedelta(hours=1), title="started an hour ago" + start_time=now - timedelta(hours=1), title="started an hour ago" ) situation = Situation.objects.create( - release_time=NOW, situation_type=situation_types.first(), situation_id="TestID" + release_time=now, situation_type=situation_types.first(), situation_id="TestID" ) situation.announcements.add(announcement_1) situation.announcements.add(announcement_2) @@ -61,16 +62,17 @@ def test_situation_start_time(situation_types): @pytest.mark.django_db def test_situation_end_time(situation_types): + now = timezone.now() announcement_1 = SituationAnnouncement.objects.create( - start_time=NOW - timedelta(hours=1), - end_time=NOW + timedelta(hours=2), + start_time=now - timedelta(hours=1), + end_time=now + timedelta(hours=2), title="ends after two hours", ) announcement_2 = SituationAnnouncement.objects.create( - start_time=NOW, end_time=NOW + timedelta(days=2), title="ends after two days" + start_time=now, end_time=now + timedelta(days=2), title="ends after two days" ) situation = Situation.objects.create( - release_time=NOW, situation_type=situation_types.first(), situation_id="TestID" + release_time=now, situation_type=situation_types.first(), situation_id="TestID" ) situation.announcements.add(announcement_1) situation.announcements.add(announcement_2) From 2979eb85b710f9801ab17c7b5f6dc2c1b6391098 Mon Sep 17 00:00:00 2001 From: juuso-j <68938778+juuso-j@users.noreply.github.com> Date: Tue, 3 Sep 2024 11:23:49 +0300 Subject: [PATCH 5/7] Add datetime.now() as fixture --- exceptional_situations/tests/conftest.py | 38 +++++++++++++----------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/exceptional_situations/tests/conftest.py b/exceptional_situations/tests/conftest.py index 36b0adba3..cb3e5048c 100644 --- a/exceptional_situations/tests/conftest.py +++ b/exceptional_situations/tests/conftest.py @@ -1,4 +1,4 @@ -from datetime import timedelta +from datetime import datetime, timedelta import pytest from django.contrib.gis.geos import GEOSGeometry @@ -13,14 +13,17 @@ SituationType, ) -NOW = timezone.now() - @pytest.fixture def api_client(): return APIClient() +@pytest.fixture +def now(): + return datetime.now().replace(tzinfo=timezone.get_default_timezone()) + + @pytest.mark.django_db @pytest.fixture def municipalities(): @@ -58,15 +61,16 @@ def locations(): @pytest.mark.django_db @pytest.fixture -def announcements(locations, municipalities): +def announcements(locations, municipalities, now): + json_data = {"test_key": "test_value"} sa = SituationAnnouncement.objects.create( title="Twelve hours", description="Twelve hours long situation", additional_info=json_data, location=locations[0], - start_time=NOW, - end_time=NOW + timedelta(hours=12), + start_time=now, + end_time=now + timedelta(hours=12), ) sa.municipalities.add(municipalities.filter(id="turku").first()) sa.municipalities.add(municipalities.filter(id="lieto").first()) @@ -75,8 +79,8 @@ def announcements(locations, municipalities): description="two days long situation", additional_info=json_data, location=locations[1], - start_time=NOW - timedelta(days=1), - end_time=NOW + timedelta(days=1), + start_time=now - timedelta(days=1), + end_time=now + timedelta(days=1), ) sa.municipalities.add(municipalities.filter(id="raisio").first()) return SituationAnnouncement.objects.all() @@ -84,24 +88,24 @@ def announcements(locations, municipalities): @pytest.mark.django_db @pytest.fixture -def inactive_announcements(locations): +def inactive_announcements(locations, now): json_data = {"test_key": "test_value"} SituationAnnouncement.objects.create( title="in past", description="inactive announcement", additional_info=json_data, location=locations[2], - start_time=NOW - timedelta(days=2), - end_time=NOW - timedelta(days=1), + start_time=now - timedelta(days=2), + end_time=now - timedelta(days=1), ) return SituationAnnouncement.objects.all() @pytest.mark.django_db @pytest.fixture -def inactive_situations(situation_types, inactive_announcements): +def inactive_situations(situation_types, inactive_announcements, now): situation = Situation.objects.create( - release_time=NOW, + release_time=now, situation_id="inactive", situation_type=situation_types.first(), ) @@ -111,15 +115,15 @@ def inactive_situations(situation_types, inactive_announcements): @pytest.mark.django_db @pytest.fixture -def situations(situation_types, announcements): +def situations(situation_types, announcements, now): situation = Situation.objects.create( - release_time=NOW, - situation_id="TwoHoursLong", + release_time=now, + situation_id="TwelveHoursLong", situation_type=situation_types.first(), ) situation.announcements.add(announcements[0]) situation = Situation.objects.create( - release_time=NOW - timedelta(days=1), + release_time=now - timedelta(days=1), situation_id="TwoDaysLong", situation_type=situation_types.first(), ) From ab76d40130162a8d44af57587b0af04af0462345 Mon Sep 17 00:00:00 2001 From: juuso-j <68938778+juuso-j@users.noreply.github.com> Date: Tue, 3 Sep 2024 11:46:28 +0300 Subject: [PATCH 6/7] Use now fixture --- exceptional_situations/tests/test_api.py | 13 ++++++------- exceptional_situations/tests/test_models.py | 11 ++++------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/exceptional_situations/tests/test_api.py b/exceptional_situations/tests/test_api.py index 1278daffc..d1f979c4f 100644 --- a/exceptional_situations/tests/test_api.py +++ b/exceptional_situations/tests/test_api.py @@ -1,7 +1,6 @@ from datetime import datetime, timedelta import pytest -from django.utils import timezone from rest_framework.reverse import reverse SITUATION_LIST_URL = reverse("exceptional_situations:situation-list") @@ -70,8 +69,8 @@ def test_situation_retrieve(api_client, situations): @pytest.mark.django_db -def test_situation_filter_by_start_time(api_client, situations): - start_time = timezone.now() - timedelta(hours=6) +def test_situation_filter_by_start_time(api_client, situations, now): + start_time = now - timedelta(hours=6) response = api_client.get( SITUATION_LIST_URL + f"?start_time__gt={datetime.strftime(start_time, DATETIME_FORMAT)}" @@ -84,7 +83,7 @@ def test_situation_filter_by_start_time(api_client, situations): ) assert response.json()["count"] == 1 - start_time = timezone.now() - timedelta(days=2) + start_time = now - timedelta(days=2) response = api_client.get( SITUATION_LIST_URL + f"?start_time__gt={datetime.strftime(start_time, DATETIME_FORMAT)}" @@ -98,8 +97,8 @@ def test_situation_filter_by_start_time(api_client, situations): @pytest.mark.django_db -def test_situation_filter_by_end_time(api_client, situations): - end_time = timezone.now() +def test_situation_filter_by_end_time(api_client, situations, now): + end_time = now response = api_client.get( SITUATION_LIST_URL + f"?end_time__gt={datetime.strftime(end_time, DATETIME_FORMAT)}" @@ -111,7 +110,7 @@ def test_situation_filter_by_end_time(api_client, situations): ) assert response.json()["count"] == 0 - end_time = timezone.now() - timedelta(days=2) + end_time = now - timedelta(days=2) response = api_client.get( SITUATION_LIST_URL + f"?end_time__gt={datetime.strftime(end_time, DATETIME_FORMAT)}" diff --git a/exceptional_situations/tests/test_models.py b/exceptional_situations/tests/test_models.py index 0561ce4a4..56ee23d1d 100644 --- a/exceptional_situations/tests/test_models.py +++ b/exceptional_situations/tests/test_models.py @@ -1,20 +1,19 @@ from datetime import timedelta import pytest -from django.utils import timezone from exceptional_situations.models import Situation, SituationAnnouncement @pytest.mark.django_db -def test_situation_is_active(situation_types): - now = timezone.now() +def test_situation_is_active(situation_types, now): announcement_1 = SituationAnnouncement.objects.create(start_time=now, title="test1") announcement_2 = SituationAnnouncement.objects.create(start_time=now, title="test2") situation = Situation.objects.create( release_time=now, situation_type=situation_types.first(), situation_id="TestID" ) + # No announcements, returns False assert situation.is_active is False situation.announcements.add(announcement_1) @@ -44,8 +43,7 @@ def test_situation_is_active(situation_types): @pytest.mark.django_db -def test_situation_start_time(situation_types): - now = timezone.now() +def test_situation_start_time(situation_types, now): announcement_1 = SituationAnnouncement.objects.create( start_time=now, title="starts now" ) @@ -61,8 +59,7 @@ def test_situation_start_time(situation_types): @pytest.mark.django_db -def test_situation_end_time(situation_types): - now = timezone.now() +def test_situation_end_time(situation_types, now): announcement_1 = SituationAnnouncement.objects.create( start_time=now - timedelta(hours=1), end_time=now + timedelta(hours=2), From 389f718fef76e45513b86302d7b1f3986c49d596 Mon Sep 17 00:00:00 2001 From: juuso-j <68938778+juuso-j@users.noreply.github.com> Date: Tue, 3 Sep 2024 12:05:06 +0300 Subject: [PATCH 7/7] Make current time timezone aware --- exceptional_situations/models.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exceptional_situations/models.py b/exceptional_situations/models.py index a4c10d616..894b92e84 100644 --- a/exceptional_situations/models.py +++ b/exceptional_situations/models.py @@ -68,12 +68,14 @@ def situation_sub_type_str(self) -> str: @property def is_active(self) -> bool: + now = datetime.now().replace(tzinfo=timezone.get_default_timezone()) if not self.announcements.exists(): return False start_times_in_future = all( - {a.start_time > timezone.now() for a in self.announcements.all()} + {a.start_time > now for a in self.announcements.all()} ) + # If all start times are in future, return False if start_times_in_future: return False @@ -84,7 +86,7 @@ def is_active(self) -> bool: # If end_time is past for all announcements, return True, else False return any( { - a.end_time > timezone.now() + a.end_time > now for a in self.announcements.filter(end_time__isnull=False) } )