from typing import Any from django.contrib.auth.mixins import PermissionRequiredMixin from django.db.models import Q, QuerySet from django.http.request import HttpRequest from django.urls import reverse from django.utils import timezone from django.views.generic.detail import SingleObjectMixin from elections.typing import AuthenticatedRequest from .models import Election, Option, Question class AdminOnlyMixin(PermissionRequiredMixin): """Restreint l'accès aux admins""" request: AuthenticatedRequest permission_required = "elections.election_admin" class SelectElectionMixin: """Sélectionne automatiquement les foreignkeys voulues""" model: type def get_queryset(self) -> QuerySet: qs = super().get_queryset() # pyright: ignore if self.model is Question: return qs.select_related("election") elif self.model is Option: return qs.select_related("question__election") return qs class RestrictAccessMixin(SelectElectionMixin): """Permet de restreindre l'accès à des élections/questions/options""" f_prefixes = { Election: "", Question: "election__", Option: "question__election__", } def get_f_prefix(self) -> str: return self.f_prefixes.get(self.model, "") def get_filters(self) -> dict[str, Any]: return {} def get_queryset(self) -> QuerySet: qs = super().get_queryset() if self.model in self.f_prefixes: return qs.filter(**self.get_filters()) # On ne sait pas ce qu'on manipule donc on ne renvoie rien return qs.none() class OpenElectionOnlyMixin(RestrictAccessMixin): """N'autorise la vue que lorsque l'élection est ouverte""" def get_filters(self) -> dict[str, Any]: f_prefix = self.get_f_prefix() # On ne peut modifier que les élections qui n'ont pas commencé, et # accessoirement qui ne sont pas dépouillées ou archivées # TODO: décider si on choisit pas de juste garder les dates d'ouverture filters = super().get_filters() filters[f_prefix + "start_date__lt"] = timezone.now() filters[f_prefix + "end_date__gt"] = timezone.now() filters[f_prefix + "visible"] = True filters[f_prefix + "tallied"] = False filters[f_prefix + "archived"] = False return filters class CreatorOnlyMixin(AdminOnlyMixin, RestrictAccessMixin, SingleObjectMixin): """Restreint l'accès au créateurice de l'élection""" def get_next_url(self): return reverse("kadenios") def get_filters(self) -> dict[str, Any]: filters = super().get_filters() # TODO: change the way we collect the user according to the model used filters[self.get_f_prefix() + "created_by"] = self.request.user return filters class CreatorOnlyEditMixin(CreatorOnlyMixin): """Permet au créateurice de modifier l'élection implicitement""" def get_filters(self) -> dict[str, Any]: # On ne peut modifier que les élections qui n'ont pas commencé filters = super().get_filters() filters[self.get_f_prefix() + "start_date__gt"] = timezone.now() return filters class ClosedElectionMixin(CreatorOnlyMixin): """Permet d'agir sur une élection terminée""" def get_filters(self) -> dict[str, Any]: f_prefix = self.get_f_prefix() # L'élection doit être terminée et non archivée filters = super().get_filters() filters[f_prefix + "end_date__lt"] = timezone.now() filters[f_prefix + "archived"] = False return filters class NotArchivedMixin: """ Permet de ne garder que les élections non archivées, et visibles ou dont on est l'admin """ request: HttpRequest def get_queryset(self) -> QuerySet: user = self.request.user qs = super().get_queryset() # pyright: ignore if user.is_authenticated: return qs.filter(Q(archived=False, visible=True) | Q(created_by=user)) return qs.filter(archived=False, visible=True)