Display interim results
This commit is contained in:
parent
41d545d191
commit
6edca66f7e
106
app.py
106
app.py
|
@ -150,7 +150,20 @@ def vote_date():
|
|||
""",
|
||||
(session["user"]["id"], meet_id,))
|
||||
dates = csr.fetchall()
|
||||
return render_template("date_vote_fragment.html", dates=dates)
|
||||
|
||||
result = instantrunoff_forward(meet_id, "date")
|
||||
log.debug("result = %s", result)
|
||||
|
||||
return render_template("date_vote_fragment.html", dates=dates, result=result)
|
||||
|
||||
@app.get("/result/<int:meet_id>/date")
|
||||
def result_date(meet_id):
|
||||
result = instantrunoff_forward(meet_id, "date")
|
||||
log.debug("result = %s", result)
|
||||
|
||||
return render_template("date_result_fragment.html", result=result)
|
||||
|
||||
|
||||
|
||||
@app.post("/vote/time")
|
||||
def vote_time():
|
||||
|
@ -186,7 +199,18 @@ def vote_time():
|
|||
""",
|
||||
(session["user"]["id"], meet_id,))
|
||||
times = csr.fetchall()
|
||||
return render_template("time_vote_fragment.html", times=times)
|
||||
|
||||
result = instantrunoff_forward(meet_id, "time")
|
||||
log.debug("result = %s", result)
|
||||
|
||||
return render_template("time_vote_fragment.html", times=times, result=result)
|
||||
|
||||
@app.get("/result/<int:meet_id>/time")
|
||||
def result_time(meet_id):
|
||||
result = instantrunoff_forward(meet_id, "time")
|
||||
log.debug("result = %s", result)
|
||||
|
||||
return render_template("time_result_fragment.html", result=result)
|
||||
|
||||
@app.post("/vote/place")
|
||||
def vote_place():
|
||||
|
@ -222,7 +246,19 @@ def vote_place():
|
|||
""",
|
||||
(session["user"]["id"], meet_id,))
|
||||
places = csr.fetchall()
|
||||
return render_template("place_vote_fragment.html", places=places)
|
||||
|
||||
result = instantrunoff_forward(meet_id, "place")
|
||||
log.debug("result = %s", result)
|
||||
|
||||
return render_template("place_vote_fragment.html", places=places, result=result)
|
||||
|
||||
|
||||
@app.get("/result/<int:meet_id>/place")
|
||||
def result_place(meet_id):
|
||||
result = instantrunoff_forward(meet_id, "place")
|
||||
log.debug("result = %s", result)
|
||||
|
||||
return render_template("place_result_fragment.html", result=result)
|
||||
|
||||
|
||||
def send_mail(email_address, confirmation_url):
|
||||
|
@ -235,6 +271,70 @@ def send_mail(email_address, confirmation_url):
|
|||
mta = smtplib.SMTP(host="localhost")
|
||||
mta.send_message(msg)
|
||||
|
||||
def get_ballots(meet_id, kind):
|
||||
csr = get_cursor()
|
||||
|
||||
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, (meet_id,))
|
||||
|
||||
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:
|
||||
log.debug ("---")
|
||||
for r in ballot:
|
||||
log.debug(r)
|
||||
|
||||
def runoff(ballots):
|
||||
count = {}
|
||||
candidates = {}
|
||||
for ballot in ballots:
|
||||
for r in ballot:
|
||||
count[r.id] = 0
|
||||
candidates[r.id] = r
|
||||
for ballot in ballots:
|
||||
# The votes are sorted by position, so the first entry is the favourite
|
||||
count[ballot[0].id] += 1
|
||||
result = sorted(count.keys(), key=lambda i: count[i])
|
||||
log.debug("result of this round:")
|
||||
for r in result:
|
||||
log.debug(r, count[r])
|
||||
log.debug("striking %d", 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
|
||||
|
||||
def instantrunoff_forward(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)
|
||||
result.append(loser)
|
||||
result = list(reversed(result))
|
||||
log.debug("final result")
|
||||
for r in result:
|
||||
log.debug(r)
|
||||
return result
|
||||
|
||||
def get_cursor():
|
||||
db = get_db()
|
||||
csr = db.cursor(row_factory=psycopg.rows.namedtuple_row)
|
||||
|
|
|
@ -10,19 +10,31 @@ body {
|
|||
padding: 1em;
|
||||
border: #CCC 1px solid;
|
||||
border-radius: 0.2em;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.blue-background-class {
|
||||
background: #CDF;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-column-gap: 2em;
|
||||
}
|
||||
|
||||
#hello {
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
#h-day, #h-time, #h-place {
|
||||
h1, h2 {
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
padding: 1em;
|
||||
border: #CCC 1px solid;
|
||||
border-radius: 0.2em;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<div id="r-day" hx-swap-oob="true">
|
||||
{% for r in result %}
|
||||
<div class="result-item">
|
||||
{{ r.display or r.date }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
|
@ -4,3 +4,10 @@
|
|||
<input type="hidden" name="date" value="{{d.id}}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div id="r-day" hx-swap-oob="true">
|
||||
{% for r in result %}
|
||||
<div class="result-item">
|
||||
{{ r.display or r.date }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<div id="r-place" hx-swap-oob="true">
|
||||
{% for r in result %}
|
||||
<div class="result-item">
|
||||
{{ r.name }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
|
@ -4,3 +4,10 @@
|
|||
<input type="hidden" name="place" value="{{d.id}}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div id="r-place" hx-swap-oob="true">
|
||||
{% for r in result %}
|
||||
<div class="result-item">
|
||||
{{ r.name }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
<body>
|
||||
<p>Registriere dich:</p>
|
||||
<form method="POST">
|
||||
<input type="email" name="email">
|
||||
<input name="target" value="{{request.args.target}}">
|
||||
<input type="email" name="email" placeholder="Mail-Adresse">
|
||||
<input type="hidden" name="target" value="{{request.args.target}}">
|
||||
<input type="submit">
|
||||
</form>
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<div id="r-time" hx-swap-oob="true">
|
||||
{% for r in result %}
|
||||
<div class="result-item">
|
||||
{{ r.display or r.time }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
|
@ -4,3 +4,10 @@
|
|||
<input type="hidden" name="time" value="{{d.id}}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div id="r-time" hx-swap-oob="true">
|
||||
{% for r in result %}
|
||||
<div class="result-item">
|
||||
{{ r.display or r.time }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
</form>
|
||||
<div id="r-day">
|
||||
<div id="r-day" hx-get="/result/{{meet.id}}/date" hx-trigger="load">
|
||||
</div>
|
||||
<h2 id="h-time">
|
||||
Zu welcher Zeit?
|
||||
|
@ -36,7 +36,7 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
</form>
|
||||
<div id="r-time">
|
||||
<div id="r-time" hx-get="/result/{{meet.id}}/time" hx-trigger="load">
|
||||
</div>
|
||||
<h2 id="h-place">
|
||||
An welchem Ort?
|
||||
|
@ -49,7 +49,7 @@
|
|||
</div>
|
||||
{% endfor %}
|
||||
</form>
|
||||
<div id="r-place">
|
||||
<div id="r-place" hx-get="/result/{{meet.id}}/place" hx-trigger="load">
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
|
|
Loading…
Reference in New Issue