Sith/com/forms.py
2025-01-10 22:08:24 +01:00

194 lines
6.6 KiB
Python

from datetime import date
from dateutil.relativedelta import relativedelta
from django import forms
from django.db.models import Exists, OuterRef
from django.forms import CheckboxInput
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from club.models import Club
from club.widgets.select import AutoCompleteSelectClub
from com.models import News, NewsDate, Poster
from core.models import User
from core.utils import get_end_of_semester
from core.views.forms import SelectDateTime
from core.views.widgets.markdown import MarkdownInput
class PosterForm(forms.ModelForm):
class Meta:
model = Poster
fields = [
"name",
"file",
"club",
"screens",
"date_begin",
"date_end",
"display_time",
]
widgets = {"screens": forms.CheckboxSelectMultiple}
help_texts = {"file": _("Format: 16:9 | Resolution: 1920x1080")}
date_begin = forms.DateTimeField(
label=_("Start date"),
widget=SelectDateTime,
required=True,
initial=timezone.now().strftime("%Y-%m-%d %H:%M:%S"),
)
date_end = forms.DateTimeField(
label=_("End date"), widget=SelectDateTime, required=False
)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user", None)
super().__init__(*args, **kwargs)
if self.user and not self.user.is_com_admin:
self.fields["club"].queryset = Club.objects.filter(
id__in=self.user.clubs_with_rights
)
self.fields.pop("display_time")
class NewsDateForm(forms.ModelForm):
"""Form to select the dates of an event."""
required_css_class = "required"
class Meta:
model = NewsDate
fields = ["start_date", "end_date"]
widgets = {"start_date": SelectDateTime, "end_date": SelectDateTime}
is_weekly = forms.BooleanField(
label=_("Weekly event"),
help_text=_("Weekly events will occur each week for a specified timespan."),
widget=CheckboxInput(attrs={"class": "switch"}),
initial=False,
required=False,
)
occurrence_choices = [
*[(str(i), _("%d times") % i) for i in range(2, 7)],
("SEMESTER_END", _("Until the end of the semester")),
]
occurrences = forms.ChoiceField(
label=_("Occurrences"),
help_text=_("How much times should the event occur (including the first one)"),
choices=occurrence_choices,
initial="SEMESTER_END",
required=False,
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.label_suffix = ""
@classmethod
def get_occurrences(cls, number: int) -> tuple[str, str] | None:
"""Find the occurrence choice corresponding to numeric number of occurrences."""
if number < 2:
# If only 0 or 1 date, there cannot be weekly events
return None
# occurrences have all a numeric value, except "SEMESTER_END"
str_num = str(number)
occurrences = next((c for c in cls.occurrence_choices if c[0] == str_num), None)
if occurrences:
return occurrences
return next((c for c in cls.occurrence_choices if c[0] == "SEMESTER_END"), None)
def save(self, commit: bool = True, *, news: News): # noqa FBT001
# the base save method contains some checks we want to run
# before doing our own logic
super().save(commit=False)
# delete existing dates before creating new ones
news.dates.all().delete()
if not self.cleaned_data.get("is_weekly"):
self.instance.news = news
return super().save(commit=commit)
dates: list[NewsDate] = [self.instance]
occurrences = self.cleaned_data.get("occurrences")
start = self.instance.start_date
end = self.instance.end_date
if occurrences[0].isdigit():
nb_occurrences = int(occurrences[0])
else: # to the end of the semester
start_date = date(start.year, start.month, start.day)
nb_occurrences = (get_end_of_semester(start_date) - start_date).days // 7
dates.extend(
[
NewsDate(
start_date=start + relativedelta(weeks=i),
end_date=end + relativedelta(weeks=i),
)
for i in range(1, nb_occurrences)
]
)
for d in dates:
d.news = news
if not commit:
return dates
return NewsDate.objects.bulk_create(dates)
class NewsForm(forms.ModelForm):
"""Form to create or edit news."""
error_css_class = "error"
required_css_class = "required"
class Meta:
model = News
fields = ["title", "club", "summary", "content"]
widgets = {
"author": forms.HiddenInput,
"summary": MarkdownInput,
"content": MarkdownInput,
}
auto_moderate = forms.BooleanField(
label=_("Automoderation"),
widget=CheckboxInput(attrs={"class": "switch"}),
required=False,
)
def __init__(self, *args, author: User, date_form: NewsDateForm, **kwargs):
super().__init__(*args, **kwargs)
self.author = author
self.date_form = date_form
self.label_suffix = ""
# if the author is an admin, he/she can choose any club,
# otherwise, only clubs for which he/she is a board member can be selected
if author.is_root or author.is_com_admin:
self.fields["club"] = forms.ModelChoiceField(
queryset=Club.objects.all(), widget=AutoCompleteSelectClub
)
else:
active_memberships = author.memberships.board().ongoing()
self.fields["club"] = forms.ModelChoiceField(
queryset=Club.objects.filter(
Exists(active_memberships.filter(club=OuterRef("pk")))
)
)
def is_valid(self):
return super().is_valid() and self.date_form.is_valid()
def full_clean(self):
super().full_clean()
self.date_form.full_clean()
def save(self, commit: bool = True): # noqa FBT001
self.instance.author = self.author
if (self.author.is_com_admin or self.author.is_root) and (
self.cleaned_data.get("auto_moderate") is True
):
self.instance.is_moderated = True
self.instance.moderator = self.author
else:
self.instance.is_moderated = False
created_news = super().save(commit=commit)
self.date_form.save(commit=commit, news=created_news)
return created_news