Skip to content

Commit

Permalink
Merge pull request #58 from eevelweezel/python312
Browse files Browse the repository at this point in the history
Python 3.12 support for eVote
  • Loading branch information
mdipierro committed Nov 9, 2023
2 parents dcb870b + b0edbf8 commit ab3de26
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 46 deletions.
4 changes: 2 additions & 2 deletions controllers/appadmin.py
Expand Up @@ -250,7 +250,7 @@ def select():
else:
rows = db(query, ignore_common_filters=True).select(
limitby=(start, stop))
except Exception, e:
except Exception as e:
import traceback
tb = traceback.format_exc()
(rows, nrows) = ([], 0)
Expand All @@ -269,7 +269,7 @@ def select():
import_csv(db[request.vars.table],
request.vars.csvfile.file)
response.flash = T('data uploaded')
except Exception, e:
except Exception as e:
response.flash = DIV(T('unable to parse csv file'), PRE(str(e)))
# end handle upload csv

Expand Down
44 changes: 22 additions & 22 deletions controllers/default.py
Expand Up @@ -26,14 +26,14 @@ def edit():
db.election.voters.default = auth.user.email
db.election.managers.default = auth.user.email
db.election.public_key.default = pubkey
db.election.private_key.default = privkey
db.election.private_key.default = privkey
form = SQLFORM(db.election,election,deletable=True,
submit_button="Save and Preview").process()
if form.accepted: redirect(URL('start',args=form.vars.id))
return dict(form=form)

@auth.requires(auth.user and auth.user.is_manager)
def start():
def start():
election = db.election(request.args(0,cast=int)) or redirect(URL('index'))
check_closed(election)
response.subtitle = election.title+T(' / Start')
Expand Down Expand Up @@ -130,10 +130,10 @@ def self_service():
else:
response.flash = T('Unable to send email')
return dict(form=form)


@auth.requires(auth.user and auth.user.is_manager)
def reminders():
def reminders():
election = db.election(request.args(0,cast=int)) or redirect(URL('index'))
response.subtitle = election.title+T(' / Reminders')
return dict(election=election)
Expand All @@ -152,7 +152,7 @@ def reminders_callback():
key = 'voter_%s' % voter.id
fields.append(Field(key,'boolean',default=not voter.voted,
label = voter.email))
if key in request.post_vars:
if key in request.post_vars:
link = URL('vote',args=(election.id,voter_uuid),scheme=SCHEME)
link_ballots = URL('ballots',args=election.id,scheme=SCHEME)
link_results = URL('results',args=election.id,scheme=SCHEME)
Expand All @@ -170,7 +170,7 @@ def reminders_callback():
sender = election.email_sender or mail.settings.sender
for to, subject, body in emails:
if not mail.send(to=to, subject=subject, message=body, sender=sender):

failures.append(email)
if not failures:
session.flash = T('Emails sent successfully')
Expand All @@ -188,7 +188,7 @@ def compute_results(election):
voted_ballots = db(query)(db.ballot.voted==True).select()
counters = {}
rankers = {}
for k,ballot in enumerate(voted_ballots):
for k,ballot in enumerate(voted_ballots):
for name in ballot.results:
# name is the name of a group as in {{name:ranking}}
# scheme is "ranking" or "checkbox" (default)
Expand All @@ -200,7 +200,7 @@ def compute_results(election):
if scheme == 'simple-majority':
# counters[key] counts how many times this checkbox was checked
counters[key] = counters.get(key,0) + 1

elif scheme == 'ranking':
raise NotImplementedError
# rankers[name] = [[2,1,3],[3,1,2],[1,2,3],...]
Expand Down Expand Up @@ -228,16 +228,16 @@ def compute_results(election):
if len(rankers[name])<k+1:
rankers[name].append([])
vote = rankers[name][-1]
print "ballot id:",ballot.id, "key:",key, "results[key]:",results[key], "vote:",vote
print("ballot id:",ballot.id, "key:",key, "results[key]:",results[key], "vote:",vote)
ranking = int(results[key])
d = ranking-len(vote)
if d>0:
print "vote before:", vote
print("vote before:", vote)
vote+=[0]*d
print "vote after: ", vote
print("vote after: ", vote)
vote[ranking-1] = value
else:
raise RuntimeError("Invalid Voting Scheme")
raise RuntimeError("Invalid Voting Scheme")

for name in rankers:
votes = rankers[name]
Expand All @@ -252,7 +252,7 @@ def compute_results(election):
for (r,k) in cschulze:
counters[key] += ' S:%s' % r

print counters
print(counters)
election.update_record(counters=counters)

#@cache(request.env.path_info,time_expire=300,cache_model=cache.ram)
Expand All @@ -274,26 +274,26 @@ def hash_ballot(text):
import re
text = text.replace('checked="checked" ','')
text = text.replace('disabled="disabled" ','')
text = re.sub('value="\d+"','',text)
text = re.sub('ballot\S+','',text)
text = re.sub(r'value="\d+"','',text)
text = re.sub(r'ballot\S+','',text)
return hash(text)

def ballots():
election = db.election(request.args(0,cast=int)) or \
redirect(URL('invalid_link'))
response.subtitle = election.title + T(' / Ballots')
ballots = db(db.ballot.election_id==election.id).select(
ballots = db(db.ballot.election_id==election.id).select(
orderby=db.ballot.ballot_uuid)
tampered = len(set(hash_ballot(b.ballot_content)
tampered = len(set(hash_ballot(b.ballot_content)
for b in ballots if b.voted))>1
return dict(ballots=ballots,election=election, tampered=tampered)

# @auth.requires(auth.user and auth.user.is_manager)
def email_voter_and_managers(election,voter,ballot,body):
import cStringIO
from io import StringIO
attachment = mail.Attachment(
filename=ballot.ballot_uuid+'.html',
payload=cStringIO.StringIO(ballot.ballot_content))
payload=StringIO(ballot.ballot_content))
sender = election.email_sender or mail.settings.sender
ret = mail.send(to=voter.email,
subject='Receipt for %s' % election.title,
Expand Down Expand Up @@ -386,18 +386,18 @@ def ballot_verifier():
response.headers['Content-Type'] = 'text/plain'
return ballot()

def vote():
def vote():
import hashlib
response.menu = []
election_id = request.args(0,cast=int)
voter_uuid = request.args(1)
election = db.election(election_id) or redirect(URL('invalid_link'))
election = db.election(election_id) or redirect(URL('invalid_link'))
voter = db(db.voter.election_id==election_id)\
(db.voter.voter_uuid==voter_uuid).select().first() or \
redirect(URL('invalid_link'))
if not DEBUG_MODE and voter.voted:
redirect(URL('voted_already'))

if election.deadline and request.now>election.deadline:
session.flash = T('Election is closed')
if voter.voted:
Expand Down
21 changes: 11 additions & 10 deletions modules/ballot.py
Expand Up @@ -3,16 +3,16 @@
import rsa
import json
import random
import cPickle as pickle
import pickle
from uuid import uuid4
try:
import ast
have_ast=True
except:
have_ast=False

regex_field = re.compile('{{(\w+)(\:\w+)?\!?}}')
regex_email = re.compile('[^\s<>"\',;]+\@[^\s<>"\',;]+',re.IGNORECASE)
regex_field = re.compile(r'{{(\w+)(\:\w+)?\!?}}')
regex_email = re.compile(r'[^\s<>"\',;]+\@[^\s<>"\',;]+',re.IGNORECASE)


def uuid():
Expand All @@ -24,13 +24,14 @@ def rsakeys():

def sign(text,privkey_pem):
privkey = rsa.PrivateKey.load_pkcs1(privkey_pem)
signature = base64.b16encode(rsa.sign(text,privkey,'SHA-1'))
signature = base64.b16encode(rsa.sign(bytes(text, 'utf-8'), privkey,'SHA-1'))
signature = signature.decode()
return signature

def ballot2form(ballot_model, readonly=False, vars=None, counters=None):
"""If counters is passed this counts the results in the ballot.
If readonly is False, then the voter has not yet voted; if readonly
is True, then they have just voted."""
is True, then they have just voted."""
ballot_structure = json.loads(ballot_model)
ballot = FORM()
for question in ballot_structure:
Expand All @@ -39,7 +40,7 @@ def ballot2form(ballot_model, readonly=False, vars=None, counters=None):
html = MARKMIN(question['preamble'])
div.append(html)
table = TABLE()
div.append(table)
div.append(table)
name = question['name']
if counters:
options = []
Expand All @@ -64,21 +65,21 @@ def ballot2form(ballot_model, readonly=False, vars=None, counters=None):
else:
inp = STRONG(counters.get(key, 0))
table.append(TR(TD(inp),TD(answer)))
if question['comments']:
if question['comments']:
value = readonly and vars.get(question['name']+'_comments') or ''
textarea = TEXTAREA(value, _disabled=readonly, _name=question['name']+'_comments')
ballot.append(DIV(H4('Comments'), textarea))
if not readonly and not counters:
if not readonly and not counters:
ballot.append(INPUT(_type='submit', _value="Submit Your Ballot!"))
return ballot

def form2ballot(ballot_model, token, vars, results):
def form2ballot(ballot_model, token, vars, results):
ballot_content = ballot2form(ballot_model, readonly=True, vars=vars).xml()
ballot_content = ballot_content.decode()
if token: ballot_content += '<pre>\n%s\n</pre>' % token
return '<div class="ballot">%s</div>' % ballot_content.strip()

def blank_ballot(token):
ballot_content = '<h2>Blank</h2>'
if token: ballot_content += '<pre>\n%s\n</pre>' % token
return '<div class="ballot">%s</div>' % ballot_content

24 changes: 12 additions & 12 deletions modules/ranking_algorithms.py
Expand Up @@ -20,7 +20,7 @@ def iro(votes):
# winners is a list of (v,k) = (number of preferences, option number)
# ordered from the candidate with the least preferences to the highest
winners = []
losers = set()
losers = set()
allowed_options = reduce(lambda a,b:a|b,[set(vote) for vote in votes])
n = len(allowed_options)
while len(winners)<n:
Expand All @@ -35,21 +35,21 @@ def iro(votes):
if not item in losers:
options[item] = 0
# for every ballot
for vote in votes:
for vote in votes:
# if the vote for the ballot is valid
if is_valid(vote):
# for each voting option in this balloe
for item in vote:
# if the option(candidate) have not been
# alreday discurded
if not item in losers:
# count how many ballot have this option
# count how many ballot have this option
# as first option
options[item] += 1
break

# find the option(candidate) with the least number of
# top preferences
# top preferences
options_list = [(v,k) for (k,v) in options.items()]
options_list.sort()
minv = options_list[0][0]
Expand Down Expand Up @@ -112,15 +112,15 @@ def schulze(votes):
winners = range(n)
winners.sort(lambda i,j: cmp(p[i,j],p[j,i]))
return [(i,candidates[k]) for (i,k) in enumerate(winners)]



def test(nsamples=10):
diff_iro_borda = 0
diff_iro_schulze = 0
diff_borda_schulze = 0
for k in range(nsamples):
votes = makeup_votes(10)
votes = makeup_votes(10)
a = iro(votes)
b = borda(votes,mode="exponential")
c = schulze(votes)
Expand All @@ -131,7 +131,7 @@ def test(nsamples=10):
if b[-1][1]!=c[-1][1]:
diff_borda_schulze+=1

print diff_iro_borda, diff_iro_schulze, diff_borda_schulze
print(diff_iro_borda, diff_iro_schulze, diff_borda_schulze)

def test_schulze():
votes = []
Expand All @@ -149,6 +149,6 @@ def test_schulze():
test()
test_schulze()
votes = makeup_votes(10)
print borda(votes,mode="exponential")
print iro(votes)
print schulze(votes)
print(borda(votes,mode="exponential"))
print(iro(votes))
print(schulze(votes))

0 comments on commit ab3de26

Please sign in to comment.