diff --git a/election/models.py b/election/models.py index fc3a90dc..3e807ff6 100644 --- a/election/models.py +++ b/election/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.db.models import Count from django.utils import timezone from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ @@ -110,27 +111,28 @@ class Role(OrderedModel): def __str__(self): return f"{self.title} - {self.election.title}" - def results(self, total_vote): - results = {} - total_vote *= self.max_choice - non_blank = 0 - for candidature in self.candidatures.all(): - cand_results = {} - cand_results["vote"] = self.votes.filter(candidature=candidature).count() - if total_vote == 0: - cand_results["percent"] = 0 - else: - cand_results["percent"] = cand_results["vote"] * 100 / total_vote - non_blank += cand_results["vote"] - results[candidature.user.username] = cand_results - results["total vote"] = total_vote + def results(self, total_vote: int) -> dict[str, dict[str, int | float]]: if total_vote == 0: - results["blank vote"] = {"vote": 0, "percent": 0} - else: - results["blank vote"] = { - "vote": total_vote - non_blank, - "percent": (total_vote - non_blank) * 100 / total_vote, + candidates = self.candidatures.values_list("user__username") + return { + key: {"vote": 0, "percent": 0} for key in ["blank_votes", *candidates] } + total_vote *= self.max_choice + results = {"total vote": total_vote} + non_blank = 0 + candidatures = self.candidatures.annotate(nb_votes=Count("votes")).values( + "nb_votes", "user__username" + ) + for candidature in candidatures: + non_blank += candidature["nb_votes"] + results[candidature["user__username"]] = { + "vote": candidature["nb_votes"], + "percent": candidature["nb_votes"] * 100 / total_vote, + } + results["blank vote"] = { + "vote": total_vote - non_blank, + "percent": (total_vote - non_blank) * 100 / total_vote, + } return results @property diff --git a/election/tests.py b/election/tests.py index f4cc380e..68dc7fc1 100644 --- a/election/tests.py +++ b/election/tests.py @@ -1,9 +1,12 @@ +import pytest from django.conf import settings from django.test import TestCase from django.urls import reverse +from model_bakery import baker +from core.baker_recipes import subscriber_user from core.models import Group, User -from election.models import Election +from election.models import Candidature, Election, ElectionList, Role, Vote class TestElection(TestCase): @@ -12,8 +15,7 @@ class TestElection(TestCase): cls.election = Election.objects.first() cls.public_group = Group.objects.get(id=settings.SITH_GROUP_PUBLIC_ID) cls.sli = User.objects.get(username="sli") - cls.subscriber = User.objects.get(username="subscriber") - cls.public = User.objects.get(username="public") + cls.public = baker.make(User) class TestElectionDetail(TestElection): @@ -36,7 +38,7 @@ class TestElectionDetail(TestElection): class TestElectionUpdateView(TestElection): def test_permission_denied(self): - self.client.force_login(self.subscriber) + self.client.force_login(subscriber_user.make()) response = self.client.get( reverse("election:update", args=str(self.election.id)) ) @@ -45,3 +47,47 @@ class TestElectionUpdateView(TestElection): reverse("election:update", args=str(self.election.id)) ) assert response.status_code == 403 + + +@pytest.mark.django_db +def test_election_results(): + election = baker.make( + Election, voters=baker.make(User, _quantity=50, _bulk_create=True) + ) + lists = baker.make(ElectionList, election=election, _quantity=2, _bulk_create=True) + roles = baker.make( + Role, election=election, max_choice=iter([1, 2]), _quantity=2, _bulk_create=True + ) + users = baker.make(User, _quantity=4, _bulk_create=True) + cand = [ + baker.make(Candidature, role=roles[0], user=users[0], election_list=lists[0]), + baker.make(Candidature, role=roles[0], user=users[1], election_list=lists[1]), + baker.make(Candidature, role=roles[1], user=users[2], election_list=lists[0]), + baker.make(Candidature, role=roles[1], user=users[3], election_list=lists[1]), + ] + votes = [ + baker.make(Vote, role=roles[0], _quantity=20, _bulk_create=True), + baker.make(Vote, role=roles[0], _quantity=25, _bulk_create=True), + baker.make(Vote, role=roles[1], _quantity=20, _bulk_create=True), + baker.make(Vote, role=roles[1], _quantity=35, _bulk_create=True), + baker.make(Vote, role=roles[1], _quantity=10, _bulk_create=True), + ] + cand[0].votes.set(votes[0]) + cand[1].votes.set(votes[1]) + cand[2].votes.set([*votes[2], *votes[4]]) + cand[3].votes.set([*votes[3], *votes[4]]) + + assert election.results == { + roles[0].title: { + cand[0].user.username: {"percent": 40.0, "vote": 20}, + cand[1].user.username: {"percent": 50.0, "vote": 25}, + "blank vote": {"percent": 10.0, "vote": 5}, + "total vote": 50, + }, + roles[1].title: { + cand[2].user.username: {"percent": 30.0, "vote": 30}, + cand[3].user.username: {"percent": 45.0, "vote": 45}, + "blank vote": {"percent": 25.0, "vote": 25}, + "total vote": 100, + }, + }