summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorakiyamn2023-11-02 22:29:09 +1100
committerakiyamn2023-11-02 22:29:09 +1100
commit5461621c421130bd359efff4a10c3e1ba330e85c (patch)
tree32ab149b5993b89c42a46449c23c102858faa3a2
downloadjacbday-5461621c421130bd359efff4a10c3e1ba330e85c.tar.gz
jacbday-5461621c421130bd359efff4a10c3e1ba330e85c.zip
Init
-rw-r--r--be/.gitignore3
-rw-r--r--be/app.py65
-rw-r--r--be/points.py79
-rw-r--r--be/requirements.txt1
-rwxr-xr-xbe/run.sh2
-rw-r--r--fe/admin.html28
-rw-r--r--fe/admin.js68
-rw-r--r--fe/global.js5
-rw-r--r--fe/index.html16
-rw-r--r--fe/leaderboard.html39
-rw-r--r--fe/leaderboard.js70
-rw-r--r--fe/play.html14
-rw-r--r--fe/play.js24
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