Template changes and vote restriction for open elections

This commit is contained in:
Tom Hubrecht 2020-12-20 10:35:18 +01:00
parent 5f1fed991b
commit 9faa0b3354
6 changed files with 134 additions and 61 deletions

View file

@ -35,6 +35,22 @@ class RestrictAccessMixin(SelectElectionMixin):
return qs.none() return qs.none()
class OpenElectionOnly(RestrictAccessMixin):
"""N'autorise la vue que lorsque l'élection est ouverte"""
def get_filters(self):
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 + "tallied"] = False
filters[f_prefix + "archived"] = False
return filters
class CreatorOnlyMixin(RestrictAccessMixin): class CreatorOnlyMixin(RestrictAccessMixin):
"""Restreint l'accès au créateurice de l'élection""" """Restreint l'accès au créateurice de l'élection"""

View file

@ -4,23 +4,74 @@
{% block content %} {% block content %}
<div class="level">
<div class="level-left">
{# Titre de l'élection #}
<div class="level-item">
<h1 class="title">{{ election.name }}</h1> <h1 class="title">{{ election.name }}</h1>
</div>
{# Dates d'ouverture de l'élection #}
<div class="level-item">
<span class="tag is-medium is-primary">{{ election.start_date|date:"d/m/Y H:i" }}</span>
<span class="icon">
<i class="fas fa-long-arrow-alt-right"></i>
</span>
<span class="tag is-medium is-primary">{{ election.end_date|date:"d/m/Y H:i" }}</span>
</div>
</div>
{% if election.start_date < current_time %}
<div class="level-right">
<div class="level-item">
<span class="tag is-medium is-outlined is-light is-primary">
{% if election.end_date < current_time %}
{% trans "Élection terminée" %}
{% else %}
{% trans "Élection en cours" %}
{% endif %}
</span>
</div>
</div>
{% endif %}
</div>
<hr> <hr>
<div class="message is-info"> {# Description de l'élection #}
<p class="message-body">{{ election.description }}</p> <div class="message is-primary">
<div class="message-body">{{ election.description|linebreaksbr }}</div>
</div> </div>
<div class="container"> {# Liste des questions #}
{% for q in questions %} {% for q in election.questions.all %}
<div class="box"> <div class="panel" id="q_{{ q.pk }}">
<h3 class="subtitle">{{ q.text }}</h3> <div class="panel-heading is-size-6">
{% if election.start_date < current_time and election.end_date > current_time %}
<a class="tag is-small is-outlined is-light is-danger" href="{% url 'election.vote' q.pk %}">
<span class="icon">
<i class="fas fa-vote-yea"></i>
</span>
<span>{% trans "Voter" %}</span>
</a>
{% endif %}
<span class="ml-2">{{ q.text }}</span>
</div>
{# Liste des options possibles #}
{% for o in q.options.all %} {% for o in q.options.all %}
{{ o.text }}<br> <div class="panel-block">
{% endfor %} {% if election.tallied and election.results_public %}
<a class="button is-outlined is-info is-light">{% trans "Voter" %}</a> <span class="tag {% if o.nb_votes == q.max_votes %}is-success{% else %}is-primary{% endif %}">
<span class="icon">
<i class="fas fa-vote-yea"></i>
</span>
<span>{{ o.nb_votes }}</span>
</span>
{% endif %}
<span class="ml-2">{{ o.text }}</span>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% endfor %}
{% endblock %} {% endblock %}

View file

@ -117,16 +117,16 @@
<i class="fas fa-edit"></i> <i class="fas fa-edit"></i>
</span> </span>
</a> </a>
</div>&nbsp;&nbsp; </div>
{% elif election.tallied %} {% elif election.tallied %}
<span class="tag {% if o.nb_votes == q.max_votes %}is-success{% else %}is-primary{% endif %}"> <span class="tag {% if o.nb_votes == q.max_votes %}is-success{% else %}is-primary{% endif %}">
<span class="icon"> <span class="icon">
<i class="fas fa-vote-yea"></i> <i class="fas fa-vote-yea"></i>
</span> </span>
<span>{{ o.nb_votes }}</span> <span>{{ o.nb_votes }}</span>
</span>&nbsp;&nbsp; </span>
{% endif %} {% endif %}
{{ o.text }} <span class="ml-2">{{ o.text }}</span>
</div> </div>
{% endfor %} {% endfor %}
@ -160,13 +160,13 @@
<div class="field has-addons"> <div class="field has-addons">
<div class="control has-icons-left is-expanded"> <div class="control has-icons-left is-expanded">
<input class="input" type="text" name="text" id="id_text" placeholder="{% trans "Rajouter une question" %}"> <input class="input is-primary" type="text" name="text" id="id_text" placeholder="{% trans "Rajouter une question" %}">
<span class="icon is-left"> <span class="icon is-left">
<i class="fas fa-question"></i> <i class="fas fa-question"></i>
</span> </span>
</div> </div>
<div class="control"> <div class="control">
<button class="button">{% trans "Valider" %}</button> <button class="button is-primary is-outlined">{% trans "Valider" %}</button>
</div> </div>
</div> </div>
</form> </form>

View file

@ -35,18 +35,15 @@
<span class="tag is-primary is-light">{{ e.end_date|date:"d/m/Y H:i" }}</span> <span class="tag is-primary is-light">{{ e.end_date|date:"d/m/Y H:i" }}</span>
{% if e.tallied %} {% if e.tallied %}
&nbsp; <span class="tag is-success is-light ml-3">{% trans "Élection dépouillée" %}</span>
<span class="tag is-success is-light">{% trans "Élection dépouillée" %}</span>
{% endif %} {% endif %}
{% if e.results_public %} {% if e.results_public %}
&nbsp; <span class="tag is-info is-light ml-3">{% trans "Élection publiée" %}</span>
<span class="tag is-info is-light">{% trans "Élection publiée" %}</span>
{% endif %} {% endif %}
{% if e.archived %} {% if e.archived %}
&nbsp; <span class="tag is-danger is-light ml-3">{% trans "Élection archivée" %}</span>
<span class="tag is-danger is-light">{% trans "Élection archivée" %}</span>
{% endif %} {% endif %}
</div> </div>
<a class="has-text-primary-light" href="{% url 'election.admin' e.pk %}"> <a class="has-text-primary-light" href="{% url 'election.admin' e.pk %}">

View file

@ -4,20 +4,36 @@
{% block content %} {% block content %}
<h1 class="title">{% trans "Vote pour la question :" %} {{ question }}</h1> <h1 class="title">{% trans "Vote pour la question :" %} {{ question.text }}</h1>
<hr>
<div class="container"> <div class="columns is-centered">
<div class="column is-two-thirds">
<form action="" method="post"> <form action="" method="post">
{% csrf_token %} {% csrf_token %}
{% include "elections/forms/formset.html" %} {% include "elections/forms/formset.html" %}
<div class="field"> <div class="field is-grouped is-centered">
<p class="control"> <div class="control is-expanded">
<input class="button is-fullwidth" type="submit" value="Enregistrer"> <button class="button is-fullwidth is-outlined is-primary is-light">
</p> <span class="icon is-small">
<i class="fas fa-check"></i>
</span>
<span>{% trans "Enregistrer" %}</span>
</button>
</div>
<div class="control">
<a class="button is-primary" href="{% url 'election.view' question.election.pk %}">
<span class="icon is-small">
<i class="fas fa-undo-alt"></i>
</span>
<span>{% trans "Retour" %}</span>
</a>
</div>
</div> </div>
</form> </form>
</div> </div>
</div>
{% endblock %} {% endblock %}

View file

@ -1,6 +1,7 @@
from django.contrib import messages from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.db.models import Count, Prefetch
# from django.db.models import Count, Prefetch
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
@ -15,7 +16,7 @@ from django.views.generic import (
) )
from .forms import ElectionForm, OptionForm, OptionFormSet, QuestionForm from .forms import ElectionForm, OptionForm, OptionFormSet, QuestionForm
from .mixins import CreatorOnlyEditMixin, CreatorOnlyMixin from .mixins import CreatorOnlyEditMixin, CreatorOnlyMixin, OpenElectionOnly
from .models import Election, Option, Question from .models import Election, Option, Question
# TODO: access control *everywhere* # TODO: access control *everywhere*
@ -265,38 +266,30 @@ class ElectionView(DetailView):
template_name = "elections/election.html" template_name = "elections/election.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) kwargs.update({"current_time": timezone.now()})
if self.object.tallied: return super().get_context_data(**kwargs)
options_qs = Option.objects.annotate(nb_votes=Count("voters")) # context = super().get_context_data(**kwargs)
questions = self.election.question.prefetch_related( # if self.object.tallied:
Prefetch("options", queryset=options_qs) # options_qs = Option.objects.annotate(nb_votes=Count("voters"))
) # questions = self.election.question.prefetch_related(
context["questions"] = questions # Prefetch("options", queryset=options_qs)
# )
return context # context["questions"] = questions
# return context
def get_queryset(self): 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 ( return (
super() super()
.get_queryset() .get_queryset()
.filter( .filter(archived=False)
election__tallied=False, .prefetch_related("questions__options")
election__archived=False,
election__start_date__lt=timezone.now(),
election__end_date__gt=timezone.now(),
)
.select_related("election")
) )
class VoteView(OpenElectionOnly, SuccessMessageMixin, DetailView):
model = Question
template_name = "elections/vote.html"
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
vote_form = OptionFormSet(instance=self.object) vote_form = OptionFormSet(instance=self.object)