163 lines
3.5 KiB
Python
163 lines
3.5 KiB
Python
import hashlib
|
|
from datetime import date, datetime, time, timedelta
|
|
|
|
from flask import Blueprint, redirect, render_template, request, url_for
|
|
|
|
from .database import get_db
|
|
|
|
bp = Blueprint("public", __name__)
|
|
|
|
VOTE_COOKIE_NAME = "wyr_daily_vote"
|
|
|
|
|
|
def get_todays_question():
|
|
db = get_db()
|
|
today = date.today().isoformat()
|
|
|
|
return db.execute(
|
|
"""
|
|
SELECT id, question, option_a, option_b, date
|
|
FROM questions
|
|
WHERE date = ?
|
|
LIMIT 1
|
|
""",
|
|
(today,),
|
|
).fetchone()
|
|
|
|
|
|
def get_question_version(question):
|
|
parts = (
|
|
str(question["id"]),
|
|
question["date"],
|
|
question["question"],
|
|
question["option_a"],
|
|
question["option_b"],
|
|
)
|
|
fingerprint = "\x1f".join(parts)
|
|
|
|
return hashlib.sha256(fingerprint.encode()).hexdigest()
|
|
|
|
|
|
def get_vote_cookie_value(question):
|
|
return f"{question['date']}:{question['id']}:{get_question_version(question)}"
|
|
|
|
|
|
def get_seconds_until_tomorrow():
|
|
now = datetime.now()
|
|
tomorrow = datetime.combine(date.today() + timedelta(days=1), time.min)
|
|
|
|
return max(1, int((tomorrow - now).total_seconds()))
|
|
|
|
|
|
def has_voted_for_question(question):
|
|
return request.cookies.get(VOTE_COOKIE_NAME) == get_vote_cookie_value(question)
|
|
|
|
|
|
def set_vote_cookie(response, question):
|
|
response.set_cookie(
|
|
VOTE_COOKIE_NAME,
|
|
get_vote_cookie_value(question),
|
|
max_age=get_seconds_until_tomorrow(),
|
|
httponly=True,
|
|
samesite="Lax",
|
|
)
|
|
|
|
return response
|
|
|
|
|
|
@bp.route("/")
|
|
def home():
|
|
question = get_todays_question()
|
|
|
|
return render_template(
|
|
"public/index.html",
|
|
question=question,
|
|
)
|
|
|
|
|
|
@bp.route("/vote", methods=["POST"])
|
|
def vote():
|
|
qid = request.form.get("qid")
|
|
selected_vote = request.form.get("vote")
|
|
|
|
if not qid or selected_vote not in {"A", "B"}:
|
|
return redirect(url_for("public.home"))
|
|
|
|
db = get_db()
|
|
question = get_todays_question()
|
|
|
|
if question is None or str(question["id"]) != str(qid):
|
|
return redirect(url_for("public.home"))
|
|
|
|
if has_voted_for_question(question):
|
|
return redirect(url_for("public.results"))
|
|
|
|
db.execute(
|
|
"""
|
|
INSERT INTO votes (question_id, vote, voter_hash)
|
|
VALUES (?, ?, ?)
|
|
""",
|
|
(qid, selected_vote, "cookie"),
|
|
)
|
|
db.commit()
|
|
|
|
response = redirect(url_for("public.results"))
|
|
return set_vote_cookie(response, question)
|
|
|
|
|
|
@bp.route("/results")
|
|
def results():
|
|
question = get_todays_question()
|
|
|
|
if question is None:
|
|
return render_template(
|
|
"public/results.html",
|
|
question=None,
|
|
votes_a=0,
|
|
votes_b=0,
|
|
percent_a=0,
|
|
percent_b=0,
|
|
total=0,
|
|
)
|
|
|
|
db = get_db()
|
|
|
|
votes_a = db.execute(
|
|
"""
|
|
SELECT COUNT(*)
|
|
FROM votes
|
|
WHERE question_id = ?
|
|
AND vote = 'A'
|
|
""",
|
|
(question["id"],),
|
|
).fetchone()[0]
|
|
|
|
votes_b = db.execute(
|
|
"""
|
|
SELECT COUNT(*)
|
|
FROM votes
|
|
WHERE question_id = ?
|
|
AND vote = 'B'
|
|
""",
|
|
(question["id"],),
|
|
).fetchone()[0]
|
|
|
|
total = votes_a + votes_b
|
|
|
|
if total == 0:
|
|
percent_a = 0
|
|
percent_b = 0
|
|
else:
|
|
percent_a = round(votes_a / total * 100)
|
|
percent_b = round(votes_b / total * 100)
|
|
|
|
return render_template(
|
|
"public/results.html",
|
|
question=question,
|
|
votes_a=votes_a,
|
|
votes_b=votes_b,
|
|
percent_a=percent_a,
|
|
percent_b=percent_b,
|
|
total=total,
|
|
)
|