diff --git a/app.py b/app.py index c679ea4..a1538ce 100644 --- a/app.py +++ b/app.py @@ -173,7 +173,7 @@ def vote_date(): (session["user"]["id"], meet_id,)) dates = csr.fetchall() - result = instantrunoff_forward(meet_id, "date") + result = instantrunoff_backward(meet_id, "date") log.debug("result = %s", result) csr.execute( @@ -194,7 +194,7 @@ def vote_date(): @app.get("/result//date") def result_date(meet_id): - result = instantrunoff_forward(meet_id, "date") + result = instantrunoff_backward(meet_id, "date") log.debug("result = %s", result) csr = get_cursor() @@ -251,7 +251,7 @@ def vote_time(): (session["user"]["id"], meet_id,)) times = csr.fetchall() - result = instantrunoff_forward(meet_id, "time") + result = instantrunoff_backward(meet_id, "time") log.debug("result = %s", result) csr.execute( @@ -272,7 +272,7 @@ def vote_time(): @app.get("/result//time") def result_time(meet_id): - result = instantrunoff_forward(meet_id, "time") + result = instantrunoff_backward(meet_id, "time") log.debug("result = %s", result) csr = get_cursor() @@ -327,7 +327,7 @@ def vote_place(): (session["user"]["id"], meet_id,)) places = csr.fetchall() - result = instantrunoff_forward(meet_id, "place") + result = instantrunoff_backward(meet_id, "place") log.debug("result = %s", result) csr.execute( @@ -349,7 +349,7 @@ def vote_place(): @app.get("/result//place") def result_place(meet_id): - result = instantrunoff_forward(meet_id, "place") + result = instantrunoff_backward(meet_id, "place") log.debug("result = %s", result) csr = get_cursor() @@ -373,7 +373,7 @@ def result_place(meet_id): @app.get("/result//date/ballot") def result_ballots_date(meet_id): ballots = get_ballots(meet_id, "date") - result = instantrunoff_forward(meet_id, "date") + result = instantrunoff_backward(meet_id, "date") return render_template( "date_result_ballots.html", @@ -382,7 +382,7 @@ def result_ballots_date(meet_id): @app.get("/result//time/ballot") def result_ballots_time(meet_id): ballots = get_ballots(meet_id, "time") - result = instantrunoff_forward(meet_id, "time") + result = instantrunoff_backward(meet_id, "time") return render_template( "time_result_ballots.html", @@ -391,7 +391,7 @@ def result_ballots_time(meet_id): @app.get("/result//place/ballot") def result_ballots_place(meet_id): ballots = get_ballots(meet_id, "place") - result = instantrunoff_forward(meet_id, "place") + result = instantrunoff_backward(meet_id, "place") return render_template( "place_result_ballots.html", @@ -412,7 +412,8 @@ def get_ballots(meet_id, kind): csr = get_cursor() 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} join {kind}_vote on {kind}.id = {kind}_vote.{kind} join bod on bod = bod.id @@ -437,7 +438,7 @@ def dump_ballots(ballots): for r in ballot: log.debug(r) -def runoff(ballots): +def runoff(ballots, direction): count = {} candidates = {} for ballot in ballots: @@ -447,13 +448,17 @@ def runoff(ballots): count[r.id] = [0] * len(ballot) candidates[r.id] = r for ballot in ballots: + weight = max(r.vote_w for r in ballot) for pos, r in enumerate(ballot): log.debug("count = %s", count) log.debug("r.id = %s", r.id) 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]) - 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:") for r in result: log.debug("%s %s", r, count[r]) @@ -472,7 +477,21 @@ def instantrunoff_forward(meet_id, kind): result = [] while max(len(b) for b in 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 = list(reversed(result)) log.debug("final result") diff --git a/utils/instantrunoff b/utils/instantrunoff index 776539a..0d5deba 100755 --- a/utils/instantrunoff +++ b/utils/instantrunoff @@ -7,6 +7,7 @@ import psycopg.rows def get_args(): ap = argparse.ArgumentParser() + ap.add_argument("--reverse", action="store_true") ap.add_argument("meet", type=int) ap.add_argument("kind") @@ -20,7 +21,8 @@ def get_ballots(): kind = args.kind 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} where meet = %s order by bod, position @@ -53,9 +55,13 @@ def runoff_forward(ballots): count[r.id] = [0] * len(ballot) candidates[r.id] = r for ballot in ballots: + weight = max(r.vote_w for r in ballot) for pos, r in enumerate(ballot): - count[r.id][pos] += 1 - result = sorted(count.keys(), key=lambda i: count[i]) + count[r.id][pos] += weight + 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:") for r in result: print(r, count[r])