Merge branch 'master' of git.hjp.at:hjp/meeat

This commit is contained in:
Peter J. Holzer 2023-10-04 18:05:26 +02:00
commit a147032c61
2 changed files with 42 additions and 17 deletions

47
app.py
View File

@ -173,7 +173,7 @@ def vote_date():
(session["user"]["id"], meet_id,)) (session["user"]["id"], meet_id,))
dates = csr.fetchall() dates = csr.fetchall()
result = instantrunoff_forward(meet_id, "date") result = instantrunoff_backward(meet_id, "date")
log.debug("result = %s", result) log.debug("result = %s", result)
csr.execute( csr.execute(
@ -194,7 +194,7 @@ def vote_date():
@app.get("/result/<int:meet_id>/date") @app.get("/result/<int:meet_id>/date")
def result_date(meet_id): def result_date(meet_id):
result = instantrunoff_forward(meet_id, "date") result = instantrunoff_backward(meet_id, "date")
log.debug("result = %s", result) log.debug("result = %s", result)
csr = get_cursor() csr = get_cursor()
@ -251,7 +251,7 @@ def vote_time():
(session["user"]["id"], meet_id,)) (session["user"]["id"], meet_id,))
times = csr.fetchall() times = csr.fetchall()
result = instantrunoff_forward(meet_id, "time") result = instantrunoff_backward(meet_id, "time")
log.debug("result = %s", result) log.debug("result = %s", result)
csr.execute( csr.execute(
@ -272,7 +272,7 @@ def vote_time():
@app.get("/result/<int:meet_id>/time") @app.get("/result/<int:meet_id>/time")
def result_time(meet_id): def result_time(meet_id):
result = instantrunoff_forward(meet_id, "time") result = instantrunoff_backward(meet_id, "time")
log.debug("result = %s", result) log.debug("result = %s", result)
csr = get_cursor() csr = get_cursor()
@ -327,7 +327,7 @@ def vote_place():
(session["user"]["id"], meet_id,)) (session["user"]["id"], meet_id,))
places = csr.fetchall() places = csr.fetchall()
result = instantrunoff_forward(meet_id, "place") result = instantrunoff_backward(meet_id, "place")
log.debug("result = %s", result) log.debug("result = %s", result)
csr.execute( csr.execute(
@ -349,7 +349,7 @@ def vote_place():
@app.get("/result/<int:meet_id>/place") @app.get("/result/<int:meet_id>/place")
def result_place(meet_id): def result_place(meet_id):
result = instantrunoff_forward(meet_id, "place") result = instantrunoff_backward(meet_id, "place")
log.debug("result = %s", result) log.debug("result = %s", result)
csr = get_cursor() csr = get_cursor()
@ -373,7 +373,7 @@ def result_place(meet_id):
@app.get("/result/<int:meet_id>/date/ballot") @app.get("/result/<int:meet_id>/date/ballot")
def result_ballots_date(meet_id): def result_ballots_date(meet_id):
ballots = get_ballots(meet_id, "date") ballots = get_ballots(meet_id, "date")
result = instantrunoff_forward(meet_id, "date") result = instantrunoff_backward(meet_id, "date")
return render_template( return render_template(
"date_result_ballots.html", "date_result_ballots.html",
@ -382,7 +382,7 @@ def result_ballots_date(meet_id):
@app.get("/result/<int:meet_id>/time/ballot") @app.get("/result/<int:meet_id>/time/ballot")
def result_ballots_time(meet_id): def result_ballots_time(meet_id):
ballots = get_ballots(meet_id, "time") ballots = get_ballots(meet_id, "time")
result = instantrunoff_forward(meet_id, "time") result = instantrunoff_backward(meet_id, "time")
return render_template( return render_template(
"time_result_ballots.html", "time_result_ballots.html",
@ -391,7 +391,7 @@ def result_ballots_time(meet_id):
@app.get("/result/<int:meet_id>/place/ballot") @app.get("/result/<int:meet_id>/place/ballot")
def result_ballots_place(meet_id): def result_ballots_place(meet_id):
ballots = get_ballots(meet_id, "place") ballots = get_ballots(meet_id, "place")
result = instantrunoff_forward(meet_id, "place") result = instantrunoff_backward(meet_id, "place")
return render_template( return render_template(
"place_result_ballots.html", "place_result_ballots.html",
@ -412,7 +412,8 @@ def get_ballots(meet_id, kind):
csr = get_cursor() csr = get_cursor()
q = f""" q = f"""
select {kind}.*, bod, position, email select {kind}.*, bod, position, email,
min(w) over(partition by bod order by position) as vote_w
from {kind} from {kind}
join {kind}_vote on {kind}.id = {kind}_vote.{kind} join {kind}_vote on {kind}.id = {kind}_vote.{kind}
join bod on bod = bod.id join bod on bod = bod.id
@ -437,7 +438,7 @@ def dump_ballots(ballots):
for r in ballot: for r in ballot:
log.debug(r) log.debug(r)
def runoff(ballots): def runoff(ballots, direction):
count = {} count = {}
candidates = {} candidates = {}
for ballot in ballots: for ballot in ballots:
@ -447,13 +448,17 @@ def runoff(ballots):
count[r.id] = [0] * len(ballot) count[r.id] = [0] * len(ballot)
candidates[r.id] = r candidates[r.id] = r
for ballot in ballots: for ballot in ballots:
weight = max(r.vote_w for r in ballot)
for pos, r in enumerate(ballot): for pos, r in enumerate(ballot):
log.debug("count = %s", count) log.debug("count = %s", count)
log.debug("r.id = %s", r.id) log.debug("r.id = %s", r.id)
log.debug("pos = %s", pos) log.debug("pos = %s", pos)
count[r.id][pos] += 1 count[r.id][pos] += weight
log.debug("count[%d][%d]) = %d", r.id, pos, count[r.id][pos]) log.debug("count[%d][%d]) = %d", r.id, pos, count[r.id][pos])
result = sorted(count.keys(), key=lambda i: count[i]) if direction == "backward":
result = sorted(count.keys(), key=lambda i: list(reversed(count[i])), reverse=True)
else:
result = sorted(count.keys(), key=lambda i: count[i])
log.debug("result of this round:") log.debug("result of this round:")
for r in result: for r in result:
log.debug("%s %s", r, count[r]) log.debug("%s %s", r, count[r])
@ -472,7 +477,21 @@ def instantrunoff_forward(meet_id, kind):
result = [] result = []
while max(len(b) for b in ballots): while max(len(b) for b in ballots):
dump_ballots(ballots) dump_ballots(ballots)
loser, ballots = runoff(ballots) loser, ballots = runoff(ballots, "forward")
result.append(loser)
result = list(reversed(result))
log.debug("final result")
for r in result:
log.debug(r)
return result
def instantrunoff_backward(meet_id, kind):
ballots = get_ballots(meet_id, kind)
result = []
while max(len(b) for b in ballots):
dump_ballots(ballots)
loser, ballots = runoff(ballots, "backward")
result.append(loser) result.append(loser)
result = list(reversed(result)) result = list(reversed(result))
log.debug("final result") log.debug("final result")

View File

@ -7,6 +7,7 @@ import psycopg.rows
def get_args(): def get_args():
ap = argparse.ArgumentParser() ap = argparse.ArgumentParser()
ap.add_argument("--reverse", action="store_true")
ap.add_argument("meet", type=int) ap.add_argument("meet", type=int)
ap.add_argument("kind") ap.add_argument("kind")
@ -20,7 +21,8 @@ def get_ballots():
kind = args.kind kind = args.kind
q = f""" q = f"""
select {kind}.*, bod, position select {kind}.*, bod, position,
min(w) over(partition by bod order by position) as vote_w
from {kind} join {kind}_vote on {kind}.id = {kind}_vote.{kind} from {kind} join {kind}_vote on {kind}.id = {kind}_vote.{kind}
where meet = %s where meet = %s
order by bod, position order by bod, position
@ -53,9 +55,13 @@ def runoff_forward(ballots):
count[r.id] = [0] * len(ballot) count[r.id] = [0] * len(ballot)
candidates[r.id] = r candidates[r.id] = r
for ballot in ballots: for ballot in ballots:
weight = max(r.vote_w for r in ballot)
for pos, r in enumerate(ballot): for pos, r in enumerate(ballot):
count[r.id][pos] += 1 count[r.id][pos] += weight
result = sorted(count.keys(), key=lambda i: count[i]) 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])
print("result of this round:") print("result of this round:")
for r in result: for r in result:
print(r, count[r]) print(r, count[r])