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, RedirectView, UpdateView from django.views.generic.detail import SingleObjectMixin from .forms import OptionFormSet from .models import Election, Option, Question # TODO: access control *everywhere* class ElectionCreateView(SuccessMessageMixin, CreateView): model = Election fields = ["name", "description", "start_date", "end_date"] 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( str(form.instance.start_date.year) + "_" + 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(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 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 déjà comptée return super().get_queryset().filter(tallied=False, archived=False) class ElectionTallyView(SuccessMessageMixin, SingleObjectMixin, RedirectView): model = Election pattern_name = "election.admin" def get_queryset(self): return super().get_queryset().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(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(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) .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}" )