Skip to content

Commit

Permalink
Logout POST + 4.1 changes
Browse files Browse the repository at this point in the history
- Modified the logout button to use POST requests instead of GET requests, as these will be deprecated in Django 5.0
- Replaced calls to django.utils.timezone.utc to datetime.timezone.utc
- Fixed several issues where a mtm or fk-relation was being retrieved from an unsaved object (without a pk)
- Changed Django's default form renderer to the new 5.0 div renderer.
  • Loading branch information
EricTRL committed Oct 27, 2024
1 parent 997634d commit 0740f20
Show file tree
Hide file tree
Showing 16 changed files with 283 additions and 275 deletions.
20 changes: 12 additions & 8 deletions activity_calendar/feeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,18 @@ def item_exdate(self, item):
# The RDATES in the recurrency module store only dates and not times, so we need to address that
exclude_dates += list(util.set_time_for_RDATE_EXDATE(item.recurrences.exdates, item.start_date))

cancelled_moments = item.activitymoment_set.filter(status=ActivityStatus.STATUS_REMOVED).values_list(
"recurrence_id", flat=True
)
tz = timezone.get_current_timezone()
exclude_dates += filter(
lambda occ: occ not in exclude_dates,
map(lambda occ: occ.astimezone(tz), cancelled_moments),
)
if item.pk:
# Some feeds create activities on the fly (e.g. BirthdayCalendarFeed)
# Those activities cannot retrieve their corresponding activitymoments, it'll cause a ValueError
# Since these activities won't have activitymoments anyway, we can skip this step for them
cancelled_moments = item.activitymoment_set.filter(status=ActivityStatus.STATUS_REMOVED).values_list(
"recurrence_id", flat=True
)
tz = timezone.get_current_timezone()
exclude_dates += filter(
lambda occ: occ not in exclude_dates,
map(lambda occ: occ.astimezone(tz), cancelled_moments),
)

# If there are no exclude_dates, don't bother including it in the icalendar file
return exclude_dates or None
Expand Down
27 changes: 27 additions & 0 deletions activity_calendar/migrations/0029_alter_activity_slot_creation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.1.13 on 2024-10-27 18:44

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("activity_calendar", "0028_remove_activity_is_public"),
]

operations = [
migrations.AlterField(
model_name="activity",
name="slot_creation",
field=models.CharField(
choices=[
("CREATION_STAFF", "By Organisers"),
("CREATION_AUTO", "Automatically"),
("CREATION_USER", "By Users"),
("CREATION_NONE", "No signup"),
],
default="CREATION_NONE",
max_length=15,
),
),
]
4 changes: 4 additions & 0 deletions activity_calendar/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,10 @@ def get_slots(self):
Gets all slots for this activity moment
:return: Queryset of all slots associated with this activity at this moment
"""
if not self.pk:
# If the activitymoment wasn't created yet, it cannot have any slots.
# Attempting to retrieve them anyway will raise a ValueError
return ActivitySlot.objects.none()
return self.activity_slot_set.all()

def is_open_for_subscriptions(self):
Expand Down
370 changes: 173 additions & 197 deletions activity_calendar/tests/test_models.py

Large diffs are not rendered by default.

14 changes: 6 additions & 8 deletions activity_calendar/tests/test_templatetags.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
from datetime import timedelta

from django.test import TestCase
from django.utils import timezone, dateparse

from datetime import datetime, timedelta, timezone
from unittest.mock import patch

from . import mock_now
from django.test import TestCase
from django.utils import dateparse

from activity_calendar.constants import ActivityStatus
from activity_calendar.models import Activity, ActivityMoment
from activity_calendar.templatetags.activity_tags import readable_activity_datetime, get_next_activity_instances
from . import mock_now


class TestActivityTags(TestCase):
Expand Down Expand Up @@ -45,7 +43,7 @@ def test_readable_activity_datetime_display_end_time_mulitple_days(self):
self._set_activity_vars(
full_day=False,
display_end_time=True,
end_date=timezone.datetime(2020, 8, 16, 10, 0, 0, tzinfo=timezone.utc),
end_date=datetime(2020, 8, 16, 10, 0, 0, tzinfo=timezone.utc),
)

self.assertEqual(
Expand All @@ -64,7 +62,7 @@ def test_readable_activity_datetime_full_day_display_end_time_over_multiple_days
self._set_activity_vars(
full_day=True,
display_end_time=True,
end_date=timezone.datetime(2020, 8, 16, 10, 0, 0, tzinfo=timezone.utc),
end_date=datetime(2020, 8, 16, 10, 0, 0, tzinfo=timezone.utc),
)
self.assertEqual(readable_activity_datetime(self.activity_moment), "Friday 14 August - Sunday 16 August")

Expand Down
72 changes: 33 additions & 39 deletions activity_calendar/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
import datetime
from datetime import datetime, timedelta, timezone
from unittest.mock import patch

from django.test import TestCase, Client
from django.contrib import messages
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.test import TestCase, Client
from django.test.utils import override_settings
from django.utils import timezone, dateparse
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from django.utils import dateparse
from django.utils.translation import gettext_lazy as _
from django.views.generic import ListView, FormView, TemplateView

from unittest.mock import patch

from activity_calendar.models import *
from activity_calendar.views import (
CreateSlotView,
ActivityMomentWithSlotsView,
ActivitySimpleMomentView,
EditActivityMomentView,
ActivityOverview,
ActivityMixin,
ActivityMomentCancelledView,
Expand All @@ -26,14 +25,10 @@
)
from activity_calendar.forms import *
from activity_calendar.constants import ActivityType, SlotCreationType, ActivityStatus

from core.tests.util import suppress_warnings
from utils.testing.view_test_utils import ViewValidityMixin, TestMixinMixin

from . import mock_now, mock_is_organiser

from django.contrib.auth import get_user_model

User = get_user_model()

##################################################################################
Expand All @@ -53,7 +48,7 @@ def setUp(self):
self.user = User.objects.create(username="new_user")
self.client.force_login(self.user)

self.recurrence_id = datetime.datetime.fromisoformat(self.default_iso_dt)
self.recurrence_id = datetime.fromisoformat(self.default_iso_dt)
self.activity = Activity.objects.get(id=self.default_activity_id)

self.activity_moment, _ = ActivityMoment.objects.get_or_create(
Expand All @@ -64,7 +59,7 @@ def setUp(self):
"activity_calendar:" + self.default_url_name,
kwargs={
"activity_id": self.default_activity_id,
"recurrence_id": datetime.datetime.fromisoformat(self.default_iso_dt),
"recurrence_id": datetime.fromisoformat(self.default_iso_dt),
},
)
super(TestActivityViewMixin, self).setUp()
Expand All @@ -87,7 +82,7 @@ def _build_response(self, method, iso_dt=None, url_name=None, activity_id=2, fol
"activity_calendar:" + url_name,
kwargs={
"activity_id": activity_id,
"recurrence_id": datetime.datetime.fromisoformat(iso_dt),
"recurrence_id": datetime.fromisoformat(iso_dt),
},
),
data=data,
Expand Down Expand Up @@ -136,7 +131,7 @@ def setUp(self):
"activity_calendar:activity_slots_on_day",
kwargs={
"activity_id": 2,
"recurrence_id": datetime.datetime.fromisoformat("2020-08-19T14:00:00").replace(tzinfo=timezone.utc),
"recurrence_id": datetime.fromisoformat("2020-08-19T14:00:00").replace(tzinfo=timezone.utc),
},
)

Expand All @@ -147,8 +142,8 @@ def test_get_slots_invalid_dates(self, mock_tz):
response = self.client.get("/calendar/activity/2//", data={})
self.assertEqual(response.status_code, 404)

# No occurence at the given date
non_matching_dt = datetime.datetime.fromisoformat("2020-08-20T14:00:00").replace(tzinfo=timezone.utc)
# No occurrence at the given date
non_matching_dt = datetime.fromisoformat("2020-08-20T14:00:00").replace(tzinfo=timezone.utc)
response = self.client.get(
reverse(
"activity_calendar:activity_slots_on_day", kwargs={"activity_id": 2, "recurrence_id": non_matching_dt}
Expand All @@ -159,7 +154,7 @@ def test_get_slots_invalid_dates(self, mock_tz):

@patch("django.utils.timezone.now", side_effect=mock_now())
def test_get_slots_valid_date(self, mock_tz):
# Valid (occurence) date given
# Valid (occurrence) date given
response = self.client.get(self.base_url, data={})
self.assertEqual(response.status_code, 200)

Expand All @@ -168,7 +163,7 @@ def test_get_slots_valid_date(self, mock_tz):
self.assertEqual(context["activity"].title, "Boardgame Evening")
self.assertEqual(
context["recurrence_id"],
datetime.datetime.fromisoformat("2020-08-19T14:00:00").replace(tzinfo=timezone.utc),
datetime.fromisoformat("2020-08-19T14:00:00").replace(tzinfo=timezone.utc),
)

slots = []
Expand All @@ -193,7 +188,7 @@ def test_invalid_post_url(self, mock_tz):
response = self.client.post("/calendar/activity/2/", data={})
self.assertEqual(response.status_code, 404)

# No occurence at the given date
# No occurrence at the given date
response = self.client.post("/calendar/activity/2/2020-09-03T14%3A00%3A00%2B00%3A00/", data={})
self.assertEqual(response.status_code, 404)

Expand Down Expand Up @@ -294,7 +289,7 @@ def test_context_data(self):
self.assertIn("show_participants", context_data)

def test_can_edit_activity_through_permission(self):
"""Tests that editing access is availlable through change_activitymoment permission"""
"""Tests that editing access is available through change_activitymoment permission"""
# Set base user to 2 as that is not a superuser
self.base_user_id = 2

Expand Down Expand Up @@ -393,7 +388,7 @@ def test_base_class_values(self):
self.assertEqual(ActivitySimpleMomentView.form_class, RegisterForActivityForm)
self.assertEqual(ActivitySimpleMomentView.template_name, "activity_calendar/activity_page_no_slots.html")

# Test errror messages
# Test error messages
self.assertIn("activity-full", ActivitySimpleMomentView.error_messages)
self.assertIn("already-registered", ActivitySimpleMomentView.error_messages)
self.assertIn("not-registered", ActivitySimpleMomentView.error_messages)
Expand Down Expand Up @@ -502,7 +497,7 @@ def test_invalid_post(self, mock_tz):
response, level=messages.ERROR, text=ActivitySimpleMomentView.error_messages["not-registered"]
)

@patch("django.utils.timezone.now", side_effect=mock_now(datetime.datetime(2020, 8, 25, 0, 0)))
@patch("django.utils.timezone.now", side_effect=mock_now(datetime(2020, 8, 25, 0, 0)))
def test_outdated_activity(self, mock_tz):
# The basic set-up is valid. User can create a slot
response = self.build_get_response()
Expand All @@ -517,8 +512,8 @@ def _verify_show_participants(self, recurrence_id, permissions, should_show_part
self.user.user_permissions.add(*permissions)

# Modify activity start/end time
self.activity.start_date = datetime.datetime.fromisoformat(recurrence_id)
self.activity.end_date = self.activity.start_date + datetime.timedelta(hours=2)
self.activity.start_date = datetime.fromisoformat(recurrence_id)
self.activity.end_date = self.activity.start_date + timedelta(hours=2)
self.activity.save()

response = self.build_get_response(iso_dt=recurrence_id)
Expand Down Expand Up @@ -549,7 +544,7 @@ def test_activitymoment_alt_start_date_text(self):
"""Tests which text appear for activity moments that have a different start_date than their
occurrence
"""
recurrence_id = timezone.datetime(2020, 8, 14, 19, 0, 0, tzinfo=timezone.utc)
recurrence_id = datetime(2020, 8, 14, 19, 0, 0, tzinfo=timezone.utc)
self.activity_moment = ActivityMoment.objects.get(
parent_activity=self.activity,
recurrence_id=recurrence_id,
Expand All @@ -561,20 +556,20 @@ def test_activitymoment_alt_start_date_text(self):
self.assertNotContains(response, "than normal!")

# Occurs at a different day
self.activity_moment.local_start_date = timezone.datetime(2020, 8, 15, 14, 0, 0, tzinfo=timezone.utc)
self.activity_moment.local_start_date = datetime(2020, 8, 15, 14, 0, 0, tzinfo=timezone.utc)
self.activity_moment.save()
response = self.build_get_response(iso_dt=recurrence_id.isoformat())
self.assertContains(response, "Replacement for the same activity on", status_code=200)

# Occurson the same day (earlier)
self.activity_moment.local_start_date = recurrence_id - timezone.timedelta(hours=1)
self.activity_moment.local_start_date = recurrence_id - timedelta(hours=1)
self.activity_moment.save()
response = self.build_get_response(iso_dt=recurrence_id.isoformat())
self.assertContains(response, "than normal!", status_code=200)
self.assertContains(response, "earlier")

# Occurson the same day (later)
self.activity_moment.local_start_date = recurrence_id + timezone.timedelta(hours=1)
self.activity_moment.local_start_date = recurrence_id + timedelta(hours=1)
self.activity_moment.save()
response = self.build_get_response(iso_dt=recurrence_id.isoformat())
self.assertContains(response, "than normal!", status_code=200)
Expand Down Expand Up @@ -760,26 +755,25 @@ class CreateSlotViewTest(TestActivityViewMixin, TestCase):
@suppress_warnings
@patch("django.utils.timezone.now", side_effect=mock_now())
def test_get_slots_invalid_dates(self, mock_tz):
# No occurence at the given date
# No occurrence at the given date
response = self.build_get_response(iso_dt="2020-08-17T14:00:00+00:00")
self.assertEqual(response.status_code, 404)

# Occureance is of a different activity
# Occurrence is of a different activity
response = self.build_get_response(activity_id=1)
self.assertEqual(response.status_code, 404)

@patch("django.utils.timezone.now", side_effect=mock_now())
def test_redirect_on_base_invalidness(self, mock_tz):
# Sign-ups are not open, so one can not create a slot
response = self.build_get_response(iso_dt="2020-08-26T14:00:00+00:00", follow=True)
# self.assertEqual(response.status_code, 302)
self.assertRedirects(
response,
reverse(
"activity_calendar:activity_slots_on_day",
kwargs={
"activity_id": 2,
"recurrence_id": datetime.datetime.fromisoformat("2020-08-26T14:00:00+00:00"),
"recurrence_id": datetime.fromisoformat("2020-08-26T14:00:00+00:00"),
},
),
)
Expand All @@ -793,7 +787,7 @@ def test_redirect_on_base_invalidness(self, mock_tz):
"activity_calendar:activity_slots_on_day",
kwargs={
"activity_id": 1,
"recurrence_id": datetime.datetime.fromisoformat("2020-08-14T19:00:00+00:00"),
"recurrence_id": datetime.fromisoformat("2020-08-14T19:00:00+00:00"),
},
),
)
Expand Down Expand Up @@ -853,7 +847,7 @@ def test_successful_post(self, mock_tz):
"activity_calendar:activity_slots_on_day",
kwargs={
"activity_id": 2,
"recurrence_id": datetime.datetime.fromisoformat("2020-08-12T14:00:00+00:00"),
"recurrence_id": datetime.fromisoformat("2020-08-12T14:00:00+00:00"),
},
),
)
Expand Down Expand Up @@ -886,7 +880,7 @@ def test_creation_none_denied(self, mock_tz):
"activity_calendar:activity_slots_on_day",
kwargs={
"activity_id": 2,
"recurrence_id": datetime.datetime.fromisoformat("2020-08-12T14:00:00+00:00"),
"recurrence_id": datetime.fromisoformat("2020-08-12T14:00:00+00:00"),
},
),
)
Expand Down Expand Up @@ -949,7 +943,7 @@ def test_successful_post(self):
"activity_calendar:activity_slots_on_day",
kwargs={
"activity_id": self.default_activity_id,
"recurrence_id": datetime.datetime.fromisoformat(self.default_iso_dt),
"recurrence_id": datetime.fromisoformat(self.default_iso_dt),
},
),
)
Expand Down Expand Up @@ -1004,7 +998,7 @@ def test_already_cancelled_get_page(self):
def test_successful_post(self):
"""Tests that a successful post is processed correctly"""
self.client.force_login(User.objects.get(is_superuser=True))
local_datetime = datetime.datetime.fromisoformat(self.default_iso_dt)
local_datetime = datetime.fromisoformat(self.default_iso_dt)

response = self.build_post_response(
{
Expand All @@ -1020,7 +1014,7 @@ def test_successful_post(self):
"activity_calendar:activity_slots_on_day",
kwargs={
"activity_id": self.default_activity_id,
"recurrence_id": datetime.datetime.fromisoformat(self.default_iso_dt),
"recurrence_id": datetime.fromisoformat(self.default_iso_dt),
},
),
)
Expand Down
8 changes: 3 additions & 5 deletions activity_calendar/util.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
from django.utils.timezone import now, localtime, get_current_timezone
import icalendar
import datetime

import zoneinfo

import zoneinfo._zoneinfo

from django.utils.timezone import utc
from django.utils.timezone import localtime, get_current_timezone

utc = datetime.timezone.utc


def set_time_for_RDATE_EXDATE(dates: list[datetime.datetime], time: datetime):
Expand Down
Loading

0 comments on commit 0740f20

Please sign in to comment.