Skip to content

Commit

Permalink
Release Candidate 1.
Browse files Browse the repository at this point in the history
General bugfix correction. Add synthetic unit tests.
  • Loading branch information
JaviMuller committed Jun 30, 2023
1 parent af0646e commit e61d381
Show file tree
Hide file tree
Showing 18 changed files with 173 additions and 61 deletions.
Binary file removed Elections/examples/bol 3 cs.xlsx
Binary file not shown.
24 changes: 0 additions & 24 deletions Elections/examples/cs3_election.csv

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
39 changes: 39 additions & 0 deletions Elections/examples/synth_tests/cases_tested.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

--------------------------------------------------------------------------------
TEST-CASES [SCRIPT(<ROUNDS>)]*
--------------------------------------------------------------------------------
(3) [synth1, <1, 4, 6, 11>]
(4.1) [synth1, <2, 3, 8>]
(4.2.a.i) [synth1, <7, 9>]
(4.2.a.ii) [synth1, <3>]
(4.2.b) [synth1, <2>]
(4.3.c) [synth1, <5, 10>]
(4.3.d) [synth1, <8>]
(4.3.e) [synth2, <1>]
(5.5) [synth1, <5>], [synth2, <1>]
(spec1) [synth2, <1>]
(spec2) [synth1, <12>]
(spec3) [synth2, <1>]


Test case explanation:
3 - 1 winner in 1st round, elected by majority
4.1 - More than 2 winners in 1st round (1st round tiebreaker)
4.2.a.i - 1 winner in @1st round, sum of two first >50%, tie for second place
4.2.a.ii - 1 winner in @1st round, sum of two first >50%, no tie for second place
4.2.b - 1 winner in @1st round, sum of two first <= 50%
4.3.c - 2 winners in @1st round, sum of two first >50%
4.3.d - 2 winners in @1st round, sum of two first <=50%, sum of three first >50%
4.3.e - 2 winners in @1st round, sum of two first <=50%, sum of three first <=50%
5.5 - Presidential tie breaker (it will use all tiebreaker criteria)
spec1 - Custom made criteria. Diminishing consequent preferential tiebreakers
spec2 - Custom made criteria. 1st round without votes
spec3 - Custom made criteria. 4.3.e + absolute tie breaker (tie in special second round)


@1st round = 1st round + tiebreakers

Notes:
- On the synthetic tests, the presidential choice is always the first one.
- The rest of the rounds of synth2 have not been checked because they are not necessary
to check all the cases
14 changes: 14 additions & 0 deletions Elections/examples/synth_tests/csv/cs_synth1_election.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CS,1,2,3,4,5,6,7,8
Candidate A,1,1,1,1,1,1,1,1
Candidate B,2,2,3,3,4,4,5,5
Candidate C,3,3,4,4,2,2,6,7
Candidate D,4,4,2,2,3,3,7,6
Candidate E,5,5,5,6,6,6,2,3
Candidate F,6,6,6,5,5,5,3,2
Candidate G,7,8,8,8,8,8,4,4
Candidate H,,7,7,,,,,
Candidate I,,,,7,7,,,
Candidate J,,,,,,7,8,
Candidate K,8,,,,,,,8
Candidate L,,,,,,,,
Candidate M,,,,,,,,
14 changes: 14 additions & 0 deletions Elections/examples/synth_tests/csv/cs_synth2_election.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CS,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34
Candidate A,1,1,1,1,2,,,,2,,,,,,,,2,2,,,2,,,,2,2,,,,,,,,
Candidate B,2,2,,,1,1,1,1,,,,,,,,,,,2,2,,2,,,,,2,2,,,,,,
Candidate C,,,2,,,2,,,1,1,1,1,,,2,2,,,,,,,2,,,,,,2,2,,,,
Candidate D,,,,,,,,,,,,,1,1,1,1,,,,,,,,2,,,,,,,2,2,,
Candidate E,,,,,,,,,,,,,,,,,1,1,1,1,,,,,,,,,,,,,2,2
Candidate F,,,,,,,,,,,,,,,,,,,,,1,1,1,1,,,,,,,,,,
Candidate G,,,,,,,,,,,,,,,,,,,,,,,,,1,1,1,,,,,,,
Candidate H,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,1,1,,,,
Candidate I,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,1,1,
Candidate J,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1
Candidate K,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Candidate L,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Candidate M,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Binary file not shown.
20 changes: 14 additions & 6 deletions Elections/program/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from functools import reduce
from constants import *
from utils import *
import math


################################################################################
Expand Down Expand Up @@ -37,6 +38,12 @@ def __str__(self):
string: the candidate's name
"""
return self.name

def __eq__(self, other):
return self.name == other.name

def __hash__(self):
return hash(self.name)

def initals(self):
return ''.join([x[0].upper() for x in self.name.split(' ')])
Expand Down Expand Up @@ -259,7 +266,7 @@ def __init__(self, votes, tally):
self.res = tally.to_ordered_list()
self.first = [x[0] for x in self.res if x[1] == self.res[0][1]]
self.second = [x[0] for x in self.res[len(self.first):] if x[1] == self.res[len(self.first)][1]]
self.percentages = [x[1]/self.votes for x in self.res]
self.percentages = [zero_div(x[1], self.votes) for x in self.res]

def single_winner(self):
"""Function to check if a round has had a single winner
Expand All @@ -286,7 +293,8 @@ def over_half(self, n):
Returns:
bool: True if the top n candidates have over half the votes, False otherwise
"""
return sum(self.percentages[0:n]) > 0.5
# The second part helps to check wether the sum is 0.5, due to floating point errors
return sum(self.percentages[0:n]) > 0.5 and not math.isclose(sum(self.percentages[0:n]), 0.5)

def sum_percentages(self, n):
"""Function to sum the percentages of the top n candidates
Expand Down Expand Up @@ -314,7 +322,7 @@ def __str__(self):
RoundResult.dash,
# Nome Votos Percentagem
'\n'.join(
[f'{str(x[0]):<30}{str(x[1]):^10}{(x[1]/self.votes*100):^12.1f}' for x in self.res]),
[f'{str(x[0]):<30}{str(x[1]):^10}{(zero_div(x[1], self.votes)*100):^12.1f}' for x in self.res]),
' '
])
)
Expand All @@ -334,9 +342,9 @@ def parse_csv(path):
tuple(election_type (int), candidates (list(Candidate)), ballots (list(Ballot))): election type, list of candidates and list of ballots
"""
data = import_csv(path)
candidates = [Candidate(name) for name in data['candidates']]
candidates = [Candidate(name) for name in sorted(data['candidates'])]
if data['type'] == TYPE_CS:
ballots = [SuperiorCouncilBallot(id, votes) for id, votes in data['ballots']]
ballots = [SuperiorCouncilBallot(id, [Candidate(name) for name in votes]) for id, votes in sorted(data['ballots'], key=lambda x: x[0])]
elif data['type'] == TYPE_NC:
ballots = [ChamberBallot(id, votes) for id, votes in data['ballots']]
ballots = [ChamberBallot(id, [Candidate(name) for name in votes]) for id, votes in sorted(data['ballots'], key=lambda x: x[0])]
return data['type'], candidates, ballots
21 changes: 18 additions & 3 deletions Elections/program/clinterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ def get_candidates(self, initial = []):
if name == '':
break
candidates.append(Candidate(name))
candidates.sort(key=lambda candidate: candidate.get_name())
print('Os candidatos são:')
for i in range(len(candidates)):
print(indent(f'{i+1}. {candidates[i]}', 1))
Expand Down Expand Up @@ -171,11 +172,25 @@ def get_ballots(self, election_type, candidates, initial=[]):
return ballots

def get_presidential_choice(self, candidates):
print('Voto presidencial:')
print('Candidatos:')
for i in range(len(candidates)):
print(indent(f'{i+1}. {candidates[i]}', 1))
choice = self.get_choice(len(candidates))
return candidates[choice-1]
return self.get_choice(len(candidates))

def get_presidential_choices(self, candidates, max_winners):
if max_winners == 1:
return [candidates[self.get_presidential_choice(candidates)-1]]
print('Candidatos:')
for i in range(len(candidates)):
print(indent(f'{i+1}. {candidates[i]}', 1))
print(f'Introduza o numero de candidatos que quer escolher (max: {max_winners})')
max_winners = self.get_choice(max_winners)
choice = []
for i in range(max_winners):
print (f'Escolha o {i+1}º candidato:')
remaining_candidates = [candidate for candidate in candidates if candidate not in choice]
choice.append(remaining_candidates[self.get_presidential_choice(remaining_candidates)-1])
return choice

def output_results(self, seats, elected):
pass # Already done in logging
Loading

0 comments on commit e61d381

Please sign in to comment.