diff --git a/elections/forms.py b/elections/forms.py
index 35ec9ba..9d3c56c 100644
--- a/elections/forms.py
+++ b/elections/forms.py
@@ -94,10 +94,10 @@ class RankVoteForm(forms.ModelForm):
class BallotFormset:
- select_formset = inlineformset_factory(
+ select = inlineformset_factory(
Question, Option, extra=0, form=SelectVoteForm, can_delete=False
)
- rank_formset = inlineformset_factory(
+ rank = inlineformset_factory(
Question, Option, extra=0, form=RankVoteForm, can_delete=False
)
diff --git a/elections/models.py b/elections/models.py
index 4419eaa..fb05ee0 100644
--- a/elections/models.py
+++ b/elections/models.py
@@ -11,7 +11,13 @@ from .staticdefs import (
TALLY_FUNCTIONS,
VALIDATE_FUNCTIONS,
)
-from .utils import CastFunctions, TallyFunctions, ValidateFunctions, choices_length
+from .utils import (
+ CastFunctions,
+ ResultsData,
+ TallyFunctions,
+ ValidateFunctions,
+ choices_length,
+)
# #############################################################################
# Models regarding an election
@@ -96,6 +102,17 @@ class Question(models.Model):
return getattr(BallotFormset, BALLOT_TYPE[self.type])
+ def get_results_data(self):
+ results_function = getattr(ResultsData, BALLOT_TYPE[self.type])
+ return results_function(self)
+
+ @property
+ def vote_type(self):
+ return BALLOT_TYPE[self.type]
+
+ def __str__(self):
+ return self.text
+
class Meta:
ordering = ["id"]
@@ -115,6 +132,9 @@ class Option(models.Model):
# 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)
+ def __str__(self):
+ return self.text
+
class Meta:
ordering = ["id"]
diff --git a/elections/staticdefs.py b/elections/staticdefs.py
index 29fffd2..8f8a7c5 100644
--- a/elections/staticdefs.py
+++ b/elections/staticdefs.py
@@ -25,9 +25,9 @@ QUESTION_TYPES = [
]
BALLOT_TYPE = {
- "assentiment": "select_formset",
- "uninominal": "select_formset",
- "condorcet": "rank_formset",
+ "assentiment": "select",
+ "uninominal": "select",
+ "condorcet": "rank",
}
VOTE_RULES = {
diff --git a/elections/templates/elections/election.html b/elections/templates/elections/election.html
index 2e85b33..59c3e26 100644
--- a/elections/templates/elections/election.html
+++ b/elections/templates/elections/election.html
@@ -124,16 +124,6 @@
{% for q in election.questions.all %}
- {% comment %}
- {% if can_vote and election.start_date < current_time and election.end_date > current_time %}
-
-
-
-
- {% trans "Voter" %}
-
- {% endif %}
- {% endcomment %}
{{ q.text }}
{% if q in cast_questions %}
@@ -145,16 +135,32 @@
{% for o in q.options.all %}
{% if election.tallied and election.results_public %}
+ {% if q.vote_type == "select" %}
{{ o.nb_votes }}
+
+ {% elif q.vote_type == "rank" %}
+
+
+
+
+ {{ forloop.counter }}
+
{% endif %}
+ {% endif %}
+
{{ o.text }}
{% endfor %}
+
+ {# Affiche plus d'informations sur le résultat #}
+ {% if election.tallied and election.results_public %}
+ {{ q.get_results_data }}
+ {% endif %}
{% endfor %}
diff --git a/elections/templates/elections/results/rank.html b/elections/templates/elections/results/rank.html
new file mode 100644
index 0000000..c3a4de8
--- /dev/null
+++ b/elections/templates/elections/results/rank.html
@@ -0,0 +1,46 @@
+{% load i18n %}
+
+
+
+
+
+
+
+
+
+
+ |
+ {% for i in range %}
+
+
+
+
+ {{ i }}
+ |
+ {% endfor %}
+
+
+
+ {% for line in matrix %}
+
+
+
+
+
+ {{ forloop.counter }}
+ |
+
+ {% for cell in line %}
+ {{ cell }} |
+ {% endfor %}
+
+ {% endfor %}
+
+
+
+
+
+
+
+ {% trans "La matrice des résultats montre les points d'avance, l'option gagnante est affichée sur la ligne et la perdante sur la colonne. " %}
+
diff --git a/elections/templates/elections/results/select.html b/elections/templates/elections/results/select.html
new file mode 100644
index 0000000..0010328
--- /dev/null
+++ b/elections/templates/elections/results/select.html
@@ -0,0 +1,5 @@
+{% load i18n %}
+
+
+ {% trans "L'option majoritaire et gagnante est colorée en vert." %}
+
diff --git a/elections/utils.py b/elections/utils.py
index 1c8ad60..56b05c1 100644
--- a/elections/utils.py
+++ b/elections/utils.py
@@ -8,6 +8,7 @@ from django.contrib.auth.hashers import make_password
from django.core.exceptions import ValidationError
from django.core.mail import EmailMessage, get_connection
from django.core.validators import validate_email
+from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _
# #############################################################################
@@ -185,6 +186,31 @@ class ValidateFunctions:
return valid
+class ResultsData:
+ """Classe pour afficher des informations supplémentaires après la fin d'une élection"""
+
+ def select(question):
+ """On renvoie l'explication des couleurs"""
+ return render_to_string("elections/results/select.html")
+
+ def rank(question):
+ """On récupère la matrice des résultats et on l'affiche"""
+ duels = question.duels.all()
+ options = list(question.options.all())
+ n = len(options)
+
+ matrix = np.zeros((n, n), dtype=int)
+
+ for d in duels:
+ i, j = options.index(d.winner), options.index(d.loser)
+ matrix[i, j] = d.amount
+ print(matrix)
+ return render_to_string(
+ "elections/results/rank.html",
+ {"q": question, "matrix": matrix, "range": range(1, n + 1)},
+ )
+
+
# #############################################################################
# Fonctions pour importer une liste de votant·e·s
# #############################################################################
diff --git a/elections/views.py b/elections/views.py
index eeb3676..1179c49 100644
--- a/elections/views.py
+++ b/elections/views.py
@@ -355,7 +355,11 @@ class ElectionView(NotArchivedMixin, DetailView):
return context
def get_queryset(self):
- return super().get_queryset().prefetch_related("questions__options")
+ return (
+ super()
+ .get_queryset()
+ .prefetch_related("questions__options", "questions__duels")
+ )
class ElectionVotersView(NotArchivedMixin, DetailView):