Add leaderboard http service
This commit is contained in:
parent
dd756cb9eb
commit
2d51468eb1
3 changed files with 196 additions and 0 deletions
4
leaderboard/README
Normal file
4
leaderboard/README
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
pip install flask
|
||||||
|
mkdir templates
|
||||||
|
mv main.jinja2 templates/
|
||||||
|
AOC_TOKEN=<adventofcode.com session cookie> FLASK_APP=app flask run
|
||||||
87
leaderboard/app.py
Normal file
87
leaderboard/app.py
Normal file
|
|
@ -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
|
||||||
105
leaderboard/templates/main.jinja2
Normal file
105
leaderboard/templates/main.jinja2
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
gap: 1em;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 1em;
|
||||||
|
width: 50em;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
th::after {
|
||||||
|
display: block;
|
||||||
|
content: "----------------------"
|
||||||
|
}
|
||||||
|
|
||||||
|
th + th::after {
|
||||||
|
content: "-------------"
|
||||||
|
}
|
||||||
|
|
||||||
|
th + th + th::after {
|
||||||
|
content: "--------"
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<h1>Leaderboard</h1>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Local score</th>
|
||||||
|
<th>Stars</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for local_score, stars, name in leaderboard %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ name }}</td>
|
||||||
|
<td>{{ local_score }}</td>
|
||||||
|
<td>{{ stars }}</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 %}
|
||||||
Loading…
Add table
Reference in a new issue