meeat/utils/instantrunoff

87 lines
2.1 KiB
Plaintext
Raw Normal View History

2022-11-07 21:06:14 +01:00
#!/usr/bin/python3
import argparse
import psycopg
import psycopg.rows
def get_args():
ap = argparse.ArgumentParser()
2023-05-12 21:38:52 +02:00
ap.add_argument("--reverse", action="store_true")
2022-11-07 21:06:14 +01:00
ap.add_argument("meet", type=int)
ap.add_argument("kind")
args = ap.parse_args()
return args
def get_ballots():
db = psycopg.connect(dbname="meeat", user="www-meeat")
csr = db.cursor(row_factory=psycopg.rows.namedtuple_row)
kind = args.kind
q = f"""
select {kind}.*, bod, position
from {kind} join {kind}_vote on {kind}.id = {kind}_vote.{kind}
where meet = %s
order by bod, position
"""
csr.execute(q, (args.meet,))
last_bod = None
ballots = []
for r in csr:
if r.bod != last_bod:
ballot = []
ballots.append(ballot)
last_bod = r.bod
ballot.append(r)
return ballots
def dump_ballots(ballots):
for ballot in ballots:
print ("---")
for r in ballot:
print(r)
def runoff(ballots):
count = {}
candidates = {}
for ballot in ballots:
for r in ballot:
count[r.id] = [0] * len(ballot)
2022-11-07 21:06:14 +01:00
candidates[r.id] = r
for ballot in ballots:
for pos, r in enumerate(ballot):
count[r.id][pos] += 1
2023-05-12 21:38:52 +02:00
if args.reverse:
result = sorted(count.keys(), key=lambda i: list(reversed(count[i])), reverse=True)
else:
result = sorted(count.keys(), key=lambda i: count[i])
2022-11-07 21:06:14 +01:00
print("result of this round:")
for r in result:
print(r, count[r])
print("striking", result[0])
loser = candidates[result[0]]
new_ballots = [
[
r for r in ballot if r.id != loser.id
] for ballot in ballots
]
return loser, new_ballots
if __name__ == "__main__":
args = get_args()
ballots = get_ballots()
result = []
while max(len(b) for b in ballots):
dump_ballots(ballots)
loser, ballots = runoff(ballots)
result.append(loser)
result = reversed(result)
print("final result")
for r in result:
print(r)