Mercurial > hg > elinux-vote
view vote-counter.py @ 5:58e4b36d5e74
Clean up the code
author | "German Poo-Caaman~o <gpoo@gnome.org>" |
---|---|
date | Thu, 14 Dec 2006 10:22:46 -0300 |
parents | ffa9fad76d38 |
children | 80df109584f7 |
line wrap: on
line source
#! /usr/bin/python # # Usage: python vote-counter.py mbox secret-cookie addresses import re import sys import string import md5 MAX_CANDIDATES = 1 candidate_tuples = [ \ ("UNIVERSIDAD TECNICA FEDERICO SANTA MARIA", 1), \ ("UNIVERSIDAD DE CONCEPCION", 2) ] class Ballot: def __init__ (self): self.email = 0 self.member_name = 0 self.member = 0 self.token = 0 self.votes = [] def add_vote(self, name, id): self.votes.append((name, id)) class Candidate: def __init__(self, name, id): self.name = name self.id = id self.count = 0 self.voters = [] # hash from valid addresses to whether they have sent in a ballot yet class Voters(dict): def load_from_file(self, filename): def unmunge_email(addr): unmunged = nospam_re.sub("", addr) return unmunged nospam_re = re.compile("no_spam\.?") comment_re = re.compile("^#.*") entry_re = re.compile(" *(.*?)(<.*?>) *\((.*?)\) *") fp = open(filename) for line in fp.readlines(): line = comment_re.sub("", line) string.strip(line) if line == "" or line == "\n": continue match = entry_re.search(line) if match: name = string.strip(match.group(1)) email = unmunge_email(string.strip(match.group(2))) contribution = string.strip(match.group(3)) self[string.strip(email)] = 0 else: print "No match: " + line fp.close() def valid_voter(self, addr): return self.has_key(addr) def get_ballots_from_mbox(filename, candidates): ballots = [] current_ballot = 0 from_line_re = re.compile ("^From: *(.*)") member_name_re = re.compile (">? *Member: *(.*)") member_address_re = re.compile (">? *Member Address: *([^ ]*)") auth_token_re = re.compile (">? *Validation Token: *(.*)") vote_re = re.compile (">? *([A-Z ]+) *\(ID# *([0-9]+)\)") fp = open(filename) for line in fp.readlines(): match = from_line_re.match(line) if match: email = string.strip(match.group(1)) if current_ballot: ballots.append(current_ballot) current_ballot = Ballot() current_ballot.email = email continue match = member_name_re.match(line) if match: member = string.strip(match.group(1)) if (current_ballot.member_name): print "Duplicate member name in ballot from '%s' - " \ "duplicates ''%s', '%s'" \ % (current_ballot.email, current_ballot.member_name, member) else: current_ballot.member_name = member continue match = member_address_re.match(line) if match: member = string.strip(match.group(1)) if (current_ballot.member): print "Duplicate member address in ballot from '%s' - " \ "duplicates ''%s', '%s'" \ % (current_ballot.email, current_ballot.member, member) else: current_ballot.member = member continue match = auth_token_re.match(line) if match: token = string.strip(match.group(1)) if (current_ballot.token): print "Duplicate auth token in ballot from '%s' - " \ "duplicates '%s', '%s'" \ % (current_ballot.email, current_ballot.token, token) else: current_ballot.token = token continue match = vote_re.match(line) if match: name = string.strip(match.group(1)) id = string.strip(match.group(2)) id = int(id) if not candidates.has_key(id): print "Unknown candidate '%s' ID %d in ballot from '%s'" \ % (name, id, current_ballot.email) elif not candidates[id].name == name: print "Candidate name '%s' for ID '%s' doesn't match, " \ "expected '%s'" % (name, id, candidates[id].name) else: current_ballot.add_vote(name, id) continue if current_ballot: ballots.append(current_ballot) fp.close () return ballots def check_ballots(ballots, valid_addressess): valid_ballots = {} def contains_dups (b): dups = {} for (name, id) in b.votes: if dups.has_key (id): return True dups[id] = 1 return False dup_tokens = {} def md5_is_bad(b): #key = b.member + secret_cookie key = "%s%s" % (b.member, secret_cookie) m = md5.new(key) digest = m.digest() token = m.hexdigest() if token == b.token: if dup_tokens.has_key(token): print "Auth token occurs twice, someone voted more than once" return True else: dup_tokens[token] = 1 return False else: print "Bad auth token is %s hashed from '%s'" % (token, key) return True i = 0 for b in ballots: error = 0 if not b.member: error = "missing member address" elif not b.token: error = "missing auth token" elif len(b.votes) > MAX_CANDIDATES: error = "too many votes (%d votes)" % len(b.votes) elif len(b.votes) == 0: error = "didn't list any candidates" elif contains_dups(b): error = "contains duplicate votes for the same candidate" elif md5_is_bad(b): error = "bad authentication token" elif not valid_addresses.valid_voter(b.member): error = "ballot from someone not in the list of valid voters" else: if valid_ballots.has_key(b.token): old = valid_ballots[b.token] print "Overriding previous valid ballot %d from %s with " \ "new ballot %d" % (old[1], old[0].email, i) valid_ballots[b.token] = (b, i) if error: print "Ignoring ballot %d from '%s' due to: %s" \ % (i, b.email, error) i = i + 1 return valid_ballots def print_valid_ballots(valid_ballots, valid_addresses, candidates): valids = valid_ballots.values() valids.sort(lambda a, b: cmp(a[1], b[1])) for (b, i) in valids: print "Ballot %d:" % i print " From: " + b.email print " Member: " + b.member_name print " Member Address: " + b.member print " Token: " + b.token print " Voted for %d candidates:" % len(b.votes) voted_for = [] valid_addresses[b.member] = 1 for (name, id) in b.votes: candidates[id].count += 1 candidates[id].voters.append(b.member) voted_for.append(candidates[id].name) for v in voted_for: print " " + v def print_voters_without_ballot(valid_addresses): print "\nThe following members did not vote:" for addr in valid_addresses.keys (): if not valid_addresses[addr]: print addr def print_summary(valid_ballots, valid_addresses, candidates): candidate_list = candidates.values() candidate_list.sort(lambda a, b: cmp(b.count, a.count)) print "\n\nELECTION RESULTS:" print " %d of %d members cast a valid ballot" \ % (len(valid_ballots), len(valid_addresses.keys())) for candidate in candidate_list: print " %s (%d votes)" % (candidate.name, candidate.count) if __name__ == '__main__': candidates = {} for (name, id) in candidate_tuples: candidates[id] = Candidate(name, id) try: mbox_filename = sys.argv[1] # mail archive file secret_cookie = sys.argv[2] # secret cookie voter_filename = sys.argv[3] # valid voter addresses file except: print "Usage: %s mbox secret-cookie list-of-voters" % sys.argv[0] sys.exit(1) valid_addresses = Voters() valid_addresses.load_from_file(voter_filename) ballots = get_ballots_from_mbox(mbox_filename, candidates) valid_ballots = check_ballots(ballots, valid_addresses) ## Print results only after all errors have been printed, so ## we don't lose any errors. print_valid_ballots(valid_ballots, valid_addresses, candidates) print_voters_without_ballot(valid_addresses) print_summary(valid_ballots, valid_addresses, candidates) # vi:set ts=4 sw=4 expandtab: