summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app.py83
-rw-r--r--static/styles/manage.css50
-rw-r--r--views/base.html5
-rw-r--r--views/manage.html55
4 files changed, 170 insertions, 23 deletions
diff --git a/app.py b/app.py
index 20467bc..dc8c076 100644
--- a/app.py
+++ b/app.py
@@ -9,6 +9,7 @@ import os
import sqlite3
from bottle.ext import sqlite
from beaker.middleware import SessionMiddleware
+import functools
load_dotenv()
@@ -31,17 +32,29 @@ connection = sqlite3.connect(DB_PATH)
cursor = connection.cursor()
cursor.executescript("""
CREATE TABLE IF NOT EXISTS applications (
+ id INTEGER PRIMARY KEY,
username VARCHAR(12) NOT NULL,
+ userId INTEGER UNIQUE NOT NULL,
preferredRole VARCHAR(6) NOT NULL,
motivation TEXT NOT NULL,
- userId INTEGER UNIQUE NOT NULL
+ applicationTime INT NOT NULL -- unix timestamp
);
+ INSERT OR IGNORE
+ INTO applications(username, userId, preferredRole, motivation, applicationTime)
+ VALUES
+ ('DillerBlaster69', 0, 'dps', 'fake motivation', strftime('%s','now')),
+ ('DillerDiller', 1, 'healer', 'fake motivation', strftime('%s','now')),
+ ('diller123', 2, 'tank', 'fake motivation', strftime('%s','now')),
+ ('susamongus', 3, 'dps', 'fake motivation #4', strftime('%s','now'));
CREATE TABLE IF NOT EXISTS users (
+ id INTEGER PRIMARY KEY,
+ username VARCHAR(12) NOT NULL,
userId INTEGER UNIQUE NOT NULL,
- role VARCHAR(6) NOT NULL
+ preferredRole VARCHAR(6) NOT NULL,
+ joinTime INT NOT NULL -- unix timestamp
);
- INSERT OR IGNORE INTO users(userId, role) VALUES (1165955606, 'dps');
+ INSERT OR IGNORE INTO users(userId, preferredRole, joinTime) VALUES (1165955606, 'dps', strftime('%s','now'));
""")
cursor.close()
connection.close()
@@ -164,7 +177,11 @@ def join_submission(db: sqlite3.Connection):
raise HTTPError(400, "Missing or invalid user id")
try:
- db.execute("INSERT INTO applications(username, preferredRole, motivation, userId) VALUES (?, ?, ?, ?)", (name, preferred_role, motivation, user_id))
+ db.execute("""
+ INSERT
+ INTO applications(username, userId, preferredRole, motivation, applicationTime)
+ VALUES (?, ?, ?, strftime('%s','now'), ?)
+ """, (name, user_id, preferred_role, motivation))
except sqlite3.IntegrityError as e:
print(e.sqlite_errorcode == sqlite3.SQLITE_CONSTRAINT_UNIQUE)
print(str(e))
@@ -176,23 +193,47 @@ def join_submission(db: sqlite3.Connection):
return template("join_success")
[email protected]("/leaderboards.html")
-def leaderboards(db: sqlite3.Connection):
- # all_members = db.execute("SELECT name FROM members")
- all_members = [
- ["a", f"10 days", 1],
- ["b", f"8 days", 2],
- ["c", f"6 days", 3],
- ["d", f"3 days", 4],
- ["e", f"1 days", 5],
- ["f", f"1 days", 6],
- ["g", f"1 days", 7],
- ["h", f"1 days", 8],
- ["i", f"1 days", 9],
- ["j", f"1 days", 10],
- ["k", f"0 days", 11]
- ]
- return template("leaderboards.html", all_members=all_members)
+def require_authentication(fn):
+ """Decorator that ensures the client is logged in"""
+ @functools.wraps(fn)
+ def wrapped(db: sqlite3.Connection, *args, **kwargs):
+ # Ensure authentication
+ session = request.environ.get("beaker.session")
+ print(session)
+ user_id = session.get("user_id", None)
+ if user_id is None:
+ raise HTTPError(403, "Must be logged in! (missing cookie)")
+ user = db.execute("SELECT * FROM users WHERE userId = ?", [user_id]).fetchone()
+ if user is None:
+ raise HTTPError(403, "Must be logged in! (unknown user)")
+
+ # Wrapped function may or may not want this
+ kwargs["db"] = db
+
+ return fn(*args, **kwargs)
+ return wrapped
+
+@require_authentication
[email protected]("/manage.html")
+def manage(db: sqlite3.Connection):
+ applications = db.execute("SELECT * FROM applications").fetchall();
+ return template("manage", applications=applications)
+
+
+@require_authentication
[email protected]("/manage/<action:re:accept|reject>/<user_id:int>", method="POST")
+def approve_application(action: str, user_id: int, db: sqlite3.Connection):
+ if action == "accept":
+ db.execute("""
+ INSERT INTO users(username, userId, preferredRole, joinTime)
+ SELECT username, userId, preferredRole, strftime('%s','now')
+ FROM applications
+ WHERE userId = ?;
+ """, [user_id])
+ print(user_id)
+ db.execute("DELETE FROM applications WHERE userId = ?", [user_id])
+
+ return f"Application {action}ed!"
@app.route("/<type:re:styles|images>/<filename>")
def server_static(type, filename):
diff --git a/static/styles/manage.css b/static/styles/manage.css
new file mode 100644
index 0000000..d2e67c9
--- /dev/null
+++ b/static/styles/manage.css
@@ -0,0 +1,50 @@
+/*
+ * This file contains styles specific to the "manage" view. The corresponding
+ * HTML/template file is `views/manage.html`.
+ */
+
+main {
+ /* Center align and keep width readable. */
+ width: 100%;
+ max-width: 500px;
+ margin: 2rem auto;
+}
+
+.applications__item {
+ border: 2px solid white;
+ border-radius: 5px;
+ padding: 1rem;
+}
+
+.application__username {
+ font-size: x-large;
+}
+
+.applications {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.action {
+ display: contents;
+}
+
+.action__button {
+ width: 48%;
+}
+
+.action__button:hover { filter: brightness(90%); }
+.action__button:active { filter: brightness(60%); }
+
+.action__button--accept {
+ border: 2px solid #4b694b;
+ background-color: #608660;
+ color: white;
+}
+
+.action__button--reject {
+ border: 2px solid #891818;
+ background-color: #cd4747;
+ color: white;
+}
diff --git a/views/base.html b/views/base.html
index 556a777..294cfd1 100644
--- a/views/base.html
+++ b/views/base.html
@@ -14,8 +14,9 @@
<ul class="navbar__links" role="navigation" aria-label="Main">
<li><a class="navbar__location" href="/index.html">About us</a></li>
<li><a class="navbar__location" href="/history.html">History</a></li>
- <li><a class="navbar__location" href="/leaderboards.html">Leaderboards</a></li>
- {% if not logged_in %}
+ {% if logged_in %}
+ <li><a class="navbar__location" href="/manage.html">Manage</a></li>
+ {% else %}
<li><a class="navbar__location" href="/login">Log in</a></li>
<li><a class="navbar__location" href="/join_intro.html">Join</a></li>
{% endif %}
diff --git a/views/manage.html b/views/manage.html
new file mode 100644
index 0000000..d7e2b65
--- /dev/null
+++ b/views/manage.html
@@ -0,0 +1,55 @@
+{% extends "base.html" %}
+
+{% block head %}
+ <link rel="stylesheet" href="/styles/manage.css">
+ <script type="module">
+ // Progressive enhancement – perform form requests without leaving page.
+
+ for (const application of document.querySelectorAll(".applications__item")) {
+ for (const form of application.querySelectorAll("form.action")) {
+ form.addEventListener("submit", async (event) => {
+ event.preventDefault();
+
+ if (!confirm("Are you sure?")) {
+ return;
+ }
+
+ let ok;
+ try {
+ const response = await fetch(form.action, {
+ method: "POST",
+ data: new FormData(form),
+ });
+ ok = response.ok;
+ } catch {
+ ok = false;
+ }
+
+ if (ok) {
+ application.remove();
+ } else {
+ alert("Something went wrong >w<");
+ }
+ });
+ }
+ }
+ </script>
+{% endblock %}
+
+{% block content %}
+ <h1>Manage applications</h1>
+ <div class="applications">
+ {% for application in applications %}
+ <section class="applications__item">
+ <span class="application__username">{{ application.username | e }}</span>
+ <p>{{ application.motivation | e }}</p>
+ <form class="action" method="POST" action="/manage/reject/{{ application.userId | e }}">
+ <button class="action__button action__button--reject">Reject</button>
+ </form>
+ <form class="action" method="POST" action="/manage/accept/{{ application.userId | e }}">
+ <button class="action__button action__button--accept">Accept</button>
+ </form>
+ </section>
+ {% endfor %}
+ </div>
+{% endblock %}