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()
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):
"""Restreint l'accès au créateurice de l'élection"""

View file

@ -4,23 +4,74 @@
{% block content %}
<h1 class="title">{{ election.name }}</h1>
<div class="level">
<div class="level-left">
{# Titre de l'élection #}
<div class="level-item">
<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>
<div class="message is-info">
<p class="message-body">{{ election.description }}</p>
{# Description de l'élection #}
<div class="message is-primary">
<div class="message-body">{{ election.description|linebreaksbr }}</div>
</div>
<div class="container">
{% for q in questions %}
<div class="box">
<h3 class="subtitle">{{ q.text }}</h3>
{# Liste des questions #}
{% for q in election.questions.all %}
<div class="panel" id="q_{{ q.pk }}">
<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 %}
{{ o.text }}<br>
{% endfor %}
<a class="button is-outlined is-info is-light">{% trans "Voter" %}</a>
<div class="panel-block">
{% if election.tallied and election.results_public %}
<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>
{% endfor %}
</div>
{% endfor %}
{% endblock %}

View file

@ -117,16 +117,16 @@
<i class="fas fa-edit"></i>
</span>
</a>
</div>&nbsp;&nbsp;
</div>
{% elif election.tallied %}
<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>&nbsp;&nbsp;
</span>
{% endif %}
{{ o.text }}
<span class="ml-2">{{ o.text }}</span>
</div>
{% endfor %}
@ -160,13 +160,13 @@
<div class="field has-addons">
<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">
<i class="fas fa-question"></i>
</span>
</div>
<div class="control">
<button class="button">{% trans "Valider" %}</button>
<button class="button is-primary is-outlined">{% trans "Valider" %}</button>
</div>
</div>
</form>

View file

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

View file

@ -4,20 +4,36 @@
{% 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">
{% csrf_token %}
{% include "elections/forms/formset.html" %}
<div class="field">
<p class="control">
<input class="button is-fullwidth" type="submit" value="Enregistrer">
</p>
<div class="field is-grouped is-centered">
<div class="control is-expanded">
<button class="button is-fullwidth is-outlined is-primary is-light">
<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>
</form>
</div>
</div>
{% endblock %}

View file

@ -1,6 +1,7 @@
from django.contrib import messages
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.urls import reverse
from django.utils import timezone
@ -15,7 +16,7 @@ from django.views.generic import (
)
from .forms import ElectionForm, OptionForm, OptionFormSet, QuestionForm
from .mixins import CreatorOnlyEditMixin, CreatorOnlyMixin
from .mixins import CreatorOnlyEditMixin, CreatorOnlyMixin, OpenElectionOnly
from .models import Election, Option, Question
# TODO: access control *everywhere*
@ -265,38 +266,30 @@ class ElectionView(DetailView):
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
kwargs.update({"current_time": timezone.now()})
return super().get_context_data(**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")
.filter(archived=False)
.prefetch_related("questions__options")
)
class VoteView(OpenElectionOnly, SuccessMessageMixin, DetailView):
model = Question
template_name = "elections/vote.html"
def get(self, request, *args, **kwargs):
self.object = self.get_object()
vote_form = OptionFormSet(instance=self.object)