add graph

This commit is contained in:
fk 2024-10-03 15:34:30 +02:00
parent 228a069bf3
commit 8d38b87989
Signed by: fk
SSH key fingerprint: SHA256:KQRtpoT/zoUtKcHNokD4N151te4gRMvc/3RHpxNd+Pk
2 changed files with 156 additions and 31 deletions

65
main.py
View file

@ -1,18 +1,70 @@
import requests import requests
from flask import Flask, jsonify, render_template from flask import Flask, jsonify, render_template
users = {"local_klink": "Francis"} users = {
"local_klink": "Francis",
"awful_lickilicky": "geen-zeuser",
"decisive_grookey": "geen",
"sweet_munna": "geen",
"lazy_pichu": "geen",
"stupid_magikarp": "geen",
"superficial_gigalith": "geen",
"ugly_kingdra": "geen",
}
def get_game_data():
res = requests.get("https://botbattle.be/api/games")
return sorted(res.json(), key=lambda x: x['gid'])
def get_data(): def get_data():
res = requests.get("https://botbattle.be/api/leaderboard") res = requests.get("https://botbattle.be/api/leaderboard")
return res.json() return res.json()
def process_game_data(game_data):
bots: dict[str, dict] = {}
min_elo: int | None = None
max_elo: int | None = None
for game in game_data:
for user in game['users']:
username = user['username']
if username in users:
if username not in bots:
if min_elo is None:
min_elo = user['elo']
max_elo = user['elo']
bots[username] = {'x': [], 'y': [], 'color_ring': user['color_ring'], 'color_body': user['color_body']}
if user['elo'] < min_elo:
min_elo = user['elo']
elif user['elo'] > max_elo:
max_elo = user['elo']
bots[username]['x'].append(game['gid'])
bots[username]['y'].append(user['elo'])
return {
'bots': bots,
'min_elo': min_elo,
'max_elo': max_elo,
'game': game_data[-1]['gid'],
}
def filter_date(data): def filter_date(data):
data = sorted(data, key=lambda x: x["elo"], reverse=True) data = sorted(data, key=lambda x: x["elo"], reverse=True)
filtered = list(filter(lambda x: x["username"] in users.keys(), data)) filtered = []
[dict.update(item, position=index + 1, name=users[item["username"]]) for index, item in enumerate(filtered)]
for i, user in enumerate(data):
user["global_position"] = i + 1
if user['username'] not in users:
continue
filtered.append(user)
user['name'] = users[user['username']]
return filtered return filtered
@ -29,9 +81,12 @@ def index():
@app.route("/leaderboard") @app.route("/leaderboard")
def leaderboard(): def leaderboard():
data = get_data() data = get_data()
filtered = filter_date(data) return jsonify(filter_date(data))
return jsonify(filtered)
@app.route("/graph")
def graph():
data = get_game_data()
return jsonify(process_game_data(data))
if __name__ == "__main__": if __name__ == "__main__":
app.run() app.run()

View file

@ -1,8 +1,21 @@
{% extends "header.html" %} {% block content %} {% extends "header.html" %} {% block content %}
<header> <div class="card m-5">
<h1 class="title">Leaderboard</h1> <div class="card-content is-size-2 has-text-centered">
</header> Zeus Leaderboard - CeneBotBattle
<table class="table"> </div>
</div>
<div class="columns m-5 is-gapless">
<div class="column">
<div class="card ">
<div class="card-content" style="height: 1000px">
<canvas id="le_graph" width="300" height="150" style="height: 100%;width:100%"></canvas>
</div>
</div>
</div>
<div class="column ml-5 is-one-third">
<div class="card">
<div class="card-content">
<table class="table is-fullwidth is-size-4">
<thead> <thead>
<tr> <tr>
<th>Position</th> <th>Position</th>
@ -14,7 +27,7 @@
<tbody id="leaderboard-body"> <tbody id="leaderboard-body">
{% for row in data %} {% for row in data %}
<tr> <tr>
<td>{{ row['position'] }}</td> <td>{{ row['global_position'] }}</td>
<td>{{ row['name'] }}</td> <td>{{ row['name'] }}</td>
<td>{{ row['elo'] }}</td> <td>{{ row['elo'] }}</td>
<td>{{ row['username'] }}</td> <td>{{ row['username'] }}</td>
@ -23,10 +36,18 @@
</tbody> </tbody>
</table> </table>
</div>
</div>
</div>
</div>
<script> <script>
async function fetchLeaderboardData() { async function fetchLeaderboardData() {
try { try {
const response = await fetch("http://localhost:5000/leaderboard"); const response = await fetch("/leaderboard");
if (!response.ok) return; if (!response.ok) return;
const data = await response.json(); const data = await response.json();
@ -38,10 +59,10 @@
data.forEach((row) => { data.forEach((row) => {
const tr = document.createElement("tr"); const tr = document.createElement("tr");
tr.innerHTML = ` tr.innerHTML = `
<td>${row.position}</td> <td>${row.global_position}</td>
<td>${row.name}</td> <td>${row.name}</td>
<td>${row.elo}</td> <td>${row.elo}</td>
<td>${row.username}</td> <td><span class="tag is-large" style="background-color: ${row.color_ring}">${row.username}</span></td>
`; `;
tableBody.appendChild(tr); tableBody.appendChild(tr);
}); });
@ -50,11 +71,60 @@
} }
} }
function getX(width, margin, min_game, max_game, game) {
return ((game - min_game) / (max_game - min_game) * (width - margin * 2)) + margin
}
function getY(height, margin, min_elo, max_elo, elo) {
return (height - ((elo - min_elo) / (max_elo - min_elo) * (height - margin * 2))) - margin
}
async function draw_graph() {
let canvas = document.getElementById('le_graph')
let ctx = canvas.getContext("2d")
try {
let res = await fetch("/graph")
let graph_data = await res.json()
let bots = graph_data.bots
resizeToParent(canvas)
const width = canvas.width
const height = canvas.height
const margin = 20
for (const [_, bot] of Object.entries(bots)) {
console.log(bot)
ctx.lineWidth = 4
ctx.strokeStyle = bot.color_ring
ctx.beginPath()
ctx.moveTo(getX(width, margin, 0, graph_data.game, bot.x[0]), getY(height, margin, graph_data.min_elo, graph_data.max_elo, bot.y[0]))
for (let i = 1; i < bot.x.length; i++) {
ctx.lineTo(getX(width, margin, 0, graph_data.game, bot.x[i]), getY(height, margin, graph_data.min_elo, graph_data.max_elo, bot.y[i]))
}
console.log("stroking")
ctx.stroke()
}
} catch (err) {
console.log("Failed to fetch graph data:", err)
}
}
function resizeToParent(canvas) {
/* https://stackoverflow.com/questions/70128894/how-to-force-a-canvas-size-to-real-size-of-a-window-in-pixels-disregarding-the */
canvas.width = canvas.clientWidth * window.devicePixelRatio;
canvas.height = canvas.clientHeight * window.devicePixelRatio;
}
// Poll the server every 5 seconds // Poll the server every 5 seconds
setInterval(fetchLeaderboardData, 5000); setInterval(fetchLeaderboardData, 5000);
setInterval(draw_graph, 5000);
// Initial load // Initial load
fetchLeaderboardData(); fetchLeaderboardData();
draw_graph();
</script> </script>
{% endblock %} {% endblock %}