From 9bccf62a1fef94c3c71b88c9b2ac5078c29d3741 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Mon, 3 May 2021 13:26:01 +0200 Subject: [PATCH 01/21] Commented question alternative API --- client/package-lock.json | 3 ++- server/app/apis/alternatives.py | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 72fcea71..2655ad2e 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -17285,7 +17285,8 @@ }, "ssri": { "version": "6.0.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "requires": { "figgy-pudding": "^3.5.1" } diff --git a/server/app/apis/alternatives.py b/server/app/apis/alternatives.py index 94f6d6b1..d054baa9 100644 --- a/server/app/apis/alternatives.py +++ b/server/app/apis/alternatives.py @@ -1,10 +1,14 @@ +""" +All API calls concerning question alternatives. +Default route: /api/competitions/<competition_id>/slides/<slide_id>/questions/<question_id>/alternatives +""" + import app.core.http_codes as codes import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import QuestionAlternativeDTO -from flask_restx import Resource -from flask_restx import reqparse from app.core.parsers import sentinel +from flask_restx import Resource, reqparse api = QuestionAlternativeDTO.api schema = QuestionAlternativeDTO.schema @@ -24,11 +28,15 @@ alternative_parser_edit.add_argument("value", type=int, default=sentinel, locati class QuestionAlternativeList(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def get(self, competition_id, slide_id, question_id): + """ Gets the all question alternatives to the specified question. """ + items = dbc.get.question_alternative_list(competition_id, slide_id, question_id) return list_response(list_schema.dump(items)) @protect_route(allowed_roles=["*"]) def post(self, competition_id, slide_id, question_id): + """ Posts a new question alternative to the specified question. """ + args = alternative_parser_add.parse_args(strict=True) item = dbc.add.question_alternative(**args, question_id=question_id) return item_response(schema.dump(item)) @@ -39,11 +47,15 @@ class QuestionAlternativeList(Resource): class QuestionAlternatives(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def get(self, competition_id, slide_id, question_id, alternative_id): + """ Gets the specified question alternative. """ + items = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id) return item_response(schema.dump(items)) @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id, question_id, alternative_id): + """ Edits the specified question alternative. """ + args = alternative_parser_edit.parse_args(strict=True) item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id) item = dbc.edit.default(item, **args) @@ -51,6 +63,8 @@ class QuestionAlternatives(Resource): @protect_route(allowed_roles=["*"]) def delete(self, competition_id, slide_id, question_id, alternative_id): + """ Deletes the specified question alternative. """ + item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id) dbc.delete.default(item) return {}, codes.NO_CONTENT -- GitLab From 0dfec37b7fb4125dcf247c7a2c30d03d953081e0 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Mon, 3 May 2021 13:36:09 +0200 Subject: [PATCH 02/21] Commented question answer API --- server/app/apis/answers.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/server/app/apis/answers.py b/server/app/apis/answers.py index 6d0490a9..109bfe57 100644 --- a/server/app/apis/answers.py +++ b/server/app/apis/answers.py @@ -1,9 +1,13 @@ +""" +All API calls concerning question answers. +Default route: /api/competitions/<competition_id>/teams/<team_id>/answers +""" + import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import QuestionAnswerDTO -from flask_restx import Resource -from flask_restx import reqparse from app.core.parsers import sentinel +from flask_restx import Resource, reqparse api = QuestionAnswerDTO.api schema = QuestionAnswerDTO.schema @@ -24,11 +28,15 @@ answer_parser_edit.add_argument("score", type=int, default=sentinel, location="j class QuestionAnswerList(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def get(self, competition_id, team_id): + """ Gets the all question answers that the specified team has given. """ + items = dbc.get.question_answer_list(competition_id, team_id) return list_response(list_schema.dump(items)) @protect_route(allowed_roles=["*"], allowed_views=["*"]) def post(self, competition_id, team_id): + """ Posts a new question answer to the specified question. """ + args = answer_parser_add.parse_args(strict=True) item = dbc.add.question_answer(**args, team_id=team_id) return item_response(schema.dump(item)) @@ -39,12 +47,19 @@ class QuestionAnswerList(Resource): class QuestionAnswers(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def get(self, competition_id, team_id, answer_id): + """ Gets the specified question answer. """ + item = dbc.get.question_answer(competition_id, team_id, answer_id) return item_response(schema.dump(item)) @protect_route(allowed_roles=["*"], allowed_views=["*"]) def put(self, competition_id, team_id, answer_id): + """ Edits the specified question answer. """ + args = answer_parser_edit.parse_args(strict=True) item = dbc.get.question_answer(competition_id, team_id, answer_id) item = dbc.edit.default(item, **args) return item_response(schema.dump(item)) + + # No need to delete an answer. It only needs to be deleted + # together with the question or the team. -- GitLab From 947b8289e76f8200f877b590a25cb4c3a7ff95e8 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 21:14:29 +0200 Subject: [PATCH 03/21] Add Auth api file header --- server/app/apis/auth.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py index bf9eeefd..9a4b78d0 100644 --- a/server/app/apis/auth.py +++ b/server/app/apis/auth.py @@ -1,3 +1,8 @@ +""" +All API calls concerning question answers. +Default route: /api/auth +""" + from datetime import timedelta import app.core.http_codes as codes -- GitLab From 043ae4d6fe5d0d6592463a1a8f0329e79a12cd02 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 21:24:40 +0200 Subject: [PATCH 04/21] Commented auth API --- server/app/apis/auth.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py index d399401e..0b34c473 100644 --- a/server/app/apis/auth.py +++ b/server/app/apis/auth.py @@ -37,10 +37,14 @@ USER_LOGIN_LOCKED_EXPIRES = current_app.config["USER_LOGIN_LOCKED_EXPIRES"] def get_user_claims(item_user): + """ Gets user details for jwt-token. """ + return {"role": item_user.role.name, "city_id": item_user.city_id} def get_code_claims(item_code): + """ Gets code details for jwt-token. """ + return { "view": item_code.view_type.name, "competition_id": item_code.competition_id, @@ -53,6 +57,8 @@ def get_code_claims(item_code): class AuthSignup(Resource): @protect_route(allowed_roles=["Admin"], allowed_views=["*"]) def get(self): + """ Tests that the user is an admin. """ + return "ok" @@ -60,6 +66,8 @@ class AuthSignup(Resource): class AuthSignup(Resource): @protect_route(allowed_roles=["Admin"]) def post(self): + """ Creates a new user if the user does not already exist. """ + args = create_user_parser.parse_args(strict=True) email = args.get("email") @@ -77,9 +85,12 @@ class AuthSignup(Resource): class AuthDelete(Resource): @protect_route(allowed_roles=["Admin"]) def delete(self, user_id): + """ Deletes a user and adds their token to the blacklist. """ + item_user = dbc.get.user(user_id) - # Blacklist all the whitelisted tokens in use for the user that will be deleted + # Blacklist all the whitelisted tokens + # in use for the user that will be deleted dbc.delete.whitelist_to_blacklist(Whitelist.user_id == user_id) # Delete user @@ -90,6 +101,8 @@ class AuthDelete(Resource): @api.route("/login") class AuthLogin(Resource): def post(self): + """ Logs in a user and creates a jwt-token. """ + args = login_parser.parse_args(strict=True) email = args.get("email") password = args.get("password") @@ -138,6 +151,8 @@ class AuthLogin(Resource): @api.route("/login/code") class AuthLoginCode(Resource): def post(self): + """ Logs in using a competition code. """ + args = login_code_parser.parse_args() code = args["code"] @@ -171,6 +186,8 @@ class AuthLoginCode(Resource): class AuthLogout(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def post(self): + """ Logs out. """ + jti = get_raw_jwt()["jti"] # Blacklist the token so the user cannot access the api anymore -- GitLab From 582480a585bf1971b73a5b22eb1ae24bc97cf380 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 22:11:09 +0200 Subject: [PATCH 05/21] Cleanup of comments in get --- server/app/database/controller/get.py | 103 +++++++++++++++++--------- 1 file changed, 67 insertions(+), 36 deletions(-) diff --git a/server/app/database/controller/get.py b/server/app/database/controller/get.py index b6701ca5..aafa7664 100644 --- a/server/app/database/controller/get.py +++ b/server/app/database/controller/get.py @@ -2,7 +2,6 @@ This file contains functionality to get data from the database. """ -from sqlalchemy.orm.util import with_polymorphic from app.core import db from app.core import http_codes as codes from app.database.models import ( @@ -18,11 +17,12 @@ from app.database.models import ( TextComponent, User, ) -from sqlalchemy.orm import joinedload, subqueryload +from sqlalchemy.orm import joinedload +from sqlalchemy.orm.util import with_polymorphic def all(db_type): - """ Gets lazy db-item in the provided table. """ + """ Gets a list of all lazy db-items in the provided table. """ return db_type.query.all() @@ -43,7 +43,10 @@ def code_by_code(code): def code_list(competition_id): - """ Gets a list of all code objects associated with a the provided competition. """ + """ + Gets a list of all code objects associated with the provided competition. + """ + # team_view_id = 1 join_competition = Competition.id == Code.competition_id filters = Competition.id == competition_id @@ -58,19 +61,23 @@ def user_exists(email): def user(user_id): - """ Gets the user object associated with the provided id. """ + """ Gets the user object associated with the provided user. """ return User.query.filter(User.id == user_id).first_extended() def user_by_email(email): """ Gets the user object associated with the provided email. """ + return User.query.filter(User.email == email).first_extended(error_code=codes.UNAUTHORIZED) ### Slides ### def slide(competition_id, slide_id): - """ Gets the slide object associated with the provided id and order. """ + """ + Gets the slide object associated with the provided competition and slide. + """ + join_competition = Competition.id == Slide.competition_id filters = (Competition.id == competition_id) & (Slide.id == slide_id) @@ -78,7 +85,10 @@ def slide(competition_id, slide_id): def slide_list(competition_id): - """ Gets a list of all slide objects associated with a the provided competition. """ + """ + Gets a list of all slide objects associated with the provided competition. + """ + join_competition = Competition.id == Slide.competition_id filters = Competition.id == competition_id @@ -91,15 +101,10 @@ def slide_count(competition_id): return Slide.query.filter(Slide.competition_id == competition_id).count() -def slide_count(competition_id): - """ Gets the number of slides in the provided competition. """ - - return Slide.query.filter(Slide.competition_id == competition_id).count() - - ### Teams ### def team(competition_id, team_id): - """ Gets the team object associated with the provided id and competition id. """ + """ Gets the team object associated with the competition and team. """ + join_competition = Competition.id == Team.competition_id filters = (Competition.id == competition_id) & (Team.id == team_id) @@ -107,19 +112,22 @@ def team(competition_id, team_id): def team_list(competition_id): - """ Gets a list of all team objects associated with a the provided competition. """ + """ + Gets a list of all team objects associated with the provided competition. + """ join_competition = Competition.id == Team.competition_id filters = Competition.id == competition_id return Team.query.join(Competition, join_competition).filter(filters).all() - return Team.query.join(Competition, join_competition).filter(filters).all() - ### Questions ### def question(competition_id, slide_id, question_id): - """ Gets the question object associated with the provided id, slide order and competition id. """ + """ + Gets the question object associated with the + provided, competition, slide and question. + """ join_competition = Competition.id == Slide.competition_id join_slide = Slide.id == Question.slide_id @@ -129,7 +137,10 @@ def question(competition_id, slide_id, question_id): def question_list(competition_id, slide_id): - """ Gets a list of all question objects associated with a the provided competition and slide. """ + """ + Gets a list of all question objects associated + with the provided competition and slide. + """ join_competition = Competition.id == Slide.competition_id join_slide = Slide.id == Question.slide_id @@ -139,7 +150,10 @@ def question_list(competition_id, slide_id): def question_list_for_competition(competition_id): - """ Gets a list of all question objects associated with a the provided competition. """ + """ + Gets a list of all question objects associated + with the provided competition. + """ join_competition = Competition.id == Slide.competition_id join_slide = Slide.id == Question.slide_id @@ -149,8 +163,16 @@ def question_list_for_competition(competition_id): ### Question Alternative ### -def question_alternative(competition_id, slide_id, question_id, alternative_id): - """ Get question alternative for a given question based on its competition and slide and ID. """ +def question_alternative( + competition_id, + slide_id, + question_id, + alternative_id, +): + """ + Get a question alternative for a given question + based on its competition, slide and question. + """ join_competition = Competition.id == Slide.competition_id join_slide = Slide.id == Question.slide_id @@ -172,7 +194,11 @@ def question_alternative(competition_id, slide_id, question_id, alternative_id): def question_alternative_list(competition_id, slide_id, question_id): - """ Get all question alternatives for a given question based on its competition and slide. """ + """ + Get a list of all question alternative objects for a + given question based on its competition and slide. + """ + join_competition = Competition.id == Slide.competition_id join_slide = Slide.id == Question.slide_id join_question = Question.id == QuestionAlternative.question_id @@ -186,18 +212,13 @@ def question_alternative_list(competition_id, slide_id, question_id): .all() ) - return ( - QuestionAlternative.query.join(Competition, join_competition) - .join(Slide, join_slide) - .join(Question, join_question) - .filter(filters) - .all() - ) - ### Question Answers ### def question_answer(competition_id, team_id, answer_id): - """ Get question answer for a given team based on its competition and ID. """ + """ + Get question answer for a given team based on its competition. + """ + join_competition = Competition.id == Team.competition_id join_team = Team.id == QuestionAnswer.team_id filters = (Competition.id == competition_id) & (Team.id == team_id) & (QuestionAnswer.id == answer_id) @@ -207,7 +228,10 @@ def question_answer(competition_id, team_id, answer_id): def question_answer_list(competition_id, team_id): - """ Get question answer for a given team based on its competition. """ + """ + Get a list of question answers for a given team based on its competition. + """ + join_competition = Competition.id == Team.competition_id join_team = Team.id == QuestionAnswer.team_id filters = (Competition.id == competition_id) & (Team.id == team_id) @@ -216,7 +240,10 @@ def question_answer_list(competition_id, team_id): ### Components ### def component(competition_id, slide_id, component_id): - """ Gets a list of all component objects associated with a the provided competition id and slide order. """ + """ + Gets a component object associated with + the provided competition id and slide order. + """ join_competition = Competition.id == Slide.competition_id join_slide = Slide.id == Component.slide_id @@ -233,7 +260,10 @@ def component(competition_id, slide_id, component_id): def component_list(competition_id, slide_id): - """ Gets a list of all component objects associated with a the provided competition id and slide order. """ + """ + Gets a list of all component objects associated with + the provided competition and slide. + """ join_competition = Competition.id == Slide.competition_id join_slide = Slide.id == Component.slide_id @@ -243,7 +273,8 @@ def component_list(competition_id, slide_id): ### Competitions ### def competition(competition_id): - """ Get Competition and all it's sub-entities """ + """ Get Competition and all it's sub-entities. """ + os1 = joinedload(Competition.slides).joinedload(Slide.components) os2 = joinedload(Competition.slides).joinedload(Slide.questions).joinedload(Question.alternatives) ot = joinedload(Competition.teams).joinedload(Team.question_answers) -- GitLab From 5bb5576bab04b40784077b515f904d8c0a04c7d1 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 22:22:12 +0200 Subject: [PATCH 06/21] Many small changes to comments --- server/app/database/controller/add.py | 41 +++++++++++++++++++----- server/app/database/controller/copy.py | 9 +++--- server/app/database/controller/delete.py | 14 +++++--- server/app/database/controller/utils.py | 6 +++- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py index 3aa0ab70..9c8827e2 100644 --- a/server/app/database/controller/add.py +++ b/server/app/database/controller/add.py @@ -49,25 +49,34 @@ def db_add(item): except (exc.SQLAlchemyError, exc.DBAPIError): db.session.rollback() # SQL errors such as item already exists - abort(codes.INTERNAL_SERVER_ERROR, f"Item of type {type(item)} could not be created") + abort( + codes.INTERNAL_SERVER_ERROR, + f"Item of type {type(item)} could not be created", + ) except: db.session.rollback() # Catching other errors - abort(codes.INTERNAL_SERVER_ERROR, f"Something went wrong when creating {type(item)}") + abort( + codes.INTERNAL_SERVER_ERROR, + f"Something went wrong when creating {type(item)}", + ) return item def component(type_id, slide_id, view_type_id, x=0, y=0, w=0, h=0, **data): """ - Adds a component to the slide at the specified coordinates with the - provided size and data . + Adds a component to the slide at the specified + coordinates with the provided size and data. """ if type_id == 2: # 2 is image item_image = get.one(Media, data["media_id"]) filename = item_image.filename - path = os.path.join(current_app.config["UPLOADED_PHOTOS_DEST"], filename) + path = os.path.join( + current_app.config["UPLOADED_PHOTOS_DEST"], + filename, + ) with Image.open(path) as im: h = im.height w = im.width @@ -79,13 +88,19 @@ def component(type_id, slide_id, view_type_id, x=0, y=0, w=0, h=0, **data): h *= ratio if type_id == ID_TEXT_COMPONENT: - item = db_add(TextComponent(slide_id, type_id, view_type_id, x, y, w, h)) + item = db_add( + TextComponent(slide_id, type_id, view_type_id, x, y, w, h), + ) item.text = data.get("text") elif type_id == ID_IMAGE_COMPONENT: - item = db_add(ImageComponent(slide_id, type_id, view_type_id, x, y, w, h)) + item = db_add( + ImageComponent(slide_id, type_id, view_type_id, x, y, w, h), + ) item.media_id = data.get("media_id") elif type_id == ID_QUESTION_COMPONENT: - item = db_add(QuestionComponent(slide_id, type_id, view_type_id, x, y, w, h)) + item = db_add( + QuestionComponent(slide_id, type_id, view_type_id, x, y, w, h), + ) item.question_id = data.get("question_id") else: abort(codes.BAD_REQUEST, f"Invalid type_id{type_id}") @@ -258,8 +273,18 @@ def question(name, total_score, type_id, slide_id, correcting_instructions=None) def question_alternative(text, value, question_id): + """ + Adds a question alternative to the specified + question using the provided arguments. + """ + return db_add(QuestionAlternative(text, value, question_id)) def question_answer(answer, score, question_id, team_id): + """ + Adds a question answer to the specified team + and question using the provided arguments. + """ + return db_add(QuestionAnswer(answer, score, question_id, team_id)) diff --git a/server/app/database/controller/copy.py b/server/app/database/controller/copy.py index 48dab2db..dd807334 100644 --- a/server/app/database/controller/copy.py +++ b/server/app/database/controller/copy.py @@ -8,7 +8,9 @@ from app.database.types import ID_IMAGE_COMPONENT, ID_QUESTION_COMPONENT, ID_TEX def _alternative(item_old, question_id): - """Internal function. Makes a copy of the provided question alternative""" + """ + Internal function. Makes a copy of the provided question alternative. + """ return add.question_alternative(item_old.text, item_old.value, question_id) @@ -73,7 +75,7 @@ def component(item_component, slide_id_new, view_type_id): def slide(item_slide_old): """ Deep copies a slide to the same competition. - Does not copy team, question answers. + Does not copy team and question answers. """ item_competition = get.competition(item_slide_old.competition_id) @@ -98,7 +100,6 @@ def slide_to_competition(item_slide_old, item_competition): for item_component in item_slide_old.components: _component(item_component, item_slide_new) - for item_question in item_slide_old.questions: _question(item_question, item_slide_new.id) @@ -123,7 +124,7 @@ def competition(item_competition_old): item_competition_old.city_id, item_competition_old.font, ) - # TODO: Add background image + item_competition_new.background_image_id = item_competition_old.background_image_id for item_slide in item_competition_old.slides: diff --git a/server/app/database/controller/delete.py b/server/app/database/controller/delete.py index b0b36fba..65737cb6 100644 --- a/server/app/database/controller/delete.py +++ b/server/app/database/controller/delete.py @@ -11,19 +11,25 @@ from flask_restx import abort def default(item): """ Deletes item and commits. """ + try: db.session.delete(item) db.session.commit() except: db.session.rollback() - abort(codes.INTERNAL_SERVER_ERROR, f"Item of type {type(item)} could not be deleted") + abort( + codes.INTERNAL_SERVER_ERROR, + f"Item of type {type(item)} could not be deleted", + ) def whitelist_to_blacklist(filters): """ - Remove whitelist by condition(filters) and insert those into blacklist - Example: When delete user all whitelisted tokens for that user should be blacklisted + Remove whitelist by condition(filters) and insert those into blacklist. + Example: When delete user all whitelisted tokens for that user should + be blacklisted. """ + whitelist = Whitelist.query.filter(filters).all() for item in whitelist: dbc.add.blacklist(item.jti) @@ -43,7 +49,6 @@ def _slide(item_slide): for item_question in item_slide.questions: question(item_question) - for item_component in item_slide.components: default(item_component) @@ -85,6 +90,7 @@ def question(item_question): question_answers(item_question_answer) for item_alternative in item_question.alternatives: alternatives(item_alternative) + default(item_question) diff --git a/server/app/database/controller/utils.py b/server/app/database/controller/utils.py index 14eaa48d..f27ce32b 100644 --- a/server/app/database/controller/utils.py +++ b/server/app/database/controller/utils.py @@ -10,6 +10,8 @@ from flask_restx import abort def move_slides(item_competition, start_order, end_order): + """ Changes a slide order and then arranges other affected slides. """ + slides = item_competition.slides # Move up if start_order < end_order: @@ -40,6 +42,7 @@ def generate_unique_code(): def refresh(item): """ Refreshes the provided item. """ + try: db.session.refresh(item) except Exception as e: @@ -49,7 +52,8 @@ def refresh(item): def commit(): - """ Commits. """ + """ Commits to the database. """ + try: db.session.commit() except Exception as e: -- GitLab From 94b03a8e05cec29c9d83c7e363c1b4991e04d985 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 22:24:40 +0200 Subject: [PATCH 07/21] Commented code API --- server/app/apis/codes.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/app/apis/codes.py b/server/app/apis/codes.py index 6409109e..45f95782 100644 --- a/server/app/apis/codes.py +++ b/server/app/apis/codes.py @@ -1,3 +1,8 @@ +""" +All API calls concerning competition codes. +Default route: /api/competitions/<competition_id>/codes +""" + import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import CodeDTO @@ -14,6 +19,8 @@ list_schema = CodeDTO.list_schema class CodesList(Resource): @protect_route(allowed_roles=["*"], allowed_views=["Operator"]) def get(self, competition_id): + """ Gets the all competition codes. """ + items = dbc.get.code_list(competition_id) return list_response(list_schema.dump(items), len(items)) @@ -23,6 +30,8 @@ class CodesList(Resource): class CodesById(Resource): @protect_route(allowed_roles=["*"]) def put(self, competition_id, code_id): + """ Generates a new competition code. """ + item = dbc.get.one(Code, code_id) item.code = dbc.utils.generate_unique_code() dbc.utils.commit_and_refresh(item) -- GitLab From e127b555a04633e9372b1ab265134c15e8a27d38 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 22:31:24 +0200 Subject: [PATCH 08/21] Commented competition API --- server/app/apis/competitions.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py index bfd06fac..61ecb4b4 100644 --- a/server/app/apis/competitions.py +++ b/server/app/apis/competitions.py @@ -1,10 +1,14 @@ +""" +All API calls concerning competitions. +Default route: /api/competitions +""" + import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import CompetitionDTO -from app.database.models import Competition -from flask_restx import Resource -from flask_restx import reqparse from app.core.parsers import search_parser, sentinel +from app.database.models import Competition +from flask_restx import Resource, reqparse api = CompetitionDTO.api schema = CompetitionDTO.schema @@ -32,6 +36,8 @@ competition_parser_search.add_argument("city_id", type=int, default=sentinel, lo class CompetitionsList(Resource): @protect_route(allowed_roles=["*"]) def post(self): + """ Posts a new competition. """ + args = competition_parser_add.parse_args(strict=True) # Add competition @@ -47,12 +53,16 @@ class CompetitionsList(Resource): class Competitions(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def get(self, competition_id): + """ Gets the specified competition. """ + item = dbc.get.competition(competition_id) return item_response(rich_schema.dump(item)) @protect_route(allowed_roles=["*"]) def put(self, competition_id): + """ Edits a competition with the specified arguments. """ + args = competition_parser_edit.parse_args(strict=True) item = dbc.get.one(Competition, competition_id) item = dbc.edit.default(item, **args) @@ -61,6 +71,8 @@ class Competitions(Resource): @protect_route(allowed_roles=["*"]) def delete(self, competition_id): + """ Deletes a competition. """ + item = dbc.get.one(Competition, competition_id) dbc.delete.competition(item) @@ -71,6 +83,8 @@ class Competitions(Resource): class CompetitionSearch(Resource): @protect_route(allowed_roles=["*"]) def get(self): + """ Finds a specific competition based on the provided arguments. """ + args = competition_parser_search.parse_args(strict=True) items, total = dbc.search.competition(**args) return list_response(list_schema.dump(items), total) @@ -81,6 +95,8 @@ class CompetitionSearch(Resource): class SlidesOrder(Resource): @protect_route(allowed_roles=["*"]) def post(self, competition_id): + """ Creates a deep copy of the specified competition. """ + item_competition = dbc.get.competition(competition_id) item_competition_copy = dbc.copy.competition(item_competition) -- GitLab From 7a09e31c36eed70f3927b8fb18e6d4ce420aea18 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 22:31:24 +0200 Subject: [PATCH 09/21] Commented competition API --- server/app/apis/competitions.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py index bfd06fac..2d381425 100644 --- a/server/app/apis/competitions.py +++ b/server/app/apis/competitions.py @@ -1,10 +1,14 @@ +""" +All API calls concerning competitions. +Default route: /api/competitions +""" + import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import CompetitionDTO -from app.database.models import Competition -from flask_restx import Resource -from flask_restx import reqparse from app.core.parsers import search_parser, sentinel +from app.database.models import Competition +from flask_restx import Resource, reqparse api = CompetitionDTO.api schema = CompetitionDTO.schema @@ -32,6 +36,8 @@ competition_parser_search.add_argument("city_id", type=int, default=sentinel, lo class CompetitionsList(Resource): @protect_route(allowed_roles=["*"]) def post(self): + """ Posts a new competition. """ + args = competition_parser_add.parse_args(strict=True) # Add competition @@ -47,12 +53,16 @@ class CompetitionsList(Resource): class Competitions(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def get(self, competition_id): + """ Gets the specified competition. """ + item = dbc.get.competition(competition_id) return item_response(rich_schema.dump(item)) @protect_route(allowed_roles=["*"]) def put(self, competition_id): + """ Edits the specified competition with the specified arguments. """ + args = competition_parser_edit.parse_args(strict=True) item = dbc.get.one(Competition, competition_id) item = dbc.edit.default(item, **args) @@ -61,6 +71,8 @@ class Competitions(Resource): @protect_route(allowed_roles=["*"]) def delete(self, competition_id): + """ Deletes the specified competition. """ + item = dbc.get.one(Competition, competition_id) dbc.delete.competition(item) @@ -71,6 +83,8 @@ class Competitions(Resource): class CompetitionSearch(Resource): @protect_route(allowed_roles=["*"]) def get(self): + """ Finds a specific competition based on the provided arguments. """ + args = competition_parser_search.parse_args(strict=True) items, total = dbc.search.competition(**args) return list_response(list_schema.dump(items), total) @@ -81,6 +95,8 @@ class CompetitionSearch(Resource): class SlidesOrder(Resource): @protect_route(allowed_roles=["*"]) def post(self, competition_id): + """ Creates a deep copy of the specified competition. """ + item_competition = dbc.get.competition(competition_id) item_competition_copy = dbc.copy.competition(item_competition) -- GitLab From 81659f0e5310cefbfda4e52319e0bea6c24202f8 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 22:44:03 +0200 Subject: [PATCH 10/21] Changed to "the specified" --- server/app/apis/answers.py | 2 +- server/app/apis/auth.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/app/apis/answers.py b/server/app/apis/answers.py index 109bfe57..d32d285f 100644 --- a/server/app/apis/answers.py +++ b/server/app/apis/answers.py @@ -54,7 +54,7 @@ class QuestionAnswers(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def put(self, competition_id, team_id, answer_id): - """ Edits the specified question answer. """ + """ Edits the specified question answer with the provided arguments. """ args = answer_parser_edit.parse_args(strict=True) item = dbc.get.question_answer(competition_id, team_id, answer_id) diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py index 0b34c473..49b5a3e5 100644 --- a/server/app/apis/auth.py +++ b/server/app/apis/auth.py @@ -85,7 +85,7 @@ class AuthSignup(Resource): class AuthDelete(Resource): @protect_route(allowed_roles=["Admin"]) def delete(self, user_id): - """ Deletes a user and adds their token to the blacklist. """ + """ Deletes the specified user and adds their token to the blacklist. """ item_user = dbc.get.user(user_id) @@ -101,7 +101,7 @@ class AuthDelete(Resource): @api.route("/login") class AuthLogin(Resource): def post(self): - """ Logs in a user and creates a jwt-token. """ + """ Logs in the specified user and creates a jwt-token. """ args = login_parser.parse_args(strict=True) email = args.get("email") @@ -151,7 +151,7 @@ class AuthLogin(Resource): @api.route("/login/code") class AuthLoginCode(Resource): def post(self): - """ Logs in using a competition code. """ + """ Logs in using the provided competition code. """ args = login_code_parser.parse_args() code = args["code"] -- GitLab From 7706944ab955d26c093f78b4f78659c0eea39ddf Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 22:44:03 +0200 Subject: [PATCH 11/21] Changed to "the specified" --- server/app/apis/answers.py | 4 ++-- server/app/apis/auth.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/app/apis/answers.py b/server/app/apis/answers.py index 109bfe57..23e46b26 100644 --- a/server/app/apis/answers.py +++ b/server/app/apis/answers.py @@ -28,7 +28,7 @@ answer_parser_edit.add_argument("score", type=int, default=sentinel, location="j class QuestionAnswerList(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def get(self, competition_id, team_id): - """ Gets the all question answers that the specified team has given. """ + """ Gets all question answers that the specified team has given. """ items = dbc.get.question_answer_list(competition_id, team_id) return list_response(list_schema.dump(items)) @@ -54,7 +54,7 @@ class QuestionAnswers(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def put(self, competition_id, team_id, answer_id): - """ Edits the specified question answer. """ + """ Edits the specified question answer with the provided arguments. """ args = answer_parser_edit.parse_args(strict=True) item = dbc.get.question_answer(competition_id, team_id, answer_id) diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py index 0b34c473..49b5a3e5 100644 --- a/server/app/apis/auth.py +++ b/server/app/apis/auth.py @@ -85,7 +85,7 @@ class AuthSignup(Resource): class AuthDelete(Resource): @protect_route(allowed_roles=["Admin"]) def delete(self, user_id): - """ Deletes a user and adds their token to the blacklist. """ + """ Deletes the specified user and adds their token to the blacklist. """ item_user = dbc.get.user(user_id) @@ -101,7 +101,7 @@ class AuthDelete(Resource): @api.route("/login") class AuthLogin(Resource): def post(self): - """ Logs in a user and creates a jwt-token. """ + """ Logs in the specified user and creates a jwt-token. """ args = login_parser.parse_args(strict=True) email = args.get("email") @@ -151,7 +151,7 @@ class AuthLogin(Resource): @api.route("/login/code") class AuthLoginCode(Resource): def post(self): - """ Logs in using a competition code. """ + """ Logs in using the provided competition code. """ args = login_code_parser.parse_args() code = args["code"] -- GitLab From fa9f3fbc267c58d63cacfa9cb4bde69e5f42246d Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 22:53:07 +0200 Subject: [PATCH 12/21] Commented component API --- server/app/apis/components.py | 56 ++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/server/app/apis/components.py b/server/app/apis/components.py index 9fff1d07..af647c76 100644 --- a/server/app/apis/components.py +++ b/server/app/apis/components.py @@ -1,10 +1,14 @@ +""" +All API calls concerning competitions. +Default route: /api/competitions<competition_id>/slides/<slide_id>/components +""" + import app.core.http_codes as codes import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import ComponentDTO -from flask_restx import Resource -from flask_restx import reqparse from app.core.parsers import sentinel +from flask_restx import Resource, reqparse api = ComponentDTO.api schema = ComponentDTO.schema @@ -31,16 +35,39 @@ component_parser_edit.add_argument("media_id", type=int, default=sentinel, locat component_parser_edit.add_argument("question_id", type=int, default=sentinel, location="json") +@api.route("") +@api.param("competition_id, slide_id") +class ComponentList(Resource): + @protect_route(allowed_roles=["*"], allowed_views=["*"]) + def get(self, competition_id, slide_id): + """ Gets all components in the specified slide and competition. """ + + items = dbc.get.component_list(competition_id, slide_id) + return list_response(list_schema.dump(items)) + + @protect_route(allowed_roles=["*"]) + def post(self, competition_id, slide_id): + """ Posts a new component to the specified slide. """ + + args = component_parser_add.parse_args() + item = dbc.add.component(slide_id=slide_id, **args) + return item_response(schema.dump(item)) + + @api.route("/<component_id>") @api.param("competition_id, slide_id, component_id") class ComponentByID(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def get(self, competition_id, slide_id, component_id): + """ Gets the specified component. """ + item = dbc.get.component(competition_id, slide_id, component_id) return item_response(schema.dump(item)) @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id, component_id): + """ Edits the specified component with the provided arguments. """ + args = component_parser_edit.parse_args(strict=True) item = dbc.get.component(competition_id, slide_id, component_id) args_without_sentinel = {key: value for key, value in args.items() if value is not sentinel} @@ -49,6 +76,8 @@ class ComponentByID(Resource): @protect_route(allowed_roles=["*"]) def delete(self, competition_id, slide_id, component_id): + """ Deletes the specified component. """ + item = dbc.get.component(competition_id, slide_id, component_id) dbc.delete.component(item) return {}, codes.NO_CONTENT @@ -59,21 +88,12 @@ class ComponentByID(Resource): class ComponentList(Resource): @protect_route(allowed_roles=["*"]) def post(self, competition_id, slide_id, component_id, view_type_id): - item_component = dbc.get.component(competition_id, slide_id, component_id) - item = dbc.copy.component(item_component, slide_id, view_type_id) - return item_response(schema.dump(item)) - - -@api.route("") -@api.param("competition_id, slide_id") -class ComponentList(Resource): - @protect_route(allowed_roles=["*"], allowed_views=["*"]) - def get(self, competition_id, slide_id): - items = dbc.get.component_list(competition_id, slide_id) - return list_response(list_schema.dump(items)) + """ Creates a deep copy of the specified component. """ - @protect_route(allowed_roles=["*"]) - def post(self, competition_id, slide_id): - args = component_parser_add.parse_args() - item = dbc.add.component(slide_id=slide_id, **args) + item_component = dbc.get.component( + competition_id, + slide_id, + component_id, + ) + item = dbc.copy.component(item_component, slide_id, view_type_id) return item_response(schema.dump(item)) -- GitLab From a95d230076d991582015edff3f5bedeefc9bbc2a Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 22:59:35 +0200 Subject: [PATCH 13/21] Commented component API --- server/app/apis/components.py | 56 ++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/server/app/apis/components.py b/server/app/apis/components.py index 9fff1d07..d8598047 100644 --- a/server/app/apis/components.py +++ b/server/app/apis/components.py @@ -1,10 +1,14 @@ +""" +All API calls concerning competitions. +Default route: /api/competitions/<competition_id>/slides/<slide_id>/components +""" + import app.core.http_codes as codes import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import ComponentDTO -from flask_restx import Resource -from flask_restx import reqparse from app.core.parsers import sentinel +from flask_restx import Resource, reqparse api = ComponentDTO.api schema = ComponentDTO.schema @@ -31,16 +35,39 @@ component_parser_edit.add_argument("media_id", type=int, default=sentinel, locat component_parser_edit.add_argument("question_id", type=int, default=sentinel, location="json") +@api.route("") +@api.param("competition_id, slide_id") +class ComponentList(Resource): + @protect_route(allowed_roles=["*"], allowed_views=["*"]) + def get(self, competition_id, slide_id): + """ Gets all components in the specified slide and competition. """ + + items = dbc.get.component_list(competition_id, slide_id) + return list_response(list_schema.dump(items)) + + @protect_route(allowed_roles=["*"]) + def post(self, competition_id, slide_id): + """ Posts a new component to the specified slide. """ + + args = component_parser_add.parse_args() + item = dbc.add.component(slide_id=slide_id, **args) + return item_response(schema.dump(item)) + + @api.route("/<component_id>") @api.param("competition_id, slide_id, component_id") class ComponentByID(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def get(self, competition_id, slide_id, component_id): + """ Gets the specified component. """ + item = dbc.get.component(competition_id, slide_id, component_id) return item_response(schema.dump(item)) @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id, component_id): + """ Edits the specified component with the provided arguments. """ + args = component_parser_edit.parse_args(strict=True) item = dbc.get.component(competition_id, slide_id, component_id) args_without_sentinel = {key: value for key, value in args.items() if value is not sentinel} @@ -49,6 +76,8 @@ class ComponentByID(Resource): @protect_route(allowed_roles=["*"]) def delete(self, competition_id, slide_id, component_id): + """ Deletes the specified component. """ + item = dbc.get.component(competition_id, slide_id, component_id) dbc.delete.component(item) return {}, codes.NO_CONTENT @@ -59,21 +88,12 @@ class ComponentByID(Resource): class ComponentList(Resource): @protect_route(allowed_roles=["*"]) def post(self, competition_id, slide_id, component_id, view_type_id): - item_component = dbc.get.component(competition_id, slide_id, component_id) - item = dbc.copy.component(item_component, slide_id, view_type_id) - return item_response(schema.dump(item)) - - -@api.route("") -@api.param("competition_id, slide_id") -class ComponentList(Resource): - @protect_route(allowed_roles=["*"], allowed_views=["*"]) - def get(self, competition_id, slide_id): - items = dbc.get.component_list(competition_id, slide_id) - return list_response(list_schema.dump(items)) + """ Creates a deep copy of the specified component. """ - @protect_route(allowed_roles=["*"]) - def post(self, competition_id, slide_id): - args = component_parser_add.parse_args() - item = dbc.add.component(slide_id=slide_id, **args) + item_component = dbc.get.component( + competition_id, + slide_id, + component_id, + ) + item = dbc.copy.component(item_component, slide_id, view_type_id) return item_response(schema.dump(item)) -- GitLab From 7d05904927cee9d6cd74b6f12aa00fd7daef5f2c Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 23:08:37 +0200 Subject: [PATCH 14/21] Comment media API --- server/app/apis/media.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/server/app/apis/media.py b/server/app/apis/media.py index 49d20608..c59589a6 100644 --- a/server/app/apis/media.py +++ b/server/app/apis/media.py @@ -1,16 +1,20 @@ +""" +All API calls concerning media. +Default route: /api/media +""" + +import app.core.files as files import app.core.http_codes as codes import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import MediaDTO -from app.core.parsers import search_parser +from app.core.parsers import search_parser, sentinel from app.database.models import Media from flask import request from flask_jwt_extended import get_jwt_identity from flask_restx import Resource from flask_uploads import UploadNotAllowed from sqlalchemy import exc -import app.core.files as files -from app.core.parsers import sentinel api = MediaDTO.api image_set = MediaDTO.image_set @@ -25,12 +29,16 @@ media_parser_search.add_argument("filename", type=str, default=sentinel, locatio class ImageList(Resource): @protect_route(allowed_roles=["*"]) def get(self): + """ Gets a list of all images with the specified filename. """ + args = media_parser_search.parse_args(strict=True) items, total = dbc.search.image(**args) return list_response(list_schema.dump(items), total) @protect_route(allowed_roles=["*"]) def post(self): + """ Posts the specified image. """ + if "image" not in request.files: api.abort(codes.BAD_REQUEST, "Missing image in request.files") try: @@ -51,11 +59,15 @@ class ImageList(Resource): class ImageList(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def get(self, ID): + """ Gets the specified image. """ + item = dbc.get.one(Media, ID) return item_response(schema.dump(item)) @protect_route(allowed_roles=["*"]) def delete(self, ID): + """ Deletes the specified image. """ + item = dbc.get.one(Media, ID) try: files.delete_image_and_thumbnail(item.filename) -- GitLab From 9f4f5a196f1a6318ca9946c66440300ddea51b9e Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 23:13:08 +0200 Subject: [PATCH 15/21] Comment misc API --- server/app/apis/misc.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/server/app/apis/misc.py b/server/app/apis/misc.py index a9069f6d..552a0ad0 100644 --- a/server/app/apis/misc.py +++ b/server/app/apis/misc.py @@ -1,10 +1,14 @@ +""" +All misc API calls. +Default route: /api/misc +""" + import app.database.controller as dbc from app.apis import list_response, protect_route from app.core import http_codes from app.core.dto import MiscDTO from app.database.models import City, Competition, ComponentType, MediaType, QuestionType, Role, User, ViewType from flask_restx import Resource, reqparse -from flask_restx import reqparse api = MiscDTO.api @@ -24,6 +28,8 @@ name_parser.add_argument("name", type=str, required=True, location="json") @api.route("/types") class TypesList(Resource): def get(self): + """ Gets a list of all types. """ + result = {} result["media_types"] = media_type_schema.dump(dbc.get.all(MediaType)) result["component_types"] = component_type_schema.dump(dbc.get.all(ComponentType)) @@ -36,6 +42,8 @@ class TypesList(Resource): class RoleList(Resource): @protect_route(allowed_roles=["*"]) def get(self): + """ Gets a list of all roles. """ + items = dbc.get.all(Role) return list_response(role_schema.dump(items)) @@ -44,11 +52,15 @@ class RoleList(Resource): class CitiesList(Resource): @protect_route(allowed_roles=["*"]) def get(self): + """ Gets a list of all cities. """ + items = dbc.get.all(City) return list_response(city_schema.dump(items)) @protect_route(allowed_roles=["Admin"]) def post(self): + """ Posts the specified city. """ + args = name_parser.parse_args(strict=True) dbc.add.city(args["name"]) items = dbc.get.all(City) @@ -60,6 +72,8 @@ class CitiesList(Resource): class Cities(Resource): @protect_route(allowed_roles=["Admin"]) def put(self, ID): + """ Edits the specified city with the provided arguments. """ + item = dbc.get.one(City, ID) args = name_parser.parse_args(strict=True) item.name = args["name"] @@ -69,6 +83,8 @@ class Cities(Resource): @protect_route(allowed_roles=["Admin"]) def delete(self, ID): + """ Deletes the specified city. """ + item = dbc.get.one(City, ID) dbc.delete.default(item) items = dbc.get.all(City) @@ -79,6 +95,8 @@ class Cities(Resource): class Statistics(Resource): @protect_route(allowed_roles=["*"]) def get(self): + """ Gets statistics. """ + user_count = User.query.count() competition_count = Competition.query.count() region_count = City.query.count() -- GitLab From f41ce2c29f838aa29219d116a5e6b953cc8534e9 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 23:21:06 +0200 Subject: [PATCH 16/21] Shortened the lines --- server/app/apis/alternatives.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/server/app/apis/alternatives.py b/server/app/apis/alternatives.py index d054baa9..c95b5698 100644 --- a/server/app/apis/alternatives.py +++ b/server/app/apis/alternatives.py @@ -30,7 +30,11 @@ class QuestionAlternativeList(Resource): def get(self, competition_id, slide_id, question_id): """ Gets the all question alternatives to the specified question. """ - items = dbc.get.question_alternative_list(competition_id, slide_id, question_id) + items = dbc.get.question_alternative_list( + competition_id, + slide_id, + question_id, + ) return list_response(list_schema.dump(items)) @protect_route(allowed_roles=["*"]) @@ -49,7 +53,12 @@ class QuestionAlternatives(Resource): def get(self, competition_id, slide_id, question_id, alternative_id): """ Gets the specified question alternative. """ - items = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id) + items = dbc.get.question_alternative( + competition_id, + slide_id, + question_id, + alternative_id, + ) return item_response(schema.dump(items)) @protect_route(allowed_roles=["*"]) @@ -57,7 +66,12 @@ class QuestionAlternatives(Resource): """ Edits the specified question alternative. """ args = alternative_parser_edit.parse_args(strict=True) - item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id) + item = dbc.get.question_alternative( + competition_id, + slide_id, + question_id, + alternative_id, + ) item = dbc.edit.default(item, **args) return item_response(schema.dump(item)) @@ -65,6 +79,11 @@ class QuestionAlternatives(Resource): def delete(self, competition_id, slide_id, question_id, alternative_id): """ Deletes the specified question alternative. """ - item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id) + item = dbc.get.question_alternative( + competition_id, + slide_id, + question_id, + alternative_id, + ) dbc.delete.default(item) return {}, codes.NO_CONTENT -- GitLab From dd92e150fb2a11615adcf03097960a640625b0a1 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 23:21:37 +0200 Subject: [PATCH 17/21] Comment question API --- server/app/apis/questions.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/server/app/apis/questions.py b/server/app/apis/questions.py index 94df2a36..8807e9ac 100644 --- a/server/app/apis/questions.py +++ b/server/app/apis/questions.py @@ -1,10 +1,14 @@ +""" +All API calls concerning question answers. +Default route: /api/competitions/<competition_id> +""" + import app.core.http_codes as codes import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import QuestionDTO -from flask_restx import Resource -from flask_restx import reqparse from app.core.parsers import sentinel +from flask_restx import Resource, reqparse api = QuestionDTO.api schema = QuestionDTO.schema @@ -28,6 +32,8 @@ question_parser_edit.add_argument("correcting_instructions", type=str, default=s class QuestionList(Resource): @protect_route(allowed_roles=["*"]) def get(self, competition_id): + """ Gets all questions in the specified competition. """ + items = dbc.get.question_list_for_competition(competition_id) return list_response(list_schema.dump(items)) @@ -37,11 +43,15 @@ class QuestionList(Resource): class QuestionListForSlide(Resource): @protect_route(allowed_roles=["*"]) def get(self, competition_id, slide_id): + """ Gets all questions in the specified competition and slide. """ + items = dbc.get.question_list(competition_id, slide_id) return list_response(list_schema.dump(items)) @protect_route(allowed_roles=["*"]) def post(self, competition_id, slide_id): + """ Posts a new question to the specified slide. """ + args = question_parser_add.parse_args(strict=True) item = dbc.add.question(slide_id=slide_id, **args) return item_response(schema.dump(item)) @@ -52,11 +62,17 @@ class QuestionListForSlide(Resource): class QuestionById(Resource): @protect_route(allowed_roles=["*"]) def get(self, competition_id, slide_id, question_id): + """ + Gets the specified question using the specified competition and slide. + """ + item_question = dbc.get.question(competition_id, slide_id, question_id) return item_response(schema.dump(item_question)) @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id, question_id): + """ Edits the specified question with the provided arguments. """ + args = question_parser_edit.parse_args(strict=True) item_question = dbc.get.question(competition_id, slide_id, question_id) @@ -66,6 +82,8 @@ class QuestionById(Resource): @protect_route(allowed_roles=["*"]) def delete(self, competition_id, slide_id, question_id): + """ Deletes the specified question. """ + item_question = dbc.get.question(competition_id, slide_id, question_id) dbc.delete.question(item_question) return {}, codes.NO_CONTENT -- GitLab From 73efe5a58b435a17c3eb77fac6017640ae9d4fc8 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 23:28:02 +0200 Subject: [PATCH 18/21] Added "using the provided arguments" --- server/app/apis/alternatives.py | 9 +++++++-- server/app/apis/answers.py | 5 ++++- server/app/apis/components.py | 2 +- server/app/apis/questions.py | 2 +- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/server/app/apis/alternatives.py b/server/app/apis/alternatives.py index c95b5698..1827b358 100644 --- a/server/app/apis/alternatives.py +++ b/server/app/apis/alternatives.py @@ -39,7 +39,10 @@ class QuestionAlternativeList(Resource): @protect_route(allowed_roles=["*"]) def post(self, competition_id, slide_id, question_id): - """ Posts a new question alternative to the specified question. """ + """ + Posts a new question alternative to the specified + question using the provided arguments. + """ args = alternative_parser_add.parse_args(strict=True) item = dbc.add.question_alternative(**args, question_id=question_id) @@ -63,7 +66,9 @@ class QuestionAlternatives(Resource): @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id, question_id, alternative_id): - """ Edits the specified question alternative. """ + """ + Edits the specified question alternative using the provided arguments. + """ args = alternative_parser_edit.parse_args(strict=True) item = dbc.get.question_alternative( diff --git a/server/app/apis/answers.py b/server/app/apis/answers.py index 23e46b26..c1ccd68b 100644 --- a/server/app/apis/answers.py +++ b/server/app/apis/answers.py @@ -35,7 +35,10 @@ class QuestionAnswerList(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def post(self, competition_id, team_id): - """ Posts a new question answer to the specified question. """ + """ + Posts a new question answer to the specified + question using the provided arguments. + """ args = answer_parser_add.parse_args(strict=True) item = dbc.add.question_answer(**args, team_id=team_id) diff --git a/server/app/apis/components.py b/server/app/apis/components.py index d8598047..39e19328 100644 --- a/server/app/apis/components.py +++ b/server/app/apis/components.py @@ -66,7 +66,7 @@ class ComponentByID(Resource): @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id, component_id): - """ Edits the specified component with the provided arguments. """ + """ Edits the specified component using the provided arguments. """ args = component_parser_edit.parse_args(strict=True) item = dbc.get.component(competition_id, slide_id, component_id) diff --git a/server/app/apis/questions.py b/server/app/apis/questions.py index 8807e9ac..6ba32382 100644 --- a/server/app/apis/questions.py +++ b/server/app/apis/questions.py @@ -50,7 +50,7 @@ class QuestionListForSlide(Resource): @protect_route(allowed_roles=["*"]) def post(self, competition_id, slide_id): - """ Posts a new question to the specified slide. """ + """ Posts a new question to the specified slide using the provided arguments. """ args = question_parser_add.parse_args(strict=True) item = dbc.add.question(slide_id=slide_id, **args) -- GitLab From 517bdfff6214cbb9094c38ae0792cd1b80d19970 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 23:29:27 +0200 Subject: [PATCH 19/21] Comment slide API --- server/app/apis/slides.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py index aa5bee3c..b44941bf 100644 --- a/server/app/apis/slides.py +++ b/server/app/apis/slides.py @@ -1,3 +1,8 @@ +""" +All API calls concerning question alternatives. +Default route: /api/competitions/<competition_id>/slides +""" + import app.core.http_codes as codes import app.database.controller as dbc from app.apis import item_response, list_response, protect_route @@ -21,25 +26,33 @@ slide_parser_edit.add_argument("background_image_id", default=sentinel, type=int class SlidesList(Resource): @protect_route(allowed_roles=["*"]) def get(self, competition_id): + """ Gets the all slides to the specified competition. """ + items = dbc.get.slide_list(competition_id) return list_response(list_schema.dump(items)) @protect_route(allowed_roles=["*"]) def post(self, competition_id): + """ Posts a new slide to the specified competition. """ + item_slide = dbc.add.slide(competition_id) return item_response(schema.dump(item_slide)) @api.route("/<slide_id>") -@api.param("competition_id,slide_id") +@api.param("competition_id, slide_id") class Slides(Resource): @protect_route(allowed_roles=["*"]) def get(self, competition_id, slide_id): + """ Gets the specified slide. """ + item_slide = dbc.get.slide(competition_id, slide_id) return item_response(schema.dump(item_slide)) @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id): + """ Edits the specified slide using the provided arguments. """ + args = slide_parser_edit.parse_args(strict=True) item_slide = dbc.get.slide(competition_id, slide_id) @@ -49,6 +62,8 @@ class Slides(Resource): @protect_route(allowed_roles=["*"]) def delete(self, competition_id, slide_id): + """ Deletes the specified slide. """ + item_slide = dbc.get.slide(competition_id, slide_id) dbc.delete.slide(item_slide) @@ -56,10 +71,12 @@ class Slides(Resource): @api.route("/<slide_id>/order") -@api.param("competition_id,slide_id") +@api.param("competition_id, slide_id") class SlideOrder(Resource): @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id): + """ Edits the specified slide order using the provided arguments. """ + args = slide_parser_edit.parse_args(strict=True) order = args.get("order") @@ -89,8 +106,9 @@ class SlideOrder(Resource): class SlideCopy(Resource): @protect_route(allowed_roles=["*"]) def post(self, competition_id, slide_id): - item_slide = dbc.get.slide(competition_id, slide_id) + """ Creates a deep copy of the specified slide. """ + item_slide = dbc.get.slide(competition_id, slide_id) item_slide_copy = dbc.copy.slide(item_slide) return item_response(schema.dump(item_slide_copy)) -- GitLab From 8053980fc8c028bfa24a857408960dff2ff4577e Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 23:33:23 +0200 Subject: [PATCH 20/21] Comment team API --- server/app/apis/slides.py | 2 +- server/app/apis/teams.py | 31 +++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py index b44941bf..945e1084 100644 --- a/server/app/apis/slides.py +++ b/server/app/apis/slides.py @@ -26,7 +26,7 @@ slide_parser_edit.add_argument("background_image_id", default=sentinel, type=int class SlidesList(Resource): @protect_route(allowed_roles=["*"]) def get(self, competition_id): - """ Gets the all slides to the specified competition. """ + """ Gets the all slides from the specified competition. """ items = dbc.get.slide_list(competition_id) return list_response(list_schema.dump(items)) diff --git a/server/app/apis/teams.py b/server/app/apis/teams.py index 71ca715d..82b88567 100644 --- a/server/app/apis/teams.py +++ b/server/app/apis/teams.py @@ -1,9 +1,14 @@ +""" +All API calls concerning question alternatives. +Default route: /api/competitions/<competition_id>/teams +""" + import app.core.http_codes as codes import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import TeamDTO -from flask_restx import Resource, reqparse from app.core.parsers import sentinel +from flask_restx import Resource, reqparse api = TeamDTO.api schema = TeamDTO.schema @@ -21,11 +26,15 @@ team_parser_edit.add_argument("name", type=str, default=sentinel, location="json class TeamsList(Resource): @protect_route(allowed_roles=["*"]) def get(self, competition_id): + """ Gets the all teams to the specified competition. """ + items = dbc.get.team_list(competition_id) return list_response(list_schema.dump(items)) @protect_route(allowed_roles=["*"]) def post(self, competition_id): + """ Posts a new team to the specified competition. """ + args = team_parser_add.parse_args(strict=True) item_team = dbc.add.team(args["name"], competition_id) return item_response(schema.dump(item_team)) @@ -36,18 +45,15 @@ class TeamsList(Resource): class Teams(Resource): @protect_route(allowed_roles=["*"]) def get(self, competition_id, team_id): + """ Gets the specified team. """ + item = dbc.get.team(competition_id, team_id) return item_response(schema.dump(item)) - @protect_route(allowed_roles=["*"]) - def delete(self, competition_id, team_id): - item_team = dbc.get.team(competition_id, team_id) - - dbc.delete.team(item_team) - return {}, codes.NO_CONTENT - @protect_route(allowed_roles=["*"]) def put(self, competition_id, team_id): + """ Edits the specified team using the provided arguments. """ + args = team_parser_edit.parse_args(strict=True) name = args.get("name") @@ -55,3 +61,12 @@ class Teams(Resource): item_team = dbc.edit.default(item_team, name=name, competition_id=competition_id) return item_response(schema.dump(item_team)) + + @protect_route(allowed_roles=["*"]) + def delete(self, competition_id, team_id): + """ Deletes the specified team. """ + + item_team = dbc.get.team(competition_id, team_id) + + dbc.delete.team(item_team) + return {}, codes.NO_CONTENT -- GitLab From 30829366f835d7c4745175ae329c7f7b271af400 Mon Sep 17 00:00:00 2001 From: Josef Olsson <josol381@student.liu.se> Date: Wed, 5 May 2021 23:37:37 +0200 Subject: [PATCH 21/21] Comment user API --- server/app/apis/slides.py | 2 +- server/app/apis/teams.py | 2 +- server/app/apis/users.py | 23 +++++++++++++++++++---- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py index 945e1084..ee9689b5 100644 --- a/server/app/apis/slides.py +++ b/server/app/apis/slides.py @@ -26,7 +26,7 @@ slide_parser_edit.add_argument("background_image_id", default=sentinel, type=int class SlidesList(Resource): @protect_route(allowed_roles=["*"]) def get(self, competition_id): - """ Gets the all slides from the specified competition. """ + """ Gets all slides from the specified competition. """ items = dbc.get.slide_list(competition_id) return list_response(list_schema.dump(items)) diff --git a/server/app/apis/teams.py b/server/app/apis/teams.py index 82b88567..913deeb7 100644 --- a/server/app/apis/teams.py +++ b/server/app/apis/teams.py @@ -26,7 +26,7 @@ team_parser_edit.add_argument("name", type=str, default=sentinel, location="json class TeamsList(Resource): @protect_route(allowed_roles=["*"]) def get(self, competition_id): - """ Gets the all teams to the specified competition. """ + """ Gets all teams to the specified competition. """ items = dbc.get.team_list(competition_id) return list_response(list_schema.dump(items)) diff --git a/server/app/apis/users.py b/server/app/apis/users.py index dc26ac5b..7cff57c8 100644 --- a/server/app/apis/users.py +++ b/server/app/apis/users.py @@ -1,11 +1,15 @@ +""" +All API calls concerning question alternatives. +Default route: /api/users +""" + import app.core.http_codes as codes import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import UserDTO -from flask_jwt_extended import get_jwt_identity -from flask_restx import Resource -from flask_restx import inputs, reqparse from app.core.parsers import search_parser, sentinel +from flask_jwt_extended import get_jwt_identity +from flask_restx import Resource, inputs, reqparse api = UserDTO.api schema = UserDTO.schema @@ -25,13 +29,14 @@ user_search_parser.add_argument("role_id", type=int, default=sentinel, location= def _edit_user(item_user, args): + """ Edits a user using the provided arguments. """ + email = args.get("email") name = args.get("name") if email: if dbc.get.user_exists(email): api.abort(codes.BAD_REQUEST, "Email is already in use") - if name: args["name"] = args["name"].title() @@ -42,11 +47,15 @@ def _edit_user(item_user, args): class UsersList(Resource): @protect_route(allowed_roles=["*"]) def get(self): + """ Gets all users. """ + item = dbc.get.user(get_jwt_identity()) return item_response(schema.dump(item)) @protect_route(allowed_roles=["*"]) def put(self): + """ Posts a new user using the specified arguments. """ + args = user_parser_edit.parse_args(strict=True) item = dbc.get.user(get_jwt_identity()) item = _edit_user(item, args) @@ -58,11 +67,15 @@ class UsersList(Resource): class Users(Resource): @protect_route(allowed_roles=["*"]) def get(self, ID): + """ Gets the specified user. """ + item = dbc.get.user(ID) return item_response(schema.dump(item)) @protect_route(allowed_roles=["Admin"]) def put(self, ID): + """ Edits the specified team using the provided arguments. """ + args = user_parser_edit.parse_args(strict=True) item = dbc.get.user(ID) item = _edit_user(item, args) @@ -73,6 +86,8 @@ class Users(Resource): class UserSearch(Resource): @protect_route(allowed_roles=["*"]) def get(self): + """ Finds a specific user based on the provided arguments. """ + args = user_search_parser.parse_args(strict=True) items, total = dbc.search.user(**args) return list_response(list_schema.dump(items), total) -- GitLab