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