Tally votes, cache results and allow for publication/depublication
This commit is contained in:
parent
9c6eaeef58
commit
3387186f76
6 changed files with 97 additions and 9 deletions
20
elections/migrations/0004_option_nb_votes.py
Normal file
20
elections/migrations/0004_option_nb_votes.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Generated by Django 2.2.17 on 2020-12-19 16:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("elections", "0003_auto_20201218_1954"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="option",
|
||||||
|
name="nb_votes",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
default=0, verbose_name="nombre de votes reçus"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
20
elections/migrations/0005_question_max_votes.py
Normal file
20
elections/migrations/0005_question_max_votes.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Generated by Django 2.2.17 on 2020-12-19 17:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("elections", "0004_option_nb_votes"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="question",
|
||||||
|
name="max_votes",
|
||||||
|
field=models.PositiveSmallIntegerField(
|
||||||
|
default=0, verbose_name="nombre maximal de votes reçus"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -29,6 +29,10 @@ class Question(models.Model):
|
||||||
Election, related_name="questions", on_delete=models.CASCADE
|
Election, related_name="questions", on_delete=models.CASCADE
|
||||||
)
|
)
|
||||||
text = models.TextField(_("question"), blank=False)
|
text = models.TextField(_("question"), blank=False)
|
||||||
|
# We cache the maximum number of votes for an option
|
||||||
|
max_votes = models.PositiveSmallIntegerField(
|
||||||
|
_("nombre maximal de votes reçus"), default=0
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Option(models.Model):
|
class Option(models.Model):
|
||||||
|
@ -40,3 +44,5 @@ class Option(models.Model):
|
||||||
User,
|
User,
|
||||||
related_name="votes",
|
related_name="votes",
|
||||||
)
|
)
|
||||||
|
# For now, we store the amount of votes received after the election is tallied
|
||||||
|
nb_votes = models.PositiveSmallIntegerField(_("nombre de votes reçus"), default=0)
|
||||||
|
|
|
@ -47,13 +47,17 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
{# Lien pour la publication des résultats #}
|
{# Lien pour la publication des résultats #}
|
||||||
<a class="button is-light is-outlined is-primary" href="{% url 'election.publish' election.pk %}">
|
<a class="button is-outlined is-primary" href="{% url 'election.publish' election.pk %}">
|
||||||
<span class="icon">
|
<span class="icon">
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
{% if not election.results_public %}
|
||||||
{% trans "Publier" %}
|
{% trans "Publier" %}
|
||||||
</a>
|
{% else %}
|
||||||
|
{% trans "Dépublier" %}
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
|
||||||
{# Lien pour l'archivage #}
|
{# Lien pour l'archivage #}
|
||||||
<a class="button is-light is-outlined is-primary" href="{% url 'election.archive' election.pk %}">
|
<a class="button is-light is-outlined is-primary" href="{% url 'election.archive' election.pk %}">
|
||||||
|
@ -116,8 +120,12 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% elif election.tallied %}
|
{% elif election.tallied %}
|
||||||
<span class="tag is-primary">
|
<span class="tag {% if o.nb_votes == q.max_votes %}is-success{% else %}is-primary{% endif %}">
|
||||||
</span>
|
<span class="icon">
|
||||||
|
<i class="fas fa-vote-yea"></i>
|
||||||
|
</span>
|
||||||
|
{{ o.nb_votes }}
|
||||||
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ o.text }}
|
{{ o.text }}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,9 @@ urlpatterns = [
|
||||||
path("update/<int:pk>", views.ElectionUpdateView.as_view(), name="election.update"),
|
path("update/<int:pk>", views.ElectionUpdateView.as_view(), name="election.update"),
|
||||||
path("tally/<int:pk>", views.ElectionTallyView.as_view(), name="election.tally"),
|
path("tally/<int:pk>", views.ElectionTallyView.as_view(), name="election.tally"),
|
||||||
path(
|
path(
|
||||||
"publish/<int:pk>", views.ElectionPublishView.as_view(), name="election.publish"
|
"publish/<int:pk>",
|
||||||
|
views.ElectionChangePublicationView.as_view(),
|
||||||
|
name="election.publish",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"archive/<int:pk>", views.ElectionArchiveView.as_view(), name="election.archive"
|
"archive/<int:pk>", views.ElectionArchiveView.as_view(), name="election.archive"
|
||||||
|
|
|
@ -50,28 +50,60 @@ class ElectionUpdateView(SuccessMessageMixin, UpdateView):
|
||||||
|
|
||||||
class ElectionTallyView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
|
class ElectionTallyView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
|
||||||
model = Election
|
model = Election
|
||||||
|
pattern_name = "election.admin"
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return super().get_queryset().prefetch_related("questions__options")
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
election = self.get_object()
|
election = self.get_object()
|
||||||
election.update(tallied=True)
|
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)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ElectionPublishView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
|
class ElectionChangePublicationView(
|
||||||
|
SuccessMessageMixin, SingleObjectMixin, RedirectView
|
||||||
|
):
|
||||||
model = Election
|
model = Election
|
||||||
|
pattern_name = "election.admin"
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
election = self.get_object()
|
election = self.get_object()
|
||||||
election.update(tallied=True)
|
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)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ElectionArchiveView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
|
class ElectionArchiveView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
|
||||||
model = Election
|
model = Election
|
||||||
|
pattern_name = "election.admin"
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
election = self.get_object()
|
election = self.get_object()
|
||||||
election.update(tallied=True)
|
election.archived = True
|
||||||
|
election.save()
|
||||||
|
messages.success(request, _("Élection archivée avec succès !"))
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue