Template changes and vote restriction for open elections
This commit is contained in:
parent
5f1fed991b
commit
9faa0b3354
6 changed files with 134 additions and 61 deletions
|
@ -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"""
|
||||||
|
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -117,16 +117,16 @@
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</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>
|
</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>
|
||||||
|
|
|
@ -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 %}
|
||||||
|
<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 %}
|
||||||
|
<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 %}
|
||||||
|
<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 %}">
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue