diff options
Diffstat (limited to 'pysite')
25 files changed, 271 insertions, 50 deletions
diff --git a/pysite/base_route.py b/pysite/base_route.py index e6bd00ad..bb50afd9 100644 --- a/pysite/base_route.py +++ b/pysite/base_route.py @@ -2,15 +2,15 @@ from collections import Iterable  from datetime import datetime  from typing import Any -from flask import Blueprint, Response, jsonify, redirect, render_template, url_for +from flask import Blueprint, Response, jsonify, redirect, render_template, session, url_for  from flask.views import MethodView  from werkzeug.exceptions import default_exceptions -from pysite.constants import DEBUG_MODE, ErrorCodes -from pysite.mixins import OauthMixin +from pysite.constants import ALL_STAFF_ROLES, DEBUG_MODE, ErrorCodes +from pysite.mixins import OAuthMixin -class BaseView(MethodView, OauthMixin): +class BaseView(MethodView, OAuthMixin):      """      Base view class with functions and attributes that should be common to all view classes. @@ -52,10 +52,26 @@ class BaseView(MethodView, OauthMixin):          context["current_page"] = self.name          context["view"] = self          context["logged_in"] = self.logged_in +        context["user"] = self.user_data          context["static_file"] = self._static_file          context["debug"] = DEBUG_MODE          context["format_datetime"] = lambda dt: dt.strftime("%b %d %Y, %H:%M") if isinstance(dt, datetime) else dt +        def is_staff(): +            if DEBUG_MODE: +                return True + +            if not self.logged_in: +                return False + +            for role in ALL_STAFF_ROLES: +                if role in self.user_data.get("roles", []): +                    return True + +            return False + +        context["is_staff"] = is_staff +          return render_template(template_names, **context)      def _static_file(self, filename): @@ -103,6 +119,14 @@ class RouteView(BaseView):          cls.name = f"{blueprint.name}.{cls.name}"  # Add blueprint to page name +    def redirect_login(self, **kwargs): +        session["redirect_target"] = { +            "url": self.name, +            "kwargs": kwargs +        } + +        return redirect(url_for("discord.login")) +  class APIView(RouteView):      """ diff --git a/pysite/constants.py b/pysite/constants.py index be1bd9f8..f95e076f 100644 --- a/pysite/constants.py +++ b/pysite/constants.py @@ -39,7 +39,7 @@ DISCORD_OAUTH_REDIRECT = "/auth/discord"  DISCORD_OAUTH_AUTHORIZED = "/auth/discord/authorized"  DISCORD_OAUTH_ID = environ.get('DISCORD_OAUTH_ID', '')  DISCORD_OAUTH_SECRET = environ.get('DISCORD_OAUTH_SECRET', '') -DISCORD_OAUTH_SCOPE = 'identify email guilds.join' +DISCORD_OAUTH_SCOPE = 'identify'  OAUTH_DATABASE = "oauth_data"  PREFERRED_URL_SCHEME = environ.get("PREFERRED_URL_SCHEME", "http") diff --git a/pysite/decorators.py b/pysite/decorators.py index 16d555f0..705c519e 100644 --- a/pysite/decorators.py +++ b/pysite/decorators.py @@ -1,11 +1,11 @@  from functools import wraps  from json import JSONDecodeError -from flask import redirect, request, url_for +from flask import request  from schema import Schema, SchemaError  from werkzeug.exceptions import Forbidden -from pysite.base_route import APIView, BaseView +from pysite.base_route import APIView, RouteView  from pysite.constants import BOT_API_KEY, CSRF, DEBUG_MODE, ErrorCodes, ValidationTypes @@ -27,21 +27,22 @@ def require_roles(*roles: int):      def inner_decorator(f):          @wraps(f) -        def inner(self: BaseView, *args, **kwargs): +        def inner(self: RouteView, *args, **kwargs):              data = self.user_data +            print(kwargs)              if DEBUG_MODE:                  return f(self, *args, **kwargs)              elif data:                  for role in roles: -                    if DEBUG_MODE or role in data.get("roles", []): +                    if role in data.get("roles", []):                          return f(self, *args, **kwargs)                  if isinstance(self, APIView):                      return self.error(ErrorCodes.unauthorized)                  raise Forbidden() -            return redirect(url_for("discord.login")) +            return self.redirect_login(**kwargs)          return inner @@ -78,7 +79,7 @@ def api_params(schema: Schema, validation_type: ValidationTypes = ValidationType      def inner_decorator(f):          @wraps(f) -        def inner(self: BaseView, *args, **kwargs): +        def inner(self: APIView, *args, **kwargs):              if validation_type == ValidationTypes.json:                  try:                      if not request.is_json: diff --git a/pysite/migrations/tables/oauth_data/v1.py b/pysite/migrations/tables/oauth_data/v1.py index dc7417bb..9ace6bf9 100644 --- a/pysite/migrations/tables/oauth_data/v1.py +++ b/pysite/migrations/tables/oauth_data/v1.py @@ -2,6 +2,10 @@ from rethinkdb import ReqlOpFailedError  def run(db, table, table_obj): +    """ +    Create a secondary index on the "snowflake" key, so we can easily get documents by matching that key +    """ +      try:          db.run(db.query(table).index_create("snowflake"))          db.run(db.query(table).index_wait("snowflake")) diff --git a/pysite/migrations/tables/users/__init__.py b/pysite/migrations/tables/users/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/pysite/migrations/tables/users/__init__.py diff --git a/pysite/migrations/tables/users/v1.py b/pysite/migrations/tables/users/v1.py new file mode 100644 index 00000000..9ba70142 --- /dev/null +++ b/pysite/migrations/tables/users/v1.py @@ -0,0 +1,11 @@ +def run(db, table, table_obj): +    """ +    Remove stored email addresses from every user document +    """ + +    for document in db.get_all(table): +        if "email" in document: +            del document["email"] + +            db.insert(table, document, conflict="update", durability="soft") +    db.sync(table) diff --git a/pysite/migrations/tables/wiki/v1.py b/pysite/migrations/tables/wiki/v1.py index a5282f28..22670342 100644 --- a/pysite/migrations/tables/wiki/v1.py +++ b/pysite/migrations/tables/wiki/v1.py @@ -1,4 +1,8 @@  def run(db, table, table_obj): +    """ +    Ensure that there are no wiki articles that don't have titles +    """ +      for document in db.pluck(table, table_obj.primary_key, "title"):          if not document.get("title"):              document["title"] = "No Title" diff --git a/pysite/mixins.py b/pysite/mixins.py index 6e5032ab..d0e822bf 100644 --- a/pysite/mixins.py +++ b/pysite/mixins.py @@ -4,7 +4,7 @@ from flask import Blueprint  from rethinkdb.ast import Table  from pysite.database import RethinkDB -from pysite.oauth import OauthBackend +from pysite.oauth import OAuthBackend  class DBMixin: @@ -58,7 +58,7 @@ class DBMixin:          return self._db() -class OauthMixin: +class OAuthMixin:      """      Mixin for the classes that need access to a logged in user's information. This class should be used      to grant route's access to user information, such as name, email, id, ect. @@ -80,11 +80,11 @@ class OauthMixin:          user_data returns None, if the user isn't logged in. -        * oauth (OauthBackend): The instance of pysite.oauth.OauthBackend, connected to the RouteManager. +        * oauth (OAuthBackend): The instance of pysite.oauth.OAuthBackend, connected to the RouteManager.      """      @classmethod -    def setup(cls: "OauthMixin", manager: "pysite.route_manager.RouteManager", blueprint: Blueprint): +    def setup(cls: "OAuthMixin", manager: "pysite.route_manager.RouteManager", blueprint: Blueprint):          if hasattr(super(), "setup"):              super().setup(manager, blueprint)  # pragma: no cover @@ -99,5 +99,5 @@ class OauthMixin:          return self.oauth.user_data()      @property -    def oauth(self) -> OauthBackend: +    def oauth(self) -> OAuthBackend:          return self._oauth() diff --git a/pysite/oauth.py b/pysite/oauth.py index d025ea37..86e7cdde 100644 --- a/pysite/oauth.py +++ b/pysite/oauth.py @@ -8,7 +8,7 @@ from flask_dance.contrib.discord import discord  from pysite.constants import DISCORD_API_ENDPOINT, OAUTH_DATABASE -class OauthBackend(BaseBackend): +class OAuthBackend(BaseBackend):      """      This is the backend for the oauth @@ -34,7 +34,6 @@ class OauthBackend(BaseBackend):          pass      def set(self, blueprint, token): -          user = self.get_user()          sess_id = str(uuid5(uuid4(), self.key))          self.add_user(token, user, sess_id) @@ -62,8 +61,7 @@ class OauthBackend(BaseBackend):              {                  "user_id": user_data["id"],                  "username": user_data["username"], -                "discriminator": user_data["discriminator"], -                "email": user_data["email"] +                "discriminator": user_data["discriminator"]              },              conflict="update"          ) @@ -85,3 +83,4 @@ class OauthBackend(BaseBackend):          sess_id = session.get("session_id")          if sess_id and self.db.get(OAUTH_DATABASE, sess_id):  # If user exists in db,              self.db.delete(OAUTH_DATABASE, sess_id)  # remove them (at least, their session) +            session.clear() diff --git a/pysite/route_manager.py b/pysite/route_manager.py index eacd74b4..c899cf02 100644 --- a/pysite/route_manager.py +++ b/pysite/route_manager.py @@ -13,7 +13,7 @@ from pysite.constants import (      CSRF, DEBUG_MODE, DISCORD_OAUTH_AUTHORIZED, DISCORD_OAUTH_ID, DISCORD_OAUTH_REDIRECT,      DISCORD_OAUTH_SCOPE, DISCORD_OAUTH_SECRET, PREFERRED_URL_SCHEME)  from pysite.database import RethinkDB -from pysite.oauth import OauthBackend +from pysite.oauth import OAuthBackend  from pysite.websockets import WS  TEMPLATES_PATH = "../templates" @@ -51,14 +51,14 @@ class RouteManager:          CSRF.init_app(self.app)  # Set up CSRF protection          # Load the oauth blueprint -        self.oauth_backend = OauthBackend(self) +        self.oauth_backend = OAuthBackend(self)          self.oauth_blueprint = make_discord_blueprint(              DISCORD_OAUTH_ID,              DISCORD_OAUTH_SECRET,              DISCORD_OAUTH_SCOPE, -            '/',              login_url=DISCORD_OAUTH_REDIRECT,              authorized_url=DISCORD_OAUTH_AUTHORIZED, +            redirect_to="main.auth.done",              backend=self.oauth_backend          )          self.log.debug(f"Loading Blueprint: {self.oauth_blueprint.name}") diff --git a/pysite/tables.py b/pysite/tables.py index a9a0dc88..be43c588 100644 --- a/pysite/tables.py +++ b/pysite/tables.py @@ -190,8 +190,7 @@ TABLES = {              "user_id",              "roles",              "username", -            "discriminator", -            "email" +            "discriminator"          ])      ), diff --git a/pysite/views/api/bot/user.py b/pysite/views/api/bot/user.py index a353ccfe..8c5d8f77 100644 --- a/pysite/views/api/bot/user.py +++ b/pysite/views/api/bot/user.py @@ -26,6 +26,8 @@ DELETE_SCHEMA = Schema([      }  ]) +BANNABLE_STATES = ("preparing", "running") +  class UserView(APIView, DBMixin):      path = "/bot/users" @@ -33,6 +35,9 @@ class UserView(APIView, DBMixin):      table_name = "users"      oauth_table_name = "oauth_data"      participants_table = "code_jam_participants" +    infractions_table = "code_jam_infractions" +    jams_table = "code_jams" +    responses_table = "code_jam_responses"      @api_key      @api_params(schema=SCHEMA, validation_type=ValidationTypes.json) @@ -42,6 +47,8 @@ class UserView(APIView, DBMixin):          deletions = 0          oauth_deletions = 0          profile_deletions = 0 +        response_deletions = 0 +        bans = 0          user_ids = [user["user_id"] for user in data] @@ -56,10 +63,42 @@ 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 +                user_id = item["snowflake"] + +                oauth_deletions += self.db.delete( +                    self.oauth_table_name, item["id"], durability="soft", return_changes=True +                ).get("deleted", 0) +                profile_deletions += self.db.delete( +                    self.participants_table, user_id, durability="soft", return_changes=True +                ).get("deleted", 0) + +                banned = False +                responses = self.db.run( +                    self.db.query(self.responses_table).filter({"snowflake": user_id}), +                    coerce=list +                ) + +                for response in responses: +                    jam = response["jam"] +                    jam_obj = self.db.get(self.jams_table, jam) + +                    if jam_obj: +                        if jam_obj["state"] in BANNABLE_STATES: +                            banned = True + +                    self.db.delete(self.responses_table, response["id"], durability="soft") +                    response_deletions += 1 + +                if banned: +                    self.db.insert( +                        self.infractions_table, { +                            "participant": user_id, +                            "reason": "Automatic ban: Removed jammer profile in the middle of a code jam", +                            "number": -1, +                            "decremented_for": [] +                        }, durability="soft" +                    ) +                    bans += 1          del user_ids @@ -69,11 +108,17 @@ class UserView(APIView, DBMixin):              durability="soft"          ) +        self.db.sync(self.infractions_table) +        self.db.sync(self.oauth_table_name) +        self.db.sync(self.participants_table) +        self.db.sync(self.responses_table)          self.db.sync(self.table_name)          changes["deleted"] = deletions          changes["deleted_oauth"] = oauth_deletions          changes["deleted_jam_profiles"] = profile_deletions +        changes["deleted_responses"] = response_deletions +        changes["jam_bans"] = bans          return jsonify(changes)  # pragma: no cover @@ -108,9 +153,40 @@ class UserView(APIView, DBMixin):              self.db.query(self.participants_table)              .get_all(*user_ids)              .delete() -        ) +        ).get("deleted", 0) + +        bans = 0 +        response_deletions = 0 + +        for user_id in user_ids: +            banned = False +            responses = self.db.run(self.db.query(self.responses_table).filter({"snowflake": user_id}), coerce=list) + +            for response in responses: +                jam = response["jam"] +                jam_obj = self.db.get(self.jams_table, jam) + +                if jam_obj: +                    if jam_obj["state"] in BANNABLE_STATES: +                        banned = True + +                self.db.delete(self.responses_table, response["id"]) +                response_deletions += 1 + +            if banned: +                self.db.insert( +                    self.infractions_table, { +                        "participant": user_id, +                        "reason": "Automatic ban: Removed jammer profile in the middle of a code jam", +                        "number": -1, +                        "decremented_for": [] +                    } +                ) +                bans += 1          changes["deleted_oauth"] = oauth_deletions          changes["deleted_jam_profiles"] = profile_deletions +        changes["deleted_responses"] = response_deletions +        changes["jam_bans"] = bans          return jsonify(changes)  # pragma: no cover diff --git a/pysite/views/main/auth/__init__.py b/pysite/views/main/auth/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/pysite/views/main/auth/__init__.py diff --git a/pysite/views/main/auth/done.py b/pysite/views/main/auth/done.py new file mode 100644 index 00000000..6e892906 --- /dev/null +++ b/pysite/views/main/auth/done.py @@ -0,0 +1,18 @@ +from flask import redirect, session, url_for + +from pysite.base_route import RouteView + + +class AuthDoneView(RouteView): +    path = "/auth/done" +    name = "auth.done" + +    def get(self): +        if self.logged_in: +            target = session.get("redirect_target") + +            if target: +                del session["redirect_target"] +                return redirect(url_for(target["url"], **target.get("kwargs", {}))) + +        return redirect(url_for("main.index")) diff --git a/pysite/views/main/jams/index.py b/pysite/views/main/jams/index.py index 6d066117..8d34fa50 100644 --- a/pysite/views/main/jams/index.py +++ b/pysite/views/main/jams/index.py @@ -16,6 +16,6 @@ class JamsIndexView(RouteView, DBMixin):              .order_by(rethinkdb.desc("number"))              .limit(5)          ) +          jams = self.db.run(query, coerce=list) -        print(jams)          return self.render("main/jams/index.html", jams=jams) diff --git a/pysite/views/main/jams/join.py b/pysite/views/main/jams/join.py index 24931f72..c4011170 100644 --- a/pysite/views/main/jams/join.py +++ b/pysite/views/main/jams/join.py @@ -5,10 +5,10 @@ from werkzeug.exceptions import BadRequest, NotFound  from pysite.base_route import RouteView  from pysite.decorators import csrf -from pysite.mixins import DBMixin, OauthMixin +from pysite.mixins import DBMixin, OAuthMixin -class JamsJoinView(RouteView, DBMixin, OauthMixin): +class JamsJoinView(RouteView, DBMixin, OAuthMixin):      path = "/jams/join/<int:jam>"      name = "jams.join" @@ -26,7 +26,7 @@ class JamsJoinView(RouteView, DBMixin, OauthMixin):              return NotFound()          if not self.user_data: -            return redirect(url_for("discord.login")) +            return self.redirect_login(jam=jam)          infractions = self.get_infractions(self.user_data["user_id"]) @@ -76,7 +76,7 @@ class JamsJoinView(RouteView, DBMixin, OauthMixin):              return NotFound()          if not self.user_data: -            return redirect(url_for("discord.login")) +            return self.redirect_login(jam=jam)          infractions = self.get_infractions(self.user_data["user_id"]) diff --git a/pysite/views/main/jams/profile.py b/pysite/views/main/jams/profile.py index ce8dfdf1..d8a663f7 100644 --- a/pysite/views/main/jams/profile.py +++ b/pysite/views/main/jams/profile.py @@ -5,10 +5,10 @@ from werkzeug.exceptions import BadRequest  from pysite.base_route import RouteView  from pysite.decorators import csrf -from pysite.mixins import DBMixin, OauthMixin +from pysite.mixins import DBMixin, OAuthMixin -class JamsProfileView(RouteView, DBMixin, OauthMixin): +class JamsProfileView(RouteView, DBMixin, OAuthMixin):      path = "/jams/profile"      name = "jams.profile" @@ -16,12 +16,14 @@ class JamsProfileView(RouteView, DBMixin, OauthMixin):      def get(self):          if not self.user_data: -            return redirect(url_for("discord.login")) +            return self.redirect_login()          participant = self.db.get(self.table_name, self.user_data["user_id"]) +        existing = True          if not participant:              participant = {"id": self.user_data["user_id"]} +            existing = False          form = request.args.get("form") @@ -32,13 +34,13 @@ class JamsProfileView(RouteView, DBMixin, OauthMixin):                  pass  # Someone trying to have some fun I guess          return self.render( -            "main/jams/profile.html", participant=participant, form=form +            "main/jams/profile.html", participant=participant, form=form, existing=existing          )      @csrf      def post(self):          if not self.user_data: -            return redirect(url_for("discord.login")) +            return self.redirect_login()          participant = self.db.get(self.table_name, self.user_data["user_id"]) @@ -56,6 +58,12 @@ class JamsProfileView(RouteView, DBMixin, OauthMixin):          dob = datetime.datetime.strptime(dob, "%Y-%m-%d")          dob = dob.replace(tzinfo=datetime.timezone.utc) +        now = datetime.datetime.now(tz=datetime.timezone.utc) +        then = now.replace(year=now.year - 13) + +        if then < dob: +            raise BadRequest()  # They're too young, but this is validated on the form +          participant["dob"] = dob          participant["github_username"] = github_username          participant["timezone"] = timezone @@ -73,5 +81,5 @@ class JamsProfileView(RouteView, DBMixin, OauthMixin):                  return redirect(url_for("main.jams.join", jam=form))          return self.render( -            "main/jams/profile.html", participant=participant, done=True +            "main/jams/profile.html", participant=participant, done=True, existing=True          ) diff --git a/pysite/views/main/jams/retract.py b/pysite/views/main/jams/retract.py new file mode 100644 index 00000000..277426b5 --- /dev/null +++ b/pysite/views/main/jams/retract.py @@ -0,0 +1,83 @@ +from werkzeug.exceptions import BadRequest + +from pysite.base_route import RouteView +from pysite.decorators import csrf +from pysite.mixins import DBMixin, OAuthMixin + +BANNABLE_STATES = ("preparing", "running") + + +class JamsProfileView(RouteView, DBMixin, OAuthMixin): +    path = "/jams/retract" +    name = "jams.retract" + +    table_name = "code_jam_participants" +    infractions_table = "code_jam_infractions" +    jams_table = "code_jams" +    responses_table = "code_jam_responses" + +    def get(self): +        if not self.user_data: +            return self.redirect_login() + +        user_id = self.user_data["user_id"] +        participant = self.db.get(self.table_name, user_id) + +        banned = False + +        if participant: +            responses = self.db.run(self.db.query(self.responses_table).filter({"snowflake": user_id}), coerce=list) + +            for response in responses: +                jam = response["jam"] +                jam_obj = self.db.get(self.jams_table, jam) + +                if jam_obj: +                    if jam_obj["state"] in BANNABLE_STATES: +                        banned = True +                        break + +        return self.render( +            "main/jams/retract.html", participant=participant, banned=banned +        ) + +    @csrf +    def post(self): +        if not self.user_data: +            return self.redirect_login() + +        user_id = self.user_data["user_id"] +        participant = self.db.get(self.table_name, user_id) + +        if not participant: +            return BadRequest() + +        banned = False + +        responses = self.db.run(self.db.query(self.responses_table).filter({"snowflake": user_id}), coerce=list) + +        for response in responses: +            jam = response["jam"] +            jam_obj = self.db.get(self.jams_table, jam) + +            if jam_obj: +                if jam_obj["state"] in BANNABLE_STATES: +                    banned = True + +            self.db.delete(self.responses_table, response["id"]) + +        self.db.delete(self.table_name, participant["id"]) + +        if banned: +            self.db.insert( +                self.infractions_table, { +                    "participant": user_id, +                    "reason": "Automatic ban: Removed jammer profile in the middle of a code jam", +                    "number": -1, +                    "decremented_for": [] +                } +            ) + +        return self.render( +            "main/jams/retracted.html", participant=participant, banned=banned +        ) diff --git a/pysite/views/main/logout.py b/pysite/views/main/logout.py index 2461450d..64326371 100644 --- a/pysite/views/main/logout.py +++ b/pysite/views/main/logout.py @@ -1,4 +1,4 @@ -from flask import redirect, session +from flask import redirect, session, url_for  from pysite.base_route import RouteView @@ -12,4 +12,5 @@ class LogoutView(RouteView):              # remove user's session              del session["session_id"]              self.oauth.logout() -        return redirect("/") + +        return redirect(url_for("main.index")) diff --git a/pysite/views/staff/jams/actions.py b/pysite/views/staff/jams/actions.py index 1af215a5..059f8969 100644 --- a/pysite/views/staff/jams/actions.py +++ b/pysite/views/staff/jams/actions.py @@ -33,7 +33,6 @@ class ActionView(APIView, DBMixin):          if action == "questions":              questions = self.db.get_all(self.questions_table) -            print(questions)              return jsonify({"questions": questions})      @csrf diff --git a/pysite/views/staff/jams/edit_ending.py b/pysite/views/staff/jams/edit_ending.py index c4dcfcb3..43a36ebc 100644 --- a/pysite/views/staff/jams/edit_ending.py +++ b/pysite/views/staff/jams/edit_ending.py @@ -39,7 +39,6 @@ class StaffView(RouteView, DBMixin):          if not jam_obj["state"] in ALLOWED_STATES:              return BadRequest() -        print(request.form)          for key in REQUIRED_KEYS:              arg = request.form.get(key) diff --git a/pysite/views/staff/jams/edit_info.py b/pysite/views/staff/jams/edit_info.py index 7d4401f0..ad0d3d41 100644 --- a/pysite/views/staff/jams/edit_info.py +++ b/pysite/views/staff/jams/edit_info.py @@ -39,7 +39,6 @@ class StaffView(RouteView, DBMixin):          if not jam_obj["state"] in ALLOWED_STATES:              return BadRequest() -        print(request.form)          for key in REQUIRED_KEYS:              arg = request.form.get(key) diff --git a/pysite/views/staff/jams/forms/questions_edit.py b/pysite/views/staff/jams/forms/questions_edit.py index 4de06793..d46c4ef3 100644 --- a/pysite/views/staff/jams/forms/questions_edit.py +++ b/pysite/views/staff/jams/forms/questions_edit.py @@ -42,8 +42,6 @@ class StaffView(RouteView, DBMixin):          optional = request.form.get("optional")          question_type = request.form.get("type") -        print(question_type) -          if not title or not optional or not question_type:              return BadRequest() diff --git a/pysite/views/staff/render.py b/pysite/views/staff/render.py index 00c9a9f3..0152e568 100644 --- a/pysite/views/staff/render.py +++ b/pysite/views/staff/render.py @@ -57,7 +57,6 @@ class RenderView(APIView):                          }                      ) -            print(data)              return jsonify(data)          except Exception as e:              return jsonify({"error": str(e)}) diff --git a/pysite/views/wiki/render.py b/pysite/views/wiki/render.py index b08f54dd..40e5d3f4 100644 --- a/pysite/views/wiki/render.py +++ b/pysite/views/wiki/render.py @@ -57,7 +57,6 @@ class RenderView(APIView):                          }                      ) -            print(data)              return jsonify(data)          except Exception as e:              return jsonify({"error": str(e)})  |