Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Still in Progress] Offer long-term event #3355

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions integreat_cms/cms/forms/events/event_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

import logging
import zoneinfo
from datetime import datetime, time, timedelta
from datetime import datetime, time
from typing import TYPE_CHECKING

from django import forms
from django.conf import settings
from django.utils.translation import gettext_lazy as _

from ...constants import status
Expand Down Expand Up @@ -48,6 +47,7 @@ class EventForm(CustomModelForm):
widget=forms.DateInput(format="%Y-%m-%d", attrs={"type": "date"}),
)
end_date = forms.DateField(
required=False,
label=_("end date"),
widget=forms.DateInput(format="%Y-%m-%d", attrs={"type": "date"}),
)
Expand All @@ -61,6 +61,9 @@ class EventForm(CustomModelForm):
required=False,
widget=forms.TimeInput(format="%H:%M", attrs={"type": "time"}),
)
is_long_term = forms.BooleanField(
required=False,
)

class Meta:
"""
Expand All @@ -78,6 +81,7 @@ class Meta:
"location",
"external_calendar",
"external_event_id",
"only_weekdays",
]
#: The widgets which are used in this form
widgets = {
Expand All @@ -104,6 +108,7 @@ def __init__(self, **kwargs: Any) -> None:
# since they will be set later in the clean method
self.fields["start"].required = False
self.fields["end"].required = False
self.fields["is_long_term"].initial = False
if self.instance.id:
# Initialize non-model fields based on event
self.fields["start_date"].initial = self.instance.start_local.date()
Expand All @@ -119,6 +124,8 @@ def __init__(self, **kwargs: Any) -> None:
else None
)
self.fields["external_event_id"].initial = self.instance.external_event_id
if not self.instance.start_local.date() == self.instance.end_local.date():
self.fields["is_long_term"].initial = True

def clean(self) -> dict[str, Any]:
"""
Expand Down Expand Up @@ -160,6 +167,9 @@ def clean(self) -> dict[str, Any]:
] == time.max.replace(second=0, microsecond=0):
self.data["is_all_day"] = True

if not cleaned_data.get("is_long_term"):
cleaned_data["end_date"] = cleaned_data.get("start_date")

if (start_date := cleaned_data.get("start_date")) and (
end_date := cleaned_data.get("end_date")
):
Expand Down Expand Up @@ -190,16 +200,7 @@ def clean(self) -> dict[str, Any]:
code="invalid",
),
)
elif end_date - start_date > timedelta(settings.MAX_EVENT_DURATION - 1):
self.add_error(
"end_date",
forms.ValidationError(
_(
"The maximum duration for events is {} days. Consider using recurring events if the event is not continuous."
).format(settings.MAX_EVENT_DURATION),
code="invalid",
),
)

# If everything looks good until now, combine the dates and times into timezone-aware datetimes
if not self.errors:
tzinfo = zoneinfo.ZoneInfo(self.instance.timezone)
Expand Down
48 changes: 12 additions & 36 deletions integreat_cms/cms/forms/events/recurrence_rule_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ class RecurrenceRuleForm(CustomModelForm):
Form for creating and modifying event recurrence rule objects
"""

has_recurrence_end_date = forms.BooleanField(
required=False, label=_("Recurrence ends")
)

class Meta:
"""
This class contains additional meta configuration of the form class, see the :class:`django.forms.ModelForm`
Expand Down Expand Up @@ -63,12 +59,6 @@ def __init__(self, **kwargs: Any) -> None:
# Instantiate CustomModelForm
super().__init__(**kwargs)

if self.instance.id:
# Initialize BooleanField based on RecurrenceRule properties
self.fields["has_recurrence_end_date"].initial = bool(
self.instance.recurrence_end_date
)

def clean(self) -> dict[str, Any]:
"""
Validate form fields which depend on each other, see :meth:`django.forms.Form.clean`
Expand Down Expand Up @@ -109,32 +99,18 @@ def clean(self) -> dict[str, Any]:
),
)

if cleaned_data.get("has_recurrence_end_date"):
if not cleaned_data.get("recurrence_end_date"):
self.add_error(
"recurrence_end_date",
forms.ValidationError(
_(
"If the recurrence ends, the recurrence end date is required"
),
code="required",
),
)
elif (
self.event_start_date
and cleaned_data.get("recurrence_end_date") <= self.event_start_date
):
self.add_error(
"recurrence_end_date",
forms.ValidationError(
_(
"The recurrence end date has to be after the event's start date"
),
code="invalid",
),
)
else:
cleaned_data["recurrence_end_date"] = None
if (
cleaned_data.get("recurrence_end_date")
and self.event_start_date
and cleaned_data.get("recurrence_end_date") <= self.event_start_date
):
self.add_error(
"recurrence_end_date",
forms.ValidationError(
_("The recurrence end date has to be after the event's start date"),
code="invalid",
),
)

logger.debug(
"RecurrenceRuleForm validated [2] with cleaned data %r", cleaned_data
Expand Down
56 changes: 56 additions & 0 deletions integreat_cms/cms/migrations/0113_alter_event_end.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from django.db import migrations, models

if TYPE_CHECKING:
from django.apps.registry import Apps
from django.db.backends.base.schema import BaseDatabaseSchemaEditor


# pylint: disable=unused-argument
def remove_end_date_from_existing_events(
apps: Apps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
"""
Set the end date of existing events equal to start date, if they have recurrence rule.
"""
Event = apps.get_model("cms", "Event")
events = Event.objects.all()
for event in events:
if (
not event.start.date == event.end.date
and event.recurrence_rule
and not event.recurrence_rule.recurrence_end_date
):
event.recurrence_rule.recurrence_end_date = event.end.date
event.end = event.start


class Migration(migrations.Migration):
"""
Offer long-term events
"""

dependencies = [
("cms", "0112_sort_contacts_by_POI_and_name"),
]

operations = [
migrations.RunPython(remove_end_date_from_existing_events),
migrations.AlterField(
model_name="event",
name="end",
field=models.DateTimeField(blank=True, verbose_name="end"),
),
migrations.AddField(
model_name="event",
name="only_weekdays",
field=models.BooleanField(
default=False,
help_text="Tick if this event takes place only on weekdays",
verbose_name="Only weekdays",
),
),
]
10 changes: 9 additions & 1 deletion integreat_cms/cms/models/events/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,15 @@ class Event(AbstractContentModel):
verbose_name=_("location"),
)
start = models.DateTimeField(verbose_name=_("start"))
end = models.DateTimeField(verbose_name=_("end"))
end = models.DateTimeField(
verbose_name=_("end"),
blank=True,
)
only_weekdays = models.BooleanField(
default=False,
verbose_name=_("Only weekdays"),
help_text=_("Tick if this event takes place only on weekdays"),
)
#: If the event is recurring, the recurrence rule contains all necessary information on the frequency, interval etc.
#: which is needed to calculate the single instances of a recurring event
recurrence_rule = models.OneToOneField(
Expand Down
22 changes: 22 additions & 0 deletions integreat_cms/cms/templates/events/_event_duration_tab.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{% load i18n %}
{% load content_filters %}
<ul id="tab-wrapper" class="flex flex-wrap items-end px-4">
<li class="mr-2 -mb-[2px] z-10" id="one-time-and-recurring-tab">
<div class="font-bold {% if not event_form.is_long_term.value %}bg-white{% else %}bg-water-500{% endif %}">
<div class="border-b-2 border-white">
<div class="py-[calc(0.75rem+2px)] px-4 border-l border-t border-r {% if event_form.is_long_term.value %}border-b{% endif %} border-blue-500 rounded-t text-blue-500 cursor-default">
<span class="flex-grow">{% translate "One-time or recurrent" %}</span>
</div>
</div>
</div>
</li>
<li class="mr-2 -mb-[2px] z-10" id="long-term-tab">
<div class="font-bold {% if event_form.is_long_term.value %}bg-white{% else %}bg-water-500{% endif %}">
<div class="border-b-2 border-white">
<div class="py-[calc(0.75rem+2px)] px-4 border-l border-t border-r {% if not event_form.is_long_term.value %}border-b{% endif %} border-blue-500 rounded-t text-blue-500 cursor-default">
<span class="flex-grow">{% translate "Long-term" %}</span>
</div>
</div>
</div>
</li>
</ul>
Loading
Loading