import json
import os
from datetime import datetime, timedelta
from json import JSONDecodeError
from flask import Flask, abort, jsonify, render_template, request
from pytz import timezone
import tournament
from common.course_config import get_endpoint
from common.db import connect_db
from contest_utils.oauth import get_group
from contest_utils.rate_limiting import ratelimited
from logger import get_log, log
from process_input import record_strat
from tournament import build_ranking, run_tournament
app = Flask(__name__)
ASSIGNMENT = "proj01contest"
with connect_db() as db:
db("CREATE TABLE IF NOT EXISTS accesses (email VARCHAR(128), last_access INTEGER)")
db(
"""CREATE TABLE IF NOT EXISTS cached_strategies (
email VARCHAR(128), name VARCHAR(1024), hash VARCHAR(128), strategy LONGBLOB
)"""
)
db(
"CREATE TABLE IF NOT EXISTS cached_winrates (hash_0 VARCHAR(128), hash_1 VARCHAR(128), winrate DOUBLE)"
)
[docs]def expand_semester(short_form):
"""
Takes in a semester name in condensed notation (eg. sp21, fa20, su18) and expands it into
a full version (eg. Spring 2021, Fall 2020, Summer 2018).
:param short_form: condensed notation for a semester
:type short_form: str
:return: expanded notation for semester (str)
"""
return "{} 20{}".format(
{"sp": "Spring", "su": "Summer", "fa": "Fall"}[short_form[:2]], short_form[2:]
)
[docs]def semester_key(short_form):
"""
Construct a key from the SHORT_FORM of the semester
:param short_form: condensed notation for a semester
:type short_form: str
:return: double representing a key value for the semester SHORT_FORM
"""
return int(short_form[2:]) + 0.2 * {"sp": 1, "su": 2, "fa": 3}[short_form[:2]]
@app.route("/")
def index():
links = sorted(
(
[x.split(".")[0], expand_semester(x.split(".")[0])]
for x in os.listdir("data/leaderboards")
),
key=lambda x: semester_key(x[0]),
)
return render_template(
"hog-template.html",
timestamp=tournament.last_updated,
ranking=tournament.ranking,
team_list=[x[1] for x in tournament.ranking],
winrate_mat=tournament.winrates,
links=links,
)
@app.route("/winners")
def winners():
with open("data/winners.json") as f:
old_winners = json.load(f)
past_winners = []
for semester, winners in old_winners.items():
past_winners.append([expand_semester(semester), winners, semester])
past_winners.sort(key=lambda x: -semester_key(x[2]))
return render_template("hog-winners.html", past_winners=past_winners)
@app.route("/old_results/<semester>/")
def old_results(semester):
for datafile in os.listdir("data/leaderboards"):
if datafile == semester + ".json":
break
else:
abort(404)
return
with open(os.path.join("data/leaderboards", datafile)) as f:
data = json.load(f)
winrate_mat, teams = data["winrate_mat"], data["teams"]
teams = [
(team, sum(score > tournament.THRESHOLD for score in team_scores))
for team, team_scores in zip(teams, winrate_mat)
]
ranking = build_ranking(teams)
return render_template(
"hog-template.html",
timestamp="the end of the tournament.",
ranking=ranking,
team_list=[x[1] for x in ranking],
winrate_mat=winrate_mat,
suffix="({})".format(expand_semester(semester)),
)
@app.route("/log")
def test():
return "<pre>{}</pre>".format(get_log())
@app.route("/api/submit_strategy", methods=["POST"])
@ratelimited(timedelta(minutes=1))
def submit_strategy():
curr_time = datetime.now().astimezone(timezone("US/Pacific"))
end_time = datetime(2021, 2, 23, 23, 59, 0, tzinfo=timezone("US/Pacific"))
if curr_time > end_time:
abort(423, "The competition has ended.")
try:
strat = json.loads(request.form["strat"])
except JSONDecodeError:
abort(400, "Received malformed JSON strategy")
group = get_group(get_endpoint("cs61a") + f"/{ASSIGNMENT}")
hashed = record_strat(request.form["name"], group, strat)
run_tournament()
log("New strategy received, tournament will restart after current match completes.")
return jsonify({"success": True, "group": group, "hash": hashed})
log("Main thread starting. If this message is duplicated, something has gone wrong.")
tournament.post_tournament()
if __name__ == "__main__":
app.run()