Fin de condorcet et affichage d'infos supplémentaires pour les résultats
This commit is contained in:
parent
fea1ab495d
commit
81e345527b
8 changed files with 124 additions and 17 deletions
|
@ -94,10 +94,10 @@ class RankVoteForm(forms.ModelForm):
|
||||||
|
|
||||||
|
|
||||||
class BallotFormset:
|
class BallotFormset:
|
||||||
select_formset = inlineformset_factory(
|
select = inlineformset_factory(
|
||||||
Question, Option, extra=0, form=SelectVoteForm, can_delete=False
|
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
|
Question, Option, extra=0, form=RankVoteForm, can_delete=False
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,7 +11,13 @@ from .staticdefs import (
|
||||||
TALLY_FUNCTIONS,
|
TALLY_FUNCTIONS,
|
||||||
VALIDATE_FUNCTIONS,
|
VALIDATE_FUNCTIONS,
|
||||||
)
|
)
|
||||||
from .utils import CastFunctions, TallyFunctions, ValidateFunctions, choices_length
|
from .utils import (
|
||||||
|
CastFunctions,
|
||||||
|
ResultsData,
|
||||||
|
TallyFunctions,
|
||||||
|
ValidateFunctions,
|
||||||
|
choices_length,
|
||||||
|
)
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Models regarding an election
|
# Models regarding an election
|
||||||
|
@ -96,6 +102,17 @@ class Question(models.Model):
|
||||||
|
|
||||||
return getattr(BallotFormset, BALLOT_TYPE[self.type])
|
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:
|
class Meta:
|
||||||
ordering = ["id"]
|
ordering = ["id"]
|
||||||
|
|
||||||
|
@ -115,6 +132,9 @@ class Option(models.Model):
|
||||||
# For now, we store the amount of votes received after the election is tallied
|
# 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)
|
nb_votes = models.PositiveSmallIntegerField(_("nombre de votes reçus"), default=0)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.text
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["id"]
|
ordering = ["id"]
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,9 @@ QUESTION_TYPES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
BALLOT_TYPE = {
|
BALLOT_TYPE = {
|
||||||
"assentiment": "select_formset",
|
"assentiment": "select",
|
||||||
"uninominal": "select_formset",
|
"uninominal": "select",
|
||||||
"condorcet": "rank_formset",
|
"condorcet": "rank",
|
||||||
}
|
}
|
||||||
|
|
||||||
VOTE_RULES = {
|
VOTE_RULES = {
|
||||||
|
|
|
@ -124,16 +124,6 @@
|
||||||
{% for q in election.questions.all %}
|
{% for q in election.questions.all %}
|
||||||
<div class="panel" id="q_{{ q.pk }}">
|
<div class="panel" id="q_{{ q.pk }}">
|
||||||
<div class="panel-heading is-size-6">
|
<div class="panel-heading is-size-6">
|
||||||
{% comment %}
|
|
||||||
{% if can_vote and election.start_date < current_time and election.end_date > current_time %}
|
|
||||||
<a class="tag 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 %}
|
|
||||||
{% endcomment %}
|
|
||||||
<span class="ml-2">{{ q.text }}</span>
|
<span class="ml-2">{{ q.text }}</span>
|
||||||
|
|
||||||
{% if q in cast_questions %}
|
{% if q in cast_questions %}
|
||||||
|
@ -145,16 +135,32 @@
|
||||||
{% for o in q.options.all %}
|
{% for o in q.options.all %}
|
||||||
<div class="panel-block">
|
<div class="panel-block">
|
||||||
{% if election.tallied and election.results_public %}
|
{% if election.tallied and election.results_public %}
|
||||||
|
{% if q.vote_type == "select" %}
|
||||||
<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>
|
||||||
|
|
||||||
|
{% elif q.vote_type == "rank" %}
|
||||||
|
<span class="tag is-primary">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-layer-group"></i>
|
||||||
|
</span>
|
||||||
|
<span>{{ forloop.counter }}</span>
|
||||||
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<span class="ml-2">{{ o.text }}</span>
|
<span class="ml-2">{{ o.text }}</span>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
{# Affiche plus d'informations sur le résultat #}
|
||||||
|
{% if election.tallied and election.results_public %}
|
||||||
|
{{ q.get_results_data }}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
46
elections/templates/elections/results/rank.html
Normal file
46
elections/templates/elections/results/rank.html
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<div class="panel-block">
|
||||||
|
<div class="columns is-centered is-fullwidth">
|
||||||
|
<div class="column">
|
||||||
|
<table class="table is-bordered is-striped">
|
||||||
|
<thead>
|
||||||
|
<th class="has-text-centered">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-undo-alt"></i>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
{% for i in range %}
|
||||||
|
<th class="has-text-centered">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-layer-group"></i>
|
||||||
|
</span>
|
||||||
|
<span>{{ i }}</span>
|
||||||
|
</th>
|
||||||
|
{% endfor %}
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{% for line in matrix %}
|
||||||
|
<tr>
|
||||||
|
<th class="has-text-centered">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fas fa-layer-group"></i>
|
||||||
|
</span>
|
||||||
|
<span>{{ forloop.counter }}</span>
|
||||||
|
</th>
|
||||||
|
|
||||||
|
{% for cell in line %}
|
||||||
|
<td class="has-text-centered">{{ cell }}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="panel-block">
|
||||||
|
<i>{% 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. " %}</i>
|
||||||
|
</div>
|
5
elections/templates/elections/results/select.html
Normal file
5
elections/templates/elections/results/select.html
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<div class="panel-block">
|
||||||
|
<i>{% trans "L'option majoritaire et gagnante est colorée en vert." %}</i>
|
||||||
|
</div>
|
|
@ -8,6 +8,7 @@ from django.contrib.auth.hashers import make_password
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.mail import EmailMessage, get_connection
|
from django.core.mail import EmailMessage, get_connection
|
||||||
from django.core.validators import validate_email
|
from django.core.validators import validate_email
|
||||||
|
from django.template.loader import render_to_string
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
@ -185,6 +186,31 @@ class ValidateFunctions:
|
||||||
return valid
|
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
|
# Fonctions pour importer une liste de votant·e·s
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
|
|
@ -355,7 +355,11 @@ class ElectionView(NotArchivedMixin, DetailView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_queryset(self):
|
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):
|
class ElectionVotersView(NotArchivedMixin, DetailView):
|
||||||
|
|
Loading…
Add table
Reference in a new issue