diff --git a/leaderboard/README b/leaderboard/README new file mode 100644 index 0000000..66669d9 --- /dev/null +++ b/leaderboard/README @@ -0,0 +1,4 @@ +pip install flask +mkdir templates +mv main.jinja2 templates/ +AOC_TOKEN= FLASK_APP=app flask run diff --git a/leaderboard/app.py b/leaderboard/app.py new file mode 100644 index 0000000..0a4407d --- /dev/null +++ b/leaderboard/app.py @@ -0,0 +1,87 @@ +import collections +from datetime import datetime +import json +import os +import urllib +from flask import Flask, render_template, request + +os.environ["TZ"] = "Europe/Stockholm" +app = Flask(__name__) + + +def get_data(token, calendar, leaderboard, dummy_data=True): + if dummy_data: + with open("leaderboard.json") as lb: + data = lb.read() + else: + try: + url = f"https://adventofcode.com/{calendar}/leaderboard/private/view/{leaderboard}.json" + headers = {"Cookie": f"session={token}"} + req = urllib.request.Request(url, headers=headers) + with urllib.request.urlopen(req) as response: + data = response.read() + except TypeError: + data = "[]" + except urllib.error.HTTPError: + data = "[]" + + return json.loads(data) + + +@app.route("/") +def hello_world(): + calendar = request.args.get("calendar", datetime.now().year) + leaderboard = request.args.get("board") + token = request.args.get("token") + if not token: + return "Missing token get parameter. Use the session cookie on adventofcode.com" + if not leaderboard: + return "Missing board get parameter." + data = get_data(token, calendar, leaderboard, False) + if len(data) == 0: + return "Token expired or no access to board" + leaderboard = sorted( + [ + (int(member["local_score"]), int(member["stars"]), member["name"]) + for _, member in data["members"].items() + if int(member["stars"]) > 0 + ], + key=lambda x: x[0], + reverse=True, + ) + timeline = collections.defaultdict(lambda: []) + for _id, m in data["members"].items(): + for d, stars in m["completion_day_level"].items(): + for p, tsd in stars.items(): + ts = datetime.fromtimestamp(tsd["get_star_ts"]) + + timeline[d].append( + (datetime.strftime(ts, "%Y-%m-%dT%H:%M:%S"), d, p, m["name"]) + ) + timeline = sorted(timeline.items(), key=lambda x: int(x[0]), reverse=True) + timeline = [ + ( + d, + sorted( + tl, key=lambda entry: datetime.strptime(entry[0], "%Y-%m-%dT%H:%M:%S") + ), + ) + for d, tl in timeline + ] + timeline = [ + ( + d, + [ + te + (get_score(time_entries, te, len(data["members"])),) + for te in time_entries + ], + ) + for d, time_entries in timeline + ] + return render_template("main.jinja2", leaderboard=leaderboard, timeline=timeline) + + +def get_score(time_entries, current_te, nr_competitors): + list_for_current_star = [te for te in time_entries if te[2] == current_te[2]] + pos = [i for i, te in enumerate(list_for_current_star) if te[3] == current_te[3]][0] + return nr_competitors - pos diff --git a/leaderboard/templates/main.jinja2 b/leaderboard/templates/main.jinja2 new file mode 100644 index 0000000..6bfa928 --- /dev/null +++ b/leaderboard/templates/main.jinja2 @@ -0,0 +1,105 @@ + + +

Leaderboard

+ + + + + + + + +{% for local_score, stars, name in leaderboard %} + + + + + +{% endfor %} +
NameLocal scoreStars
{{ name }}{{ local_score }}{{ stars }}
+ +

Events

+ +{% for day, item in timeline %} +

Day {{ day }}

+
    + {% for ts, day, star, name, points in item %} +
  1. + {% if star == '2' %} + {{ ts }} - {{ name }} finished + {% else %} + {{ ts }} - {{ name }} solved part 1 + {% endif %} + and got {{ points }} points! +
  2. + {% endfor %} +
+{% endfor %}