From 44453da722f7be76b50e391ca96d0b71a2c49acd Mon Sep 17 00:00:00 2001 From: Gareth Coles Date: Sat, 19 May 2018 19:34:44 +0100 Subject: [Jams] Signups code is more or less done, just a few things left --- pysite/mixins.py | 3 +- pysite/tables.py | 8 +- pysite/views/main/jams/join.py | 180 ++++++++++++++++++++ pysite/views/main/jams/signup.py | 9 - static/style.css | 11 ++ templates/main/jams/already.html | 26 +++ templates/main/jams/banned.html | 44 +++++ templates/main/jams/index.html | 2 +- templates/main/jams/join.html | 357 +++++++++++++++++++++++++++++++++++++++ templates/main/jams/signup.html | 15 -- templates/main/jams/thanks.html | 25 +++ templates/main/navigation.html | 6 - 12 files changed, 651 insertions(+), 35 deletions(-) create mode 100644 pysite/views/main/jams/join.py delete mode 100644 pysite/views/main/jams/signup.py create mode 100644 templates/main/jams/already.html create mode 100644 templates/main/jams/banned.html create mode 100644 templates/main/jams/join.html delete mode 100644 templates/main/jams/signup.html create mode 100644 templates/main/jams/thanks.html 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 c180e161..c68feba0 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,14 +101,15 @@ 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 + "id", # str "skill_level", # str "age", # str "github_username", # str diff --git a/pysite/views/main/jams/join.py b/pysite/views/main/jams/join.py new file mode 100644 index 00000000..87a2c4ca --- /dev/null +++ b/pysite/views/main/jams/join.py @@ -0,0 +1,180 @@ +from email.utils import parseaddr + +from flask import request, redirect, url_for +from werkzeug.exceptions import NotFound, BadRequest + +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/" + 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")) + + if self.get_response(jam, self.user_data["user_id"]): + return self.render("main/jams/already.html", jam=jam_obj) + + 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) + + 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")) + + if self.get_response(jam, self.user_data["user_id"]): + return self.render("main/jams/already.html", jam=jam_obj) + + 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) + + 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 + } + + print(response) + 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) + print(result) + + 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/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/static/style.css b/static/style.css index 2b9b329c..1f3f9f58 100644 --- a/static/style.css +++ b/static/style.css @@ -179,4 +179,15 @@ select { left: auto !important; -webkit-appearance: unset !important; opacity: 1 !important; +} + +div.danger-input * { + /*border-radius: 5px;*/ + /*padding: 0.8rem;*/ + /*min-height: 3.5rem;*/ + color: red; + border-color: red !important; + + transition: color 0.5s ease, + border-color 0.5s ease; } \ No newline at end of file diff --git a/templates/main/jams/already.html b/templates/main/jams/already.html new file mode 100644 index 00000000..0baaf4a9 --- /dev/null +++ b/templates/main/jams/already.html @@ -0,0 +1,26 @@ +{% extends "main/base.html" %} +{% block title %}Code Jams | Banned{% endblock %} +{% block og_title %}Code Jams | Banned{% endblock %} + +{% block content %} +
+
+

+ Code Jam {{ jam.number }}: {{ jam.title }} +

+ + +

+ Thanks for your interest in this code jam! It looks like we already have an application here for you, + so please just sit back, relax, and we'll let you know whether you've been selected for this code + jam when the time comes. +

+ + +  Back to all code jams + +
+
+{% endblock %} diff --git a/templates/main/jams/banned.html b/templates/main/jams/banned.html new file mode 100644 index 00000000..adc82fef --- /dev/null +++ b/templates/main/jams/banned.html @@ -0,0 +1,44 @@ +{% extends "main/base.html" %} +{% block title %}Code Jams | Banned{% endblock %} +{% block og_title %}Code Jams | Banned{% endblock %} + +{% block content %} +
+
+

+ Code Jam {{ jam.number }}: {{ jam.title }} +

+ + + {% if infraction.number == -1 %} +

+ Thanks for your interest in this code jam! Unfortunately, due to your previous actions, you have been + permanently banned from participating in our code jams. +
+
+ The reason given is: {{ infraction.reason }} +
+
+ If you feel that this is a mistake, please feel free to contact one of the admins on Discord. +

+ {% else %} +

+ Thanks for your interest in this code jam! Unfortunately, due to your previous actions, you have been + temporarily banned from participating in our code jams. +
+
+ The reason given is: {{ infraction.reason }} +
+
+ If you feel that this is a mistake, please feel free to contact one of the admins on Discord. +

+ {% endif + + +  Back to all code jams + +
+
+{% endblock %} diff --git a/templates/main/jams/index.html b/templates/main/jams/index.html index a5d2a5d0..71bce999 100644 --- a/templates/main/jams/index.html +++ b/templates/main/jams/index.html @@ -61,7 +61,7 @@ Code Jam {{ jam.number }}: {{ jam.title }} {% if jam.state == "announced" %} - +  Join {% else %} diff --git a/templates/main/jams/join.html b/templates/main/jams/join.html new file mode 100644 index 00000000..ffa80cb5 --- /dev/null +++ b/templates/main/jams/join.html @@ -0,0 +1,357 @@ +{% extends "main/base.html" %} +{% block title %}Code Jams | Join{% endblock %} +{% block og_title %}Code Jams | Join{% endblock %} + +{% macro show_question(question) %} +
+
+ {% if question.optional %} + + {% else %} + + {% endif %} +
+
+ {% if question.type == "checkbox" %} + {% if question.optional %} + + + {% else %} + + + {% endif %} + + {% elif question.type == "email" %} + {% if question.optional %} + + {% else %} + + {% endif %} + + {% elif question.type == "number" %} + {% if question.optional %} + + {% else %} + + {% endif %} + + {% elif question.type == "radio" %} + {% if question.optional %} + {% for option in question.data.options %} + + + {% endfor %} + {% else %} + {% for option in question.data.options %} + + + {% endfor %} + {% endif %} + + {% elif question.type == "range" %} +
+ {% if question.optional %} + {% for num in range(question.data.min, question.data.max + 1) %} + + + + + {% endfor %} + {% else %} + {% for num in range(question.data.min, question.data.max + 1) %} + + + + + {% endfor %} + {% endif %} +
+ + {% elif question.type == "text" %} + {% if question.optional %} + + {% else %} + + {% endif %} + + {% elif question.type == "textarea" %} + {% if question.optional %} + + {% else %} + + {% endif %} + + {% elif question.type == "slider" %} +
+ + +
+ + {% endif %} +
+
+{% endmacro %} + +{% block content %} +
+
+

+ Code Jam {{ jam.number }}: {{ jam.title }} +

+ +

+ Please fill out the form below to apply for this code jam. Once you've submitted your application and the + application window has closed, we'll review it and let you know whether you've been entered! +

+

+ Please note that you will not be able to edit your application after you've submitted it. +

+
+ + {% if jam.state != "announced" %} +

+ Unfortunately, we're not accepting applications for this code jam right now - but we appreciate your + interest. Keep an eye on #announcements on Discord for information on the next jam! +

+ {% else %} +
+ {% for question in questions %} + {{ show_question(question) }} +
+ {% endfor %} +
+ + + +
+ +  Back + + +
+
+ + {% endif %} +
+
+ + +{% endblock %} diff --git a/templates/main/jams/signup.html b/templates/main/jams/signup.html deleted file mode 100644 index ddb48733..00000000 --- a/templates/main/jams/signup.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "main/base.html" %} -{% block title %}Home{% endblock %} -{% block og_title %}Home{% endblock %} -{% block content %} -
-
-

- Sign Up -

- -
-
-{% endblock %} diff --git a/templates/main/jams/thanks.html b/templates/main/jams/thanks.html new file mode 100644 index 00000000..e6709485 --- /dev/null +++ b/templates/main/jams/thanks.html @@ -0,0 +1,25 @@ +{% extends "main/base.html" %} +{% block title %}Code Jams | Banned{% endblock %} +{% block og_title %}Code Jams | Banned{% endblock %} + +{% block content %} +
+
+

+ Code Jam {{ jam.number }}: {{ jam.title }} +

+ + +

+ Thanks for your application! Just sit back, relax, and we'll let you know whether you've been selected + for this code jam when the time comes. +

+ + +  Back to all code jams + +
+
+{% endblock %} diff --git a/templates/main/navigation.html b/templates/main/navigation.html index 391bbcf5..4e2ed369 100644 --- a/templates/main/navigation.html +++ b/templates/main/navigation.html @@ -107,12 +107,6 @@
  • Info
  • {% endif %} - {% if current_page == "main.jams.signup" %} -
  • Sign Up
  • - {% else %} -
  • Sign Up
  • - {% endif %} -
  • {% if current_page.startswith("main.about.privacy") %} -- cgit v1.2.3