aboutsummaryrefslogtreecommitdiffstats
path: root/pysite
diff options
context:
space:
mode:
Diffstat (limited to 'pysite')
-rw-r--r--pysite/mixins.py3
-rw-r--r--pysite/tables.py11
-rw-r--r--pysite/views/api/bot/user.py13
-rw-r--r--pysite/views/main/jams/join.py188
-rw-r--r--pysite/views/main/jams/profile.py59
-rw-r--r--pysite/views/main/jams/signup.py9
-rw-r--r--pysite/views/staff/jams/actions.py3
7 files changed, 270 insertions, 16 deletions
diff --git a/pysite/mixins.py b/pysite/mixins.py
index a3edc4f2..6e5032ab 100644
--- a/pysite/mixins.py
+++ b/pysite/mixins.py
@@ -4,6 +4,7 @@ from flask import Blueprint
from rethinkdb.ast import Table
from pysite.database import RethinkDB
+from pysite.oauth import OauthBackend
class DBMixin:
@@ -98,5 +99,5 @@ class OauthMixin:
return self.oauth.user_data()
@property
- def oauth(self):
+ def oauth(self) -> OauthBackend:
return self._oauth()
diff --git a/pysite/tables.py b/pysite/tables.py
index c592333d..e99ca989 100644
--- a/pysite/tables.py
+++ b/pysite/tables.py
@@ -79,8 +79,9 @@ TABLES = {
primary_key="id",
keys=sorted([
"id", # uuid
+ "snowflake", # str
"jam", # int
- "answers", # dict {question, answer, metadata}
+ "answers", # list [{question, answer, metadata}]
"approved" # bool
])
),
@@ -100,16 +101,16 @@ TABLES = {
"id", # uuid
"participant", # str
"reason", # str
- "number" # int (optionally -1 for permanent)
+ "number", # int (optionally -1 for permanent)
+ "decremented_for" # list[int]
])
),
"code_jam_participants": Table( # Info for each participant
primary_key="id",
keys=sorted([
- "snowflake", # int
- "skill_level", # str
- "age", # str
+ "id", # str
+ "dob", # str
"github_username", # str
"timezone" # str
])
diff --git a/pysite/views/api/bot/user.py b/pysite/views/api/bot/user.py
index 12f5a2c7..a353ccfe 100644
--- a/pysite/views/api/bot/user.py
+++ b/pysite/views/api/bot/user.py
@@ -32,6 +32,7 @@ class UserView(APIView, DBMixin):
name = "bot.users"
table_name = "users"
oauth_table_name = "oauth_data"
+ participants_table = "code_jam_participants"
@api_key
@api_params(schema=SCHEMA, validation_type=ValidationTypes.json)
@@ -40,6 +41,8 @@ class UserView(APIView, DBMixin):
deletions = 0
oauth_deletions = 0
+ profile_deletions = 0
+
user_ids = [user["user_id"] for user in data]
all_users = self.db.run(self.db.query(self.table_name), coerce=list)
@@ -54,7 +57,9 @@ class UserView(APIView, DBMixin):
for item in all_oauth_data:
if item["snowflake"] not in user_ids:
self.db.delete(self.oauth_table_name, item["id"], durability="soft")
+ self.db.delete(self.participants_table, item["id"], durability="soft")
oauth_deletions += 1
+ profile_deletions += 1
del user_ids
@@ -68,6 +73,7 @@ class UserView(APIView, DBMixin):
changes["deleted"] = deletions
changes["deleted_oauth"] = oauth_deletions
+ changes["deleted_jam_profiles"] = profile_deletions
return jsonify(changes) # pragma: no cover
@@ -98,6 +104,13 @@ class UserView(APIView, DBMixin):
.delete()
).get("deleted", 0)
+ profile_deletions = self.db.run(
+ self.db.query(self.participants_table)
+ .get_all(*user_ids)
+ .delete()
+ )
+
changes["deleted_oauth"] = oauth_deletions
+ changes["deleted_jam_profiles"] = profile_deletions
return jsonify(changes) # pragma: no cover
diff --git a/pysite/views/main/jams/join.py b/pysite/views/main/jams/join.py
new file mode 100644
index 00000000..83013a01
--- /dev/null
+++ b/pysite/views/main/jams/join.py
@@ -0,0 +1,188 @@
+from email.utils import parseaddr
+
+from flask import redirect, request, url_for
+from werkzeug.exceptions import BadRequest, NotFound
+
+from pysite.base_route import RouteView
+from pysite.decorators import csrf
+from pysite.mixins import DBMixin, OauthMixin
+
+
+class JamsJoinView(RouteView, DBMixin, OauthMixin):
+ path = "/jams/join/<int:jam>"
+ name = "jams.join"
+
+ table_name = "code_jams"
+ forms_table = "code_jam_forms"
+ questions_table = "code_jam_questions"
+ responses_table = "code_jam_responses"
+ participants_table = "code_jam_participants"
+ infractions_table = "code_jam_infractions"
+
+ def get(self, jam):
+ jam_obj = self.db.get(self.table_name, jam)
+
+ if not jam_obj:
+ return NotFound()
+
+ if not self.user_data:
+ return redirect(url_for("discord.login"))
+
+ infractions = self.get_infractions(self.user_data["user_id"])
+
+ for infraction in infractions:
+ if infraction["number"] == -1: # Indefinite ban
+ return self.render("main/jams/banned.html", infraction=infraction, jam=jam_obj)
+
+ if infraction["number"]: # Got some jams left
+ if jam not in infraction["decremented_for"]:
+ # Make sure they haven't already tried to apply for this jam
+ infraction["number"] -= 1
+ infraction["decremented_for"].append(jam)
+
+ self.db.insert(self.infractions_table, infraction, conflict="replace")
+
+ return self.render("main/jams/banned.html", infraction=infraction, jam=jam_obj)
+
+ if jam in infraction["decremented_for"]:
+ # They already tried to apply for this jam
+ return self.render("main/jams/banned.html", infraction=infraction, jam=jam_obj)
+
+ participant = self.db.get(self.participants_table, self.user_data["user_id"])
+
+ if not participant:
+ return redirect(url_for("info.jams.profile"))
+
+ if self.get_response(jam, self.user_data["user_id"]):
+ return self.render("main/jams/already.html", jam=jam_obj)
+
+ form_obj = self.db.get(self.forms_table, jam)
+ questions = []
+
+ if form_obj:
+ for question in form_obj["questions"]:
+ questions.append(self.db.get(self.questions_table, question))
+
+ return self.render(
+ "main/jams/join.html", jam=jam_obj, form=form_obj,
+ questions=questions, question_ids=[q["id"] for q in questions]
+ )
+
+ @csrf
+ def post(self, jam):
+ jam_obj = self.db.get(self.table_name, jam)
+
+ if not jam_obj:
+ return NotFound()
+
+ if not self.user_data:
+ return redirect(url_for("discord.login"))
+
+ infractions = self.get_infractions(self.user_data["user_id"])
+
+ for infraction in infractions:
+ if infraction["number"] == -1: # Indefinite ban
+ return self.render("main/jams/banned.html", infraction=infraction)
+
+ if infraction["number"]: # Got some jams left
+ if jam not in infraction["decremented_for"]:
+ # Make sure they haven't already tried to apply for this jam
+ infraction["number"] -= 1
+ infraction["decremented_for"].append(jam)
+
+ self.db.insert(self.infractions_table, infraction, conflict="replace")
+
+ return self.render("main/jams/banned.html", infraction=infraction, jam=jam_obj)
+
+ if jam in infraction["decremented_for"]:
+ # They already tried to apply for this jam
+ return self.render("main/jams/banned.html", infraction=infraction, jam=jam_obj)
+
+ participant = self.db.get(self.participants_table, self.user_data["user_id"])
+
+ if not participant:
+ return redirect(url_for("info.jams.profile"))
+
+ if self.get_response(jam, self.user_data["user_id"]):
+ return self.render("main/jams/already.html", jam=jam_obj)
+
+ form_obj = self.db.get(self.forms_table, jam)
+
+ if not form_obj:
+ return NotFound()
+
+ questions = []
+
+ for question in form_obj["questions"]:
+ questions.append(self.db.get(self.questions_table, question))
+
+ answers = []
+
+ for question in questions:
+ value = request.form.get(question["id"])
+ answer = {"question": question["id"]}
+
+ if not question["optional"] and value is None:
+ return BadRequest()
+
+ if question["type"] == "checkbox":
+ if value == "on":
+ answer["value"] = True
+ elif not question["optional"]:
+ return BadRequest()
+ else:
+ answer["value"] = False
+
+ elif question["type"] == "email":
+ if value:
+ address = parseaddr(value)
+
+ if address == ("", ""):
+ return BadRequest()
+
+ answer["value"] = value
+
+ elif question["type"] in ["number", "range", "slider"]:
+ if value is not None:
+ value = int(value)
+
+ if value > int(question["data"]["max"]) or value < int(question["data"]["min"]):
+ return BadRequest()
+
+ answer["value"] = value
+
+ elif question["type"] == "radio":
+ if value:
+ if value not in question["data"]["options"]:
+ return BadRequest()
+
+ answer["value"] = value
+
+ elif question["type"] in ["text", "textarea"]:
+ answer["value"] = value
+
+ answers.append(answer)
+
+ user_id = self.user_data["user_id"]
+
+ response = {
+ "snowflake": user_id,
+ "jam": jam,
+ "approved": False,
+ "answers": answers
+ }
+
+ self.db.insert(self.responses_table, response)
+ return self.render("main/jams/thanks.html", jam=jam_obj)
+
+ def get_response(self, jam, user_id):
+ query = self.db.query(self.responses_table).filter({"jam": jam, "snowflake": user_id})
+ result = self.db.run(query, coerce=list)
+
+ if result:
+ return result[0]
+ return None
+
+ def get_infractions(self, user_id):
+ query = self.db.query(self.infractions_table).filter({"participant": user_id})
+ return self.db.run(query, coerce=list)
diff --git a/pysite/views/main/jams/profile.py b/pysite/views/main/jams/profile.py
new file mode 100644
index 00000000..407f842e
--- /dev/null
+++ b/pysite/views/main/jams/profile.py
@@ -0,0 +1,59 @@
+import datetime
+
+from flask import redirect, request, url_for
+from werkzeug.exceptions import BadRequest
+
+from pysite.base_route import RouteView
+from pysite.decorators import csrf
+from pysite.mixins import DBMixin, OauthMixin
+
+
+class JamsProfileView(RouteView, DBMixin, OauthMixin):
+ path = "/jams/profile"
+ name = "jams.profile"
+
+ table_name = "code_jam_participants"
+
+ def get(self):
+ if not self.user_data:
+ return redirect(url_for("discord.login"))
+
+ participant = self.db.get(self.table_name, self.user_data["user_id"])
+
+ if not participant:
+ participant = {"id": self.user_data["user_id"]}
+
+ return self.render(
+ "main/jams/profile.html", participant=participant
+ )
+
+ @csrf
+ def post(self):
+ if not self.user_data:
+ return redirect(url_for("discord.login"))
+
+ participant = self.db.get(self.table_name, self.user_data["user_id"])
+
+ if not participant:
+ participant = {"id": self.user_data["user_id"]}
+
+ dob = request.form.get("dob")
+ github_username = request.form.get("github_username")
+ timezone = request.form.get("timezone")
+
+ if not dob or not github_username or not timezone:
+ return BadRequest()
+
+ # Convert given datetime strings into actual objects, adding timezones to keep rethinkdb happy
+ dob = datetime.datetime.strptime(dob, "%Y-%m-%d")
+ dob = dob.replace(tzinfo=datetime.timezone.utc)
+
+ participant["dob"] = dob
+ participant["github_username"] = github_username
+ participant["timezone"] = timezone
+
+ self.db.insert(self.table_name, participant, conflict="replace")
+
+ return self.render(
+ "main/jams/profile.html", participant=participant, done=True
+ )
diff --git a/pysite/views/main/jams/signup.py b/pysite/views/main/jams/signup.py
deleted file mode 100644
index 632da6d6..00000000
--- a/pysite/views/main/jams/signup.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from pysite.base_route import RouteView
-
-
-class JamsSignupView(RouteView):
- path = "/jams/signup"
- name = "jams.signup"
-
- def get(self):
- return self.render("main/jams/signup.html")
diff --git a/pysite/views/staff/jams/actions.py b/pysite/views/staff/jams/actions.py
index f08b3635..1af215a5 100644
--- a/pysite/views/staff/jams/actions.py
+++ b/pysite/views/staff/jams/actions.py
@@ -183,7 +183,8 @@ class ActionView(APIView, DBMixin):
result = self.db.insert(self.infractions_table, {
"participant": participant,
"reason": reason,
- "number": number
+ "number": number,
+ "decremented_for": []
})
return jsonify({"id": result["generated_keys"][0]})