def testElectAll(self): "count a profile with nSeats candidates" b = '''2 2 4 1 0 4 2 0 2 1 0 0 "Castor" "Pollux" "test nseats candidates"''' E = Election(ElectionProfile(data=b), dict(rule='cfer')) E.count() elected = [c.name for c in E.elected] self.assertEqual(elected, ['Castor', 'Pollux']) defeated = [c.name for c in E.defeated] self.assertEqual(defeated, []) report = E.report() self.assertTrue(report.find('Elect all'))
def testDefeatBatch(self): "count a profile that has with defeat_batch option" b = '''4 2 4 1 0 3 2 0 2 3 2 0 0 "Castor" "Pollux" "Helen" "George" "test defeat-batch"''' E = Election(ElectionProfile(data=b), dict(rule="wigm", defeat_batch="zero")) E.count() elected = [c.name for c in E.elected] self.assertEqual(elected, ["Castor", "Pollux"]) defeated = [c.name for c in E.defeated] self.assertEqual(defeated, ["Helen", "George"]) report = E.report() self.assertTrue(report.find("Defeat batch"))
def testDefeatRemaining(self): "count a profile that has hopeful candidates left over to defeat" b = '''3 2 4 1 0 4 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "test defeat-remaining"''' E = Election(ElectionProfile(data=b), dict(rule='wigm-prf')) E.count() elected = [c.name for c in E.elected] self.assertEqual(elected, ['Castor', 'Pollux']) defeated = [c.name for c in E.defeated] self.assertEqual(defeated, ['Helen']) report = E.report() self.assertTrue(report.find('Defeat remaining'))
def testDefeatBatch(self): "count a profile that has with defeat_batch option" b = '''4 2 4 1 0 3 2 0 2 3 2 0 0 "Castor" "Pollux" "Helen" "George" "test defeat-batch"''' E = Election(ElectionProfile(data=b), dict(rule='wigm', defeat_batch='zero')) E.count() elected = [c.name for c in E.elected] self.assertEqual(elected, ['Castor', 'Pollux']) defeated = [c.name for c in E.defeated] self.assertEqual(defeated, ['Helen', 'George']) report = E.report() self.assertTrue(report.find('Defeat batch'))
def testNickReport(self): "using nicknames shouldn't alter dump or report" b1 = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' b2 = '''3 2 [nick a b c] 4 a b 0 2 c 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' E = Election(ElectionProfile(data=b1), dict(rule='wigm-prf')) E.count() r1 = E.report() d1 = E.dump() E = Election(ElectionProfile(data=b2), dict(rule='wigm-prf')) E.count() r2 = E.report() d2 = E.dump() self.assertEqual(r1, r2) self.assertEqual(d1, d2)
def testNickReport(self): "using nicknames shouldn't alter dump or report" b1 = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' b2 = '''3 2 [nick a b c] 4 a b 0 2 c 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' E = Election(ElectionProfile(data=b1), dict(rule='meek-prf')) E.count() r1 = E.report() d1 = E.dump() E = Election(ElectionProfile(data=b2), dict(rule='meek-prf')) E.count() r2 = E.report() d2 = E.dump() self.assertEqual(r1, r2) self.assertEqual(d1, d2)
def testRules(self): "basic test of each rule" b = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' profile = ElectionProfile(data=b) for rulename in droop.electionRuleNames(): options = dict(rule=rulename) E = Election(profile, options) self.assertTrue(E.rule.__class__.__name__ == 'Rule', 'bad rule class') self.assertTrue(len(options) >= 1, 'rule should set/leave at least one option') self.assertTrue(E.options.getopt('arithmetic') in ('fixed', 'integer', 'guarded', 'rational'), 'legal arithmetic') candidates = E.C self.assertTrue("Castor" in [c.name for c in candidates]) self.assertTrue("Castor" in [str(c) for c in candidates]) self.assertTrue(1 in [c for c in candidates]) for c in candidates: self.assertEqual(c.order, c.tieOrder) E.count() self.assertEqual(len(E.elected), E.nSeats)
class TestQpq(unittest.TestCase): ''' Create an Election instance from a simple profile and the QPQ rule and test its basic initialization, and that it elects the specified number of seats. ''' def setUp(self): "initialize profile and rule" b = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' self.Profile = ElectionProfile(data=b) self.E = Election(self.Profile, dict(rule='qpq')) def testElectionInit(self): "check that election is initialized" E = self.E self.assertTrue(E.rule.__class__.__name__ == 'Rule', 'bad rule class') self.assertEqual(len(E.options.force), 4, 'qpq should force 4 options') self.assertEqual(E.options.getopt('arithmetic'), 'guarded', 'qpq should set arithmetic=guarded') self.assertEqual(E.options.getopt('precision'), 9, 'qpq should set precision=9') self.assertEqual(E.options.getopt('guard'), 9, 'qpq should set guard=9') self.assertEqual(E.options.getopt('display'), 9, 'qpq should set display=9') self.assertEqual(E.C.byCid(1).name, "Castor") self.assertEqual(str(E.C.byCid(1)), "Castor") self.assertTrue(E.C.byCid(1) == 1) self.assertTrue(E.C.byCid(1) == '1') self.assertFalse(E.C.byCid(1) == None) def testElectionTieOrder(self): "test default tie order" for c in self.E.C: self.assertEqual(c.order, c.tieOrder) def testElectionCount1(self): "try a basic count" self.E.count() self.assertEqual(len(self.E.elected), self.E.nSeats)
def testRules(self): "basic test of each rule" b = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' profile = ElectionProfile(data=b) for rulename in droop.electionRuleNames(): options = dict(rule=rulename) E = Election(profile, options) self.assertTrue(E.rule.__class__.__name__ == 'Rule', 'bad rule class') self.assertTrue( len(options) >= 1, 'rule should set/leave at least one option') self.assertTrue( E.options.getopt('arithmetic') in ('fixed', 'integer', 'guarded', 'rational'), 'legal arithmetic') candidates = E.C self.assertTrue("Castor" in [c.name for c in candidates]) self.assertTrue("Castor" in [str(c) for c in candidates]) self.assertTrue(1 in [c for c in candidates]) for c in candidates: self.assertEqual(c.order, c.tieOrder) E.count() self.assertEqual(len(E.elected), E.nSeats)
def testReports(self): "look at election outputs" b = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' profile = ElectionProfile(data=b) rulename = droop.electionRuleNames()[0] # pick the first rule arbitrarily E = Election(profile, dict(rule=rulename)) E.count() self.assertEqual(E.report().find('interrupted'), -1) self.assertTrue(E.report(intr=True).find('interrupted') > 0) E = Election(profile, dict(rule=rulename)) E.count() self.assertEqual(E.dump().find('interrupted'), -1) self.assertTrue(E.dump(intr=True).find('interrupted') > 0) E = Election(profile, dict(rule=rulename)) E.count() self.assertEqual(E.json().find('interrupted'), -1) self.assertTrue(E.json(intr=True).find('interrupted') > 0) r = E.record() self.assertTrue(r, dict) self.assertEqual(r['actions'][-1]['tag'], 'log')
def testReports(self): "look at election outputs" b = '''3 2 4 1 2 0 2 3 0 0 "Castor" "Pollux" "Helen" "Pollux and Helen should tie"''' profile = ElectionProfile(data=b) rulename = droop.electionRuleNames()[ 0] # pick the first rule arbitrarily E = Election(profile, dict(rule=rulename)) E.count() self.assertEqual(E.report().find('interrupted'), -1) self.assertTrue(E.report(intr=True).find('interrupted') > 0) E = Election(profile, dict(rule=rulename)) E.count() self.assertEqual(E.dump().find('interrupted'), -1) self.assertTrue(E.dump(intr=True).find('interrupted') > 0) E = Election(profile, dict(rule=rulename)) E.count() self.assertEqual(E.json().find('interrupted'), -1) self.assertTrue(E.json(intr=True).find('interrupted') > 0) r = E.record() self.assertTrue(r, dict) self.assertEqual(r['actions'][-1]['tag'], 'log')
def testElectionMpls1(self): "mpls: everyone elected at first" p_mpls1 = '''3 2 4 1 2 0 4 2 1 0 1 3 0 0 "a" "b" "c" "2 elected at first"''' E = Election(ElectionProfile(data=p_mpls1), dict(rule='mpls')) E.count() self.assertEqual(len(E.elected), 2)
def doDumpCompare(options, filename, subdir=''): ''' helper: do a count and compare dump/report to reference ''' if not filename.endswith('.blt'): filename += '.blt' base, ext = os.path.splitext(filename) # pylint: disable=W0612 blt = os.path.join(testdir, 'blt', subdir, filename) E = Election(ElectionProfile(blt), options) E.count() tag = '%s-%s-%s' % (base, E.rule.tag(), E.V.tag()) def readFile(path): "read a json/dump/report file" f = open(path, 'r') data = f.read() f.close() return data def writeFile(path, data): "write a json/dump/report file" if not os.path.isdir(os.path.dirname(path)): os.makedirs(os.path.dirname(path)) f = open(path, 'w') f.write(data) f.close() # first do report # rref = os.path.join(testdir, 'ref', 'report', subdir, '%s.txt' % tag) rout = os.path.join(testdir, 'out', 'report', subdir, '%s.txt' % tag) report = E.report() if not os.path.isfile(rref): writeFile(rref, report) reportref = readFile(rref) if os.path.isfile(rout): os.unlink(rout) # don't include version number in comparison report0 = re.sub(r'droop v\d+\.\d+', 'droop v0.0', report) reportref = re.sub(r'droop v\d+\.\d+', 'droop v0.0', reportref) if report0 != reportref: writeFile(rout, report) if compare_report: return False # same logic with json # sref = os.path.join(testdir, 'ref', 'json', subdir, '%s.txt' % tag) sout = os.path.join(testdir, 'out', 'json', subdir, '%s.txt' % tag) json = E.json() if not os.path.isfile(sref): writeFile(sref, json) jsonref = readFile(sref) if os.path.isfile(sout): os.unlink(sout) # don't include version number in comparison json0 = re.sub(r'"droop_version": "\d+\.\d+"', '"droop_version": "0.0"', json) jsonref = re.sub(r'"droop_version": "\d+\.\d+"', '"droop_version": "0.0"', jsonref) if json0 != jsonref: writeFile(sout, json) if compare_json: return False # same logic with dump # dref = os.path.join(testdir, 'ref', 'dump', subdir, '%s.txt' % tag) dout = os.path.join(testdir, 'out', 'dump', subdir, '%s.txt' % tag) dump = E.dump() if not os.path.isfile(dref): writeFile(dref, dump) dumpref = readFile(dref) if os.path.isfile(dout): os.unlink(dout) if dump != dumpref: writeFile(dout, dump) if compare_dump: return False return True
def getDump(options, base): "run a count and return the dump" blt = '%s/blt/%s.blt' % (testdir, base) E = Election(ElectionProfile(blt), options) E.count() return E.dump()
def doCount(options, blt): "run the count and return the Election" p = ElectionProfile(testdir + '/blt/' + blt) E = Election(p, options) E.count() return E
def testElectionQpq1(self): "qpq: everyone elected at first" b = '''3 2 4 1 2 0 4 2 1 0 1 3 0 0 "a" "b" "c" "2 elected at first"''' E = Election(ElectionProfile(data=b), dict(rule='qpq')) E.count() self.assertEqual(len(E.elected), 2)
def doQpqCount(filename): "each of five elections from the Woodall paper" blt = os.path.join(testdir, 'blt', 'qpq', filename) E = Election(ElectionProfile(blt), dict(rule='qpq')) E.count() return E
def calc_winners(self): # Import here to avoid loop in loading from politicians.models import Politician r = self # All ballots for a riding all_ballots = r.ballots() # All ballots for the calculation # TODO: Should this filtering move to the Riding class or Ballot class? calculation_ballots = all_ballots.filter(state='C').filter(spoiled=False) # All spoiled ballots # TODO: Should this filtering move to the Riding class or Ballot class? num_spoiled_ballots = r.num_spoiled_ballots() # All candidates for the riding c = self.candidates() # Get distinct ballot contents and how many times they occured b2 = calculation_ballots.values("vote").annotate(cnt=Count('vote')) # Dictionary of (key=droop candidate ID, value=politician.id) c2 = dict((i+1,v.id) for i, v in enumerate(list(c))) # Dictionary of (key=politician.id, value=droop candidate ID) c2b = dict((v,k) for k,v in c2.iteritems()) fc_votes = {} for politician in c: fc_votes[politician.id] = 0 # More sanity if r.num_seats < 1: raise DroopElectionProfileError("Too few Seats in " + r.name) if r.num_candidates() < r.num_seats: raise DroopElectionProfileError("Too few Candidates in " + r.name + ". " + str(r.num_seats) + " seats for " + str(r.num_candidates()) + " candidates.") #check if the number of ballots is enough for BCSTV if calculation_ballots.count() < (r.num_candidates() + 1): raise DroopElectionProfileError("Too few ballots to calculate BCSTV") # Start of BLT generation # Number of candidates, Number of seats data = str(c.count()) + " " + str(r.num_seats) + "\n" # For each distinct ballot content for ballot in b2: # Count of timesro_home.html data = data + str(ballot['cnt']) + " " # Content of ballot vote_line = json.loads(ballot['vote']) first = True for _i, _c in vote_line.iteritems(): # Of the droop ID numbers for the candidate if _c == "": # If empty, skip (empty line on ballot) pass else: data = data + str(c2b[int(_c)]) + " " if first: fc_votes[int(_c)] += ballot['cnt'] first = False # 0 to say no more candidates on ballot data = data + "0\n" # 0 to say no more ballots data = data + "0\n" # candidates in droop order for key, candidate in c2.iteritems(): data = data + "\"" + unicode(candidate) + "\"\n" # Name of election data = data + "\"" + r.name + " Results\"" # End of BLT generation try: E = DroopElection(DroopElectionProfile(data=data.encode('ascii', 'ignore')), dict(rule='bcstv')) except DroopElectionProfileError as e: raise e E.count() result = E.record() candidate_states = {} for i in range(len(result['actions'][-1]['cstate'])): temp = {} k = result['cdict'][i+1]['name'] pol = Politician.objects.get(id = k) temp['droop_cstate'] = result['actions'][-1]['cstate'][i+1] temp['first_choice_votes'] = fc_votes[int(k)] temp['droop_id'] = i temp['cand_id'] = k candidate_states[pol] = temp return { 'E': E, 'candidate_states': candidate_states, 'result': result, 'candidates': c, 'num_spoiled_ballots': num_spoiled_ballots, 'nballots': result['nballots'], 'riding': self, }