Skip to content

Commit

Permalink
adding support for implicit invalid vote (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
edulix authored Apr 29, 2022
1 parent 0a43f95 commit 2f81735
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 25 deletions.
5 changes: 4 additions & 1 deletion tally_methods/tally.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
# along with tally-methods. If not, see <http://www.gnu.org/licenses/>.


from tally_methods.voting_systems.base import get_voting_system_by_id, BlankVoteException
from tally_methods.voting_systems.base import (
get_voting_system_by_id,
BlankVoteException
)

import copy
import glob
Expand Down
51 changes: 39 additions & 12 deletions tally_methods/voting_systems/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,12 @@ def parse_vote(
'''
raw_ballot = self.decoder.decode_from_int(int_ballot)
decoded_ballot = self.decoder.decode_raw_ballot(raw_ballot)
exception = None

# detect if the ballot was marked as invalid, even if there's no
# explicit invalid answer
if raw_ballot['choices'][0] > 0:
raise Exception()
exception = 'explicit'

non_blank_unwithdrawed_answers = None
if self.custom_subparser is None:
Expand All @@ -208,8 +209,8 @@ def parse_vote(
withdrawals
)

if len(non_blank_unwithdrawed_answers) == 0:
raise BlankVoteException()
if len(non_blank_unwithdrawed_answers) == 0 and exception is None:
exception = 'blank'

# check that no write-in is repeated or else it's an invalid vote
write_in_answers = [
Expand All @@ -220,15 +221,15 @@ def parse_vote(
len(answer['text']) > 0
)
]
if len(write_in_answers) != len(set(write_in_answers)):
raise Exception()
if len(write_in_answers) != len(set(write_in_answers)) and exception != 'explicit':
exception = 'implicit'

# detect and deal with different types of invalid votes
if (
len(non_blank_unwithdrawed_answers) < question['min'] or
len(set(non_blank_unwithdrawed_answers)) != len(non_blank_unwithdrawed_answers)
):
raise Exception()
) and exception != 'explicit':
exception = 'implicit'

truncate = False
if len(non_blank_unwithdrawed_answers) > question['max']:
Expand All @@ -239,8 +240,8 @@ def parse_vote(
non_blank_unwithdrawed_answers = \
non_blank_unwithdrawed_answers[:question['max']]
truncate = True
else:
raise Exception()
elif exception != 'explicit':
exception = 'implicit'

# if panachage is disabled and vote is for answer of multiple categories
# then it's an invalid vote
Expand All @@ -255,10 +256,20 @@ def parse_vote(
]
if truncate:
filtered_answer_categories = filtered_answer_categories[:question['max']]
if len(set(filtered_answer_categories)) > 1:
raise Exception()
if (
len(set(filtered_answer_categories)) > 1 and
exception != 'explicit'
):
exception = 'implicit'

return non_blank_unwithdrawed_answers
if exception is None:
return non_blank_unwithdrawed_answers
elif exception == 'blank':
raise BlankVoteException(non_blank_unwithdrawed_answers)
elif exception == 'explicit':
raise ExplicitInvalidVoteException(non_blank_unwithdrawed_answers)
elif exception == 'implicit':
raise ImplicitInvalidVoteException(non_blank_unwithdrawed_answers)

def add_vote(self, voter_answers, questions, is_delegated):
'''
Expand Down Expand Up @@ -354,3 +365,19 @@ def get_log(self):

class BlankVoteException(Exception):
pass

class ExplicitInvalidVoteException(Exception):

ballot = None

def __init__(self, ballot):
self.ballot = ballot
super().__init__()

class ImplicitInvalidVoteException(Exception):

ballot = None

def __init__(self, ballot):
self.ballot = ballot
super().__init__()
13 changes: 9 additions & 4 deletions tally_methods/voting_systems/borda.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
BaseVotingSystem,
BaseTally,
WeightedChoice,
ImplicitInvalidVoteException,
get_key
)

Expand Down Expand Up @@ -52,6 +53,7 @@ class BordaTally(BaseTally):
def init(self):
def custom_subparser(decoded_ballot, question, withdrawals):
answers = set()
exception = None

if 'bordas-max-points' not in question:
max_points = question['max']
Expand All @@ -78,7 +80,7 @@ def custom_subparser(decoded_ballot, question, withdrawals):
]
# - no position is repeated
if len(selection) != len(set(selection)):
raise Exception()
exception = 'implicit'

selection_sorted = sorted(selection)
should_be_selection_sorted = [
Expand All @@ -87,10 +89,13 @@ def custom_subparser(decoded_ballot, question, withdrawals):
]
# - no missing position in-between
if selection_sorted != should_be_selection_sorted:
raise Exception()
exception = 'implicit'


return frozenset(answers)
ret_value = frozenset(answers)
if exception is None:
return ret_value
else:
raise ImplicitInvalidVoteException(ret_value)

self.custom_subparser = custom_subparser

Expand Down
13 changes: 9 additions & 4 deletions tally_methods/voting_systems/borda_custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
BaseVotingSystem,
BaseTally,
WeightedChoice,
ImplicitInvalidVoteException,
get_key
)
from .borda import BordaTally
Expand Down Expand Up @@ -53,8 +54,8 @@ class BordaCustomTally(BordaTally):
def init(self):
def custom_subparser(decoded_ballot, question, withdrawals):
answers = set()

weights = question['borda_custom_weights']
exception = None

for answer in decoded_ballot["answers"]:
if answer['selected'] < 0 or answer['id'] in withdrawals:
Expand All @@ -76,7 +77,7 @@ def custom_subparser(decoded_ballot, question, withdrawals):
]
# - no position is repeated
if len(selection) != len(set(selection)):
raise Exception()
exception = 'implicit'

selection_sorted = sorted(selection)
should_be_selection_sorted = [
Expand All @@ -85,8 +86,12 @@ def custom_subparser(decoded_ballot, question, withdrawals):
]
# - no missing position in-between
if selection_sorted != should_be_selection_sorted:
raise Exception()
exception = 'implicit'

return frozenset(answers)
ret_value = frozenset(answers)
if exception is None:
return ret_value
else:
raise ImplicitInvalidVoteException(ret_value)

self.custom_subparser = custom_subparser
14 changes: 10 additions & 4 deletions tally_methods/voting_systems/borda_nauru.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
from .base import (
BaseVotingSystem,
BaseTally,
WeightedChoice,
WeightedChoice,
ImplicitInvalidVoteException,
get_key
)
from .borda import BordaTally
Expand Down Expand Up @@ -50,6 +51,7 @@ class BordaNauruTally(BordaTally):
def init(self):
def custom_subparser(decoded_ballot, question, withdrawals):
answers = set()
exception = None

for answer in decoded_ballot["answers"]:
if answer['selected'] < 0 or answer['id'] in withdrawals:
Expand All @@ -71,7 +73,7 @@ def custom_subparser(decoded_ballot, question, withdrawals):
]
# - no position is repeated
if len(selection) != len(set(selection)):
raise Exception()
exception = 'implicit'

selection_sorted = sorted(selection)
should_be_selection_sorted = [
Expand All @@ -80,8 +82,12 @@ def custom_subparser(decoded_ballot, question, withdrawals):
]
# - no missing position in-between
if selection_sorted != should_be_selection_sorted:
raise Exception()
exception = 'implicit'

return frozenset(answers)
ret_value = frozenset(answers)
if exception is None:
return ret_value
else:
raise ImplicitInvalidVoteException(ret_value)

self.custom_subparser = custom_subparser

0 comments on commit 2f81735

Please sign in to comment.