diff options
Diffstat (limited to 'pysite')
-rw-r--r-- | pysite/mixins.py | 3 | ||||
-rw-r--r-- | pysite/tables.py | 11 | ||||
-rw-r--r-- | pysite/views/api/bot/user.py | 13 | ||||
-rw-r--r-- | pysite/views/main/jams/join.py | 188 | ||||
-rw-r--r-- | pysite/views/main/jams/profile.py | 59 | ||||
-rw-r--r-- | pysite/views/main/jams/signup.py | 9 | ||||
-rw-r--r-- | pysite/views/staff/jams/actions.py | 3 |
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]}) |