Last active
December 13, 2021 08:14
-
-
Save madr/67618ae6880a7a4d0f87f996ebec8ccf to your computer and use it in GitHub Desktop.
Advent of Code timeline
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pip install flask | |
mkdir templates | |
mv main.jinja2 templates/ | |
AOC_TOKEN=<adventofcode.com session cookie> FLASK_APP=app flask run |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", "2021") | |
leaderboard = request.args.get("board") | |
token = os.environ.get("AOC_TOKEN") | |
if not token: | |
return "Missing AOC_TOKEN environment variable. 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, "%H:%M:%S (%d/%m)"), 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], "%H:%M:%S (%d/%m)") | |
), | |
) | |
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<style> | |
html { | |
font-family: monospace; | |
} | |
* { | |
margin: 0; | |
padding: 0; | |
} | |
body { | |
display: flex; | |
gap: 1em; | |
flex-direction: column; | |
padding: 1em; | |
} | |
h1, h2 { | |
font-size: 1em; | |
font-weight: normal; | |
text-transform: uppercase; | |
} | |
h1::after { | |
display: block; | |
content: '===========' | |
} | |
h2::after { | |
display: block; | |
content: '------' | |
} | |
h3 { | |
font-size: 1em; | |
font-weight: normal; | |
} | |
h3::after { | |
content: ':' | |
} | |
ul, ol { | |
list-style: none; | |
} | |
</style> | |
<h1>Leaderboard</h1> | |
<table> | |
{% for local_score, stars, name in leaderboard %} | |
<tr> | |
<td>{{ local_score }}</td> | |
<td>{{ stars }}</td> | |
<td>{{ name }}</td> | |
</tr> | |
{% endfor %} | |
</table> | |
<h2>Events</h2> | |
{% for day, item in timeline %} | |
<h3>Day {{ day }}</h3> | |
<ol> | |
{% for ts, day, star, name, points in item %} | |
<li> | |
{% if star == '2' %} | |
{{ ts }} - {{ name }} finished | |
{% else %} | |
{{ ts }} - {{ name }} solved part 1 | |
{% endif %} | |
and got {{ points }} points! | |
</li> | |
{% endfor %} | |
</ol> | |
{% endfor %} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment