changeset 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
files vote-counter.py
diffstat 1 files changed, 215 insertions(+), 210 deletions(-) [+]
line wrap: on
line diff
--- a/vote-counter.py	Thu Dec 07 18:41:27 2006 -0300
+++ b/vote-counter.py	Thu Dec 14 10:22:46 2006 -0300
@@ -1,4 +1,6 @@
 #! /usr/bin/python
+#
+# Usage: python vote-counter.py mbox secret-cookie addresses
 
 import re
 import sys
@@ -8,8 +10,8 @@
 MAX_CANDIDATES = 1
 
 candidate_tuples = [ \
-	("UNIVERSIDAD TECNICA FEDERICO SANTA MARIA", 1), \
-	("UNIVERSIDAD DE CONCEPCION", 2) ]
+    ("UNIVERSIDAD TECNICA FEDERICO SANTA MARIA", 1), \
+    ("UNIVERSIDAD DE CONCEPCION", 2) ]
 
 class Ballot:
     def __init__ (self):
@@ -23,254 +25,257 @@
         self.votes.append((name, id))
 
 class Candidate:
-    def __init__ (self, name, id):
+    def __init__(self, name, id):
         self.name = name
         self.id = id
         self.count = 0
-	self.voters = []
-        
-def get_voters(filename):
-	# hash from valid addresses to whether they have sent in a ballot yet
-	addresses = {}
+        self.voters = []
 
-	nospam_re = re.compile("no_spam\.?")
-	comment_re = re.compile("^#.*")
-	entry_re = re.compile(" *(.*?)(<.*?>) *\((.*?)\) *")
+# 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
 
-	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)
+        fp = open(filename)
 
-	for line in fp.readlines():
-		line = comment_re.sub("", line)
-		string.strip(line)
-		if line == "" or line == "\n":
-			continue
+        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))
-			addresses[string.strip(email)] = 0
-		else:
-			print "No match: " + line
+            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()
+        fp.close()
 
-	return addresses
+    def valid_voter(self, addr):
+        return self.has_key(addr)
 
 def get_ballots_from_mbox(filename, candidates):
-	ballots = []
-	current_ballot = 0
+    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]+)\)")
 
-	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
 
-	handle = open(filename)
-	for line in handle.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
+        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_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 = 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
 
-			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)
 
-		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 ()
 
-			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)    
-        
-	handle.close ()
+    return ballots
+
+def check_ballots(ballots, valid_addressess):
+    valid_ballots = {}
 
-	return ballots
-
-def valid_voter(addr):
-    return valid_addresses.has_key(addr)
-
-def print_error(ballots):
-	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
 
-	def contains_dups (b):
-		dups = {}
-		for v in b.votes:
-			id = v[1]
-			if dups.has_key (id):
-				return 1
-			dups[id] = 1
-		return 0
+    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
 
-	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 1
-			else:
-				dup_tokens[token] = 1
-			return 0
-		else:
-			print "Bad auth token is %s hashed from '%s'" % (token, key)
-			return 1
-
-	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_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)
+    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
+        if error:
+            print "Ignoring ballot %d from '%s' due to: %s" \
+                  % (i, b.email, error)
+            
+        i = i + 1
 
-	return valid_ballots
+    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]))
 
-def print_valid_votes(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
+    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)
+        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 = []
+        voted_for = []
 
-		valid_addresses[b.member] = 1
+        valid_addresses[b.member] = 1
 
-		for v in b.votes:
-			id = v[1]
-			candidates[id].count = candidates[id].count + 1
-			candidates[id].voters.append (b.member)
-			voted_for.append(candidates[id].name)
+        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
-
-	return valids
+        for v in voted_for:
+            print "   " + v
 
-def print_no_votes(valid_addresses):
-	print "The following members did not vote:"
-	for addr in valid_addresses.keys ():
-		if not valid_addresses[addr]:
-			print addr
+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_addresses, candidates, valids):
-	candidate_list = candidates.values()
-	candidate_list.sort(lambda a, b: cmp(b.count, a.count))
+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 "\n\nELECTION RESULTS:"
 
-	print " %d of %d members cast a valid ballot" \
-		  % (len(valids), len(valid_addresses.keys()))
+    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)
+    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)
+    candidates = {}
+    for (name, id) in candidate_tuples:
+        candidates[id] = Candidate(name, id)
 
-	mbox_filename = sys.argv[1]     # mail archive file 
-	secret_cookie = sys.argv[2]     # secret cookie
-	voter_filename = sys.argv[3]    # valid voter addresses file
+    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 = get_voters(voter_filename)
-	ballots = get_ballots_from_mbox(mbox_filename, candidates)
+    valid_addresses = Voters()
+    valid_addresses.load_from_file(voter_filename)
 
-	valid_ballots = print_error(ballots)
+    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.
-	valids = print_valid_votes(valid_ballots, valid_addresses, candidates)
-	print_no_votes(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_addresses, candidates, valids)
+    print_summary(valid_ballots, valid_addresses, candidates)
+
+# vi:set ts=4 sw=4 expandtab: