kadenios/elections/views.py

206 lines
6.7 KiB
Python

from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.db.models import Count, Prefetch
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils import timezone
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from django.views.generic import (
CreateView,
DetailView,
ListView,
RedirectView,
UpdateView,
)
from django.views.generic.detail import SingleObjectMixin
from .forms import ElectionCreateForm, OptionFormSet
from .mixins import CreatorOnlyMixin
from .models import Election, Option, Question
# TODO: access control *everywhere*
# #############################################################################
# Administration Views
# #############################################################################
class ElectionCreateView(SuccessMessageMixin, CreateView):
model = Election
form_class = ElectionCreateForm
template_name = "elections/election_create.html"
success_message = _("Élection créée avec succès !")
def get_success_url(self):
return reverse("election.admin", args=[self.object.pk])
def form_valid(self, form):
# We need to add the short name and the creator od the election
form.instance.short_name = slugify(
form.instance.start_date.strftime("%Y-%m-%d") + "_" + form.instance.name
)[:50]
# TODO: Change this if we modify the user model
form.instance.created_by = self.request.user
return super().form_valid(form)
# TODO : only the creator can edit the election and view the admin panel
class ElectionAdminView(CreatorOnlyMixin, DetailView):
model = Election
template_name = "elections/election_admin.html"
def get_context_data(self, **kwargs):
kwargs.update({"current_time": timezone.now()})
return super().get_context_data(**kwargs)
def get_queryset(self):
return super().get_queryset().prefetch_related("questions__options")
class ElectionListView(CreatorOnlyMixin, ListView):
model = Election
template_name = "elections/election_list.html"
class ElectionUpdateView(SuccessMessageMixin, UpdateView):
model = Election
fields = ["name", "description", "start_date", "end_date"]
success_message = _("Élection modifiée avec succès !")
template_name = "elections/election_update.html"
def get_success_url(self):
return reverse("election.admin", args=[self.object.pk])
def get_queryset(self):
# On ne peut plus modifier une élection qui a déjà commencé
return super().get_queryset().filter(start_date__gt=timezone.now())
class ElectionTallyView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
model = Election
pattern_name = "election.admin"
def get_queryset(self):
return (
super()
.get_queryset()
.filter(end_date__lt=timezone.now())
.prefetch_related("questions__options")
)
def get(self, request, *args, **kwargs):
election = self.get_object()
options, questions = [], []
for q in election.questions.all():
max_votes = 0
for o in q.options.all():
o.nb_votes = o.voters.count()
max_votes = max(max_votes, o.nb_votes)
options.append(o)
q.max_votes = max_votes
questions.append(q)
Option.objects.bulk_update(options, ["nb_votes"])
Question.objects.bulk_update(questions, ["max_votes"])
election.tallied = True
election.save()
messages.success(request, _("Élection dépouillée avec succès !"))
return super().get(request, *args, **kwargs)
class ElectionChangePublicationView(
SuccessMessageMixin, SingleObjectMixin, RedirectView
):
model = Election
pattern_name = "election.admin"
def get_queryset(self):
return super().get_queryset().filter(end_date__lt=timezone.now())
def get(self, request, *args, **kwargs):
election = self.get_object()
election.results_public = not election.results_public
election.save()
messages.success(
request,
_("Élection publiée avec succès !")
if election.results_public
else _("Élection dépubliée avec succès !"),
)
return super().get(request, *args, **kwargs)
class ElectionArchiveView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
model = Election
pattern_name = "election.admin"
def get_queryset(self):
return super().get_queryset().filter(end_date__lt=timezone.now())
def get(self, request, *args, **kwargs):
election = self.get_object()
election.archived = True
election.save()
messages.success(request, _("Élection archivée avec succès !"))
return super().get(request, *args, **kwargs)
class ElectionView(DetailView):
model = Election
template_name = "elections/election.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.object.tallied:
options_qs = Option.objects.annotate(nb_votes=Count("voters"))
questions = self.election.question.prefetch_related(
Prefetch("options", queryset=options_qs)
)
context["questions"] = questions
return context
def get_queryset(self):
return super().get_queryset().filter(archived=False)
class VoteView(SuccessMessageMixin, DetailView):
model = Question
template_name = "elections/vote.html"
def get_queryset(self):
# On ne peut voter que si l'élection n'a pas été comptée
return (
super()
.get_queryset()
.filter(
election__tallied=False,
election__archived=False,
election__start_date__lt=timezone.now(),
election__end_date__gt=timezone.now(),
)
.select_related("election")
)
def get(self, request, *args, **kwargs):
self.object = self.get_object()
vote_form = OptionFormSet(instance=self.object)
return self.render_to_response(self.get_context_data(formset=vote_form))
def post(self, request, *args, **kwargs):
self.object = self.get_object()
vote_form = OptionFormSet(self.request.POST, instance=self.object)
# We record the cast votes
for v in vote_form:
v.record_vote(self.request.user)
messages.success(self.request, _("Votre vote a bien été enregistré !"))
return HttpResponseRedirect(
reverse("election.view", args=[self.object.election.pk])
+ f"#q_{self.object.pk}"
)