diff options
| -rw-r--r-- | be/.gitignore | 3 | ||||
| -rw-r--r-- | be/app.py | 65 | ||||
| -rw-r--r-- | be/points.py | 79 | ||||
| -rw-r--r-- | be/requirements.txt | 1 | ||||
| -rwxr-xr-x | be/run.sh | 2 | ||||
| -rw-r--r-- | fe/admin.html | 28 | ||||
| -rw-r--r-- | fe/admin.js | 68 | ||||
| -rw-r--r-- | fe/global.js | 5 | ||||
| -rw-r--r-- | fe/index.html | 16 | ||||
| -rw-r--r-- | fe/leaderboard.html | 39 | ||||
| -rw-r--r-- | fe/leaderboard.js | 70 | ||||
| -rw-r--r-- | fe/play.html | 14 | ||||
| -rw-r--r-- | fe/play.js | 24 |
13 files changed, 414 insertions, 0 deletions
diff --git a/be/.gitignore b/be/.gitignore new file mode 100644 index 0000000..853bb7a --- /dev/null +++ b/be/.gitignore @@ -0,0 +1,3 @@ +venv +__pycache__ +*.db diff --git a/be/app.py b/be/app.py new file mode 100644 index 0000000..399ce97 --- /dev/null +++ b/be/app.py @@ -0,0 +1,65 @@ +from flask import Flask, request +from flask_cors import CORS +from werkzeug.middleware.proxy_fix import ProxyFix +import points + +app = Flask(__name__) + +app.wsgi_app = ProxyFix( + app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1 +) + +CORS(app) + +@app.route("/") +def ping(): + return "🀄" + +TEAMS = ["green", "purple"] +points.init_databases(TEAMS) + +def sanitise(string): + return string.lower() + +@app.route('/increment/<team>/<username>', methods=["POST"]) +def increment_user_points(team, username): + team = team.lower() + multiplier = points.get_multiplier(team) + return {"points": points.increment_points(sanitise(team), sanitise(username), float(multiplier))} + +@app.route('/points/<team>/<username>', methods=["GET"]) +def user_points(team, username): + return {"points": points.get_user_points(sanitise(team), sanitise(username))} + +@app.route('/points/<team>/', methods=["GET"]) +def get_team_points(team): + return {"points": points.get_team_points(sanitise(team))} + +@app.route("/leaderboard/", methods=["GET"]) +def get_leaderboard(): + return points.get_leaderboard() + +@app.route("/multiplier/<team>", methods=["GET", "POST"]) +def multiplier(team): + if request.method == "GET": + return {"multiplier": points.get_multiplier(sanitise(team))} + else: + data = request.get_json() + if "multiplier" in data: + return {"multiplier": points.set_multiplier(sanitise(team), data["multiplier"])} + return "", 400 + +@app.route("/prompt/", methods=["GET", "POST"]) +def prompt(): + if request.method == "GET": + return {"prompt": points.get_prompt()} + else: + data = request.get_json() + if "prompt" in data: + return {"prompt": points.set_prompt(data["prompt"])} + return "", 400 + +@app.route("/clear/", methods=["POST"]) +def clear(): + points.init_databases(TEAMS, clear=True) + return "" diff --git a/be/points.py b/be/points.py new file mode 100644 index 0000000..9ad693b --- /dev/null +++ b/be/points.py @@ -0,0 +1,79 @@ +import shelve + + +def init_databases(teams, clear=False): + with shelve.open("points.db", writeback=True) as db: + for team in teams: + if team not in db or clear: + db[team] = {} + db[team]["__total"] = 0 + with shelve.open("settings.db", writeback=True) as db: + if "multiplier" not in db or clear: + db["multiplier"] = {} + for team in teams: + if team not in db["multiplier"] or clear: + db["multiplier"][team] = 1 + db["prompt"] = "" + + +# def init_settings(clear=False): +# with shelve.open("settings.db") as db: +# if "multiplier" not in db or clear: +# db["multiplier"] = 1 + + +def increment_points(team, username, amount): + with shelve.open("points.db", writeback=True) as db: + if username not in db[team]: + db[team][username] = 0 + db[team][username] += amount + db[team]["__total"] += amount + return db[team][username] + + +def get_user_points(team, username): + with shelve.open("points.db") as db: + if username in db[team]: + return db[team][username] + return 0 + + +def get_team_points(team): + return get_user_points(team, "__total") + + +def get_leaderboard(): + multipliers = None + with shelve.open("settings.db") as db: + multipliers = db["multiplier"] + with shelve.open("points.db") as db: + return { + "settings": { + "prompt": get_prompt(), + "multiplier": multipliers + } + , + "points": dict(db), + } + + +def get_multiplier(team): + with shelve.open("settings.db") as db: + return db["multiplier"][team] + + +def set_multiplier(team, multiplier): + with shelve.open("settings.db", writeback=True) as db: + db["multiplier"][team] = multiplier + return db["multiplier"][team] + + +def get_prompt(): + with shelve.open("settings.db") as db: + return db["prompt"] + + +def set_prompt(prompt): + with shelve.open("settings.db", writeback=True) as db: + db["prompt"] = prompt + return db["prompt"] diff --git a/be/requirements.txt b/be/requirements.txt new file mode 100644 index 0000000..b81f683 --- /dev/null +++ b/be/requirements.txt @@ -0,0 +1 @@ +Flask>=3.0.0
\ No newline at end of file diff --git a/be/run.sh b/be/run.sh new file mode 100755 index 0000000..bbb2676 --- /dev/null +++ b/be/run.sh @@ -0,0 +1,2 @@ +source venv/bin/activate +flask run --host 0.0.0.0 --port 1997 diff --git a/fe/admin.html b/fe/admin.html new file mode 100644 index 0000000..d95f4fa --- /dev/null +++ b/fe/admin.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Document</title> +</head> +<body> + <div class="username"></div> + <div id="prompt-form"> + <label>Prompt: </label> + <input type="text" id="prompt"></input> + <button id="submit-settings" value="Submit">Submit</button> + </div> + <div id="multi-form"> + <label>Green: x</label> + <input type="number" id="green-team-multiplier" value="1"></input> + <label>Purple: x</label> + <input type="number" id="purple-team-multiplier" value="1"></input> + <button id="submit-multi">Submit</button> + </div> + <hr /> + <button id="green-team-increment">Green++</button> + <button id="purple-team-increment">Purple++</button> + <script type="module" src="admin.js"></script> +</body> +</html>
\ No newline at end of file diff --git a/fe/admin.js b/fe/admin.js new file mode 100644 index 0000000..5b0b7ee --- /dev/null +++ b/fe/admin.js @@ -0,0 +1,68 @@ +import { API_ROOT, ADMIN_CONSOLE_USER, TEAMS, EGRESS_HEADERS } from "./global.js" + +const element = (e) => { if (document.querySelector(e)) { return document.querySelector(e) } else { console.error("what is", e); return null } } +const bindClick = (e, f) => element(e).addEventListener("click", f); + +async function fillValues() { + for (let team of TEAMS) { + const multiplier = await getMultiplier(team) + element(`#${team}-team-multiplier`).value = multiplier + } + element("#prompt").value = await getPrompt() +} + + +async function submitSettings(event) { + console.log("asd") + const prompt = element("#prompt").value + await setPrompt(prompt) +} + +async function submitMulti(event) { + const multipliers = TEAMS.map(team => [team, element(`#${team}-team-multiplier`).value]) + multipliers.forEach(async x => await setMultiplier(...x)) +} + +async function getMultiplier(team) { + const response = await fetch(`${API_ROOT}/multiplier/${team}`, { "method": "GET" }) + const { multiplier } = await response.json() + return multiplier +} + +async function setMultiplier(team, multiplier) { + const body = JSON.stringify({ multiplier }) + const response = await fetch(`${API_ROOT}/multiplier/${team}/`, { "method": "POST", headers: EGRESS_HEADERS, body}) + const { multiplier: result } = await response.json() + console.log(`Setting multiplier to ${multiplier} for team ${team} | Result: ${result}`) + return result +} + +async function getPrompt() { + const response = await fetch(`${API_ROOT}/prompt/`, { "method": "GET" }) + const { prompt } = await response.json() + return prompt +} + +async function setPrompt(prompt) { + const body = JSON.stringify({ prompt }) + const response = await fetch(`${API_ROOT}/prompt/`, { "method": "POST", headers: EGRESS_HEADERS, body}) + const { prompt: result } = await response.json() + console.log(`Setting prompt to "${prompt}" | Result: ${result}`) + return result +} + +async function incrementTeam(team) { + const response = await fetch(`${API_ROOT}/increment/${team}/${ADMIN_CONSOLE_USER}/`, { "method": "POST" }) + const { points } = await response.json() + console.log(`Incrementing for ${team} | Result: ${points}`) + return points +} + +bindClick("#submit-settings", submitSettings) +bindClick("#submit-multi", submitMulti) +bindClick("#green-team-increment", _ => incrementTeam("green")) +bindClick("#purple-team-increment", _ => incrementTeam("purple")) + +document.addEventListener("DOMContentLoaded", async () => { + await fillValues() +});
\ No newline at end of file diff --git a/fe/global.js b/fe/global.js new file mode 100644 index 0000000..19b02fc --- /dev/null +++ b/fe/global.js @@ -0,0 +1,5 @@ +export const API_ROOT = "https://unchi.net/jac/be" +export const ADMIN_CONSOLE_USER = "admin" +export const TEAMS = ["green", "purple"] +export const EGRESS_HEADERS = { "Content-Type": "application/json" } +export const INGORED_USERS = ["__total"] diff --git a/fe/index.html b/fe/index.html new file mode 100644 index 0000000..9f3b3f0 --- /dev/null +++ b/fe/index.html @@ -0,0 +1,16 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Vite App</title> + </head> + <body> + <form id="play-form" action="./play.html" method="get"> + <label for="name">Name:</label> + <input type="text" id="username" name="username"><br><br> + <input type="submit" name="team" value="Green"> + <input type="submit" name="team" value="Purple"> + </form> + </body> +</html> diff --git a/fe/leaderboard.html b/fe/leaderboard.html new file mode 100644 index 0000000..e51eca9 --- /dev/null +++ b/fe/leaderboard.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Document</title> +</head> +<body> + <div id="leaderboard"> + <div id="leaderboard-title">Leaderboard</div> + <div id="prompt">x1.0</div> + <div id="team-container"> + + <!-- <div class="team-box green-team"> + <div id="team-data"></div> + <div id="team-name">Green Team</div> + <div id="team-total">12</div> + <div class="team-member-row"> + <div class="team-member-name">Bob</div> + <div class="team-member-score">12</div> + </div> + </div> + + <div class="team-box purple-team"> + <div id="team-data"></div> + <div id="team-name">Purple Team</div> + <div id="team-total">1</div> + <div class="team-member-row"> + <div class="team-member-name">Not Bob</div> + <div class="team-member-score">1</div> + </div> + </div> --> + + </div> + </div> + <script type="module" src="leaderboard.js"></script> +</body> +</html>
\ No newline at end of file diff --git a/fe/leaderboard.js b/fe/leaderboard.js new file mode 100644 index 0000000..426fb47 --- /dev/null +++ b/fe/leaderboard.js @@ -0,0 +1,70 @@ +import { API_ROOT, INGORED_USERS } from "./global.js" + +const element = (e) => document.querySelector(e) + +async function update() { + const data = await get_leaderboard_data() + fill_metadata(data.settings) + fill_leaderboard(data.points, data.settings.multiplier) +} + + +function sentenceCase(string) { + const out = string.slice(0, 1).toUpperCase() + string.slice(1).toLowerCase() + return out +} + +async function get_leaderboard_data() { + const response = await fetch(`${API_ROOT}/leaderboard/`) + console.log(API_ROOT) + const data = await response.json() + return data +} + +function fill_metadata(settings) { + element("#prompt").innerHTML = `"${settings.prompt}"` +} + +function clear_leaderboard() { + element(`#team-container`).innerHTML = "" +} + +function fill_leaderboard(points, multipliers) { + clear_leaderboard() + for (let team of ["green", "purple"]) { + console.log(team) + const html = ` + <div class="team-box ${team}-team"> + <div id="team-data"></div> + <div id="team-name">${sentenceCase(team)} Team</div> + <div id="team-multiplier">x${multipliers[team]}</div> + <div id="team-total">Total: ${points[team]["__total"]}</div> + ${team_member_entry(points[team])} + </div> + ` + element(`#team-container`).innerHTML += html + } +} + +function team_member_entry(teamPoints) { + let html = "" + for (let member in teamPoints) { + if (!INGORED_USERS.includes(member)) { + html += ` + <div class="team-member-row"> + <div class="team-member-name">${sentenceCase(member)}</div> + <div class="team-member-score">${teamPoints[member]}</div> + </div> + ` + } + } + return html +} + +document.addEventListener("DOMContentLoaded", async () => { + update() + const urlParams = new URLSearchParams(window.location.search); + const poll = urlParams.get("poll") || 3000 + const polling = setInterval(update, poll) +}); + diff --git a/fe/play.html b/fe/play.html new file mode 100644 index 0000000..d535b40 --- /dev/null +++ b/fe/play.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Document</title> +</head> +<body> + <div class="username"></div> + <script type="module" src="play.js"></script> + <button id="increment">+</button> +</body> +</html>
\ No newline at end of file diff --git a/fe/play.js b/fe/play.js new file mode 100644 index 0000000..cc790a7 --- /dev/null +++ b/fe/play.js @@ -0,0 +1,24 @@ +import { API_ROOT } from "./global.js" + +const {team: TEAM, username: USERNAME} = getUserInfo() + +async function increment() { + const response = await fetch(`${API_ROOT}/increment/${TEAM}/${USERNAME}`, {"method": "POST"}) + const {points} = await response.json() + return points +} + +function getUserInfo() { + const urlParams = new URLSearchParams(window.location.search); + return {"team": urlParams.get("team"), "username": urlParams.get("username")} + +} + +async function getUserPoints() { + const response = await fetch(`${API_ROOT}/points/${TEAM}/${USERNAME}`) + const {points} = await response.json() + return points +} + +document.querySelector("#increment").addEventListener("click", increment) +document.querySelector(".username")
\ No newline at end of file |
