diff --git a/server/app/apis/__init__.py b/server/app/apis/__init__.py index 53589ca1b1beaf91595a3e460d5fa84479e288d6..91928ba2dfc19fa3f97b66c5760f097474941ba2 100644 --- a/server/app/apis/__init__.py +++ b/server/app/apis/__init__.py @@ -114,19 +114,12 @@ def protect_route(allowed_roles=None, allowed_views=None): # from .users import api as user_ns # flask_api = Api() -# flask_api.add_namespace(media_ns, path="/api/media") -# flask_api.add_namespace(misc_ns, path="/api/misc") -# # flask_api.add_namespace(user_ns, path="/api/users") -# flask_api.regis -# flask_api.add_namespace(auth_ns, path="/api/auth") -# flask_api.add_namespace(comp_ns, path="/api/competitions") # flask_api.add_namespace(slide_ns, path="/api/competitions/<competition_id>/slides") # flask_api.add_namespace( # alternative_ns, path="/api/competitions/<competition_id>/slides/<slide_id>/questions/<question_id>/alternatives" # ) # flask_api.add_namespace(team_ns, path="/api/competitions/<competition_id>/teams") # flask_api.add_namespace(code_ns, path="/api/competitions/<competition_id>/codes") -# flask_api.add_namespace(question_ns, path="/api/competitions/<competition_id>") # flask_api.add_namespace(component_ns, path="/api/competitions/<competition_id>/slides/<slide_id>/components") # flask_api.add_namespace(answer_ns, path="/api/competitions/<competition_id>/teams/<team_id>/answers") # flask_api.add_namespace(score_ns, path="/api/competitions/<competition_id>/teams/<team_id>/answers/question_scores") @@ -142,6 +135,7 @@ def init_api(): from .competitions import blp as competitions_blp from .media import blp as media_blp from .misc import blp as misc_blp + from .questions import blp as questions_blp from .slides import blp as slide_blp from .users import blp as user_blp @@ -151,3 +145,4 @@ def init_api(): flask_api.register_blueprint(misc_blp) flask_api.register_blueprint(media_blp) flask_api.register_blueprint(slide_blp) + flask_api.register_blueprint(questions_blp) diff --git a/server/app/apis/questions.py b/server/app/apis/questions.py index 6ba32382fa524180c76eef35bd02a68382e552c6..29785906568bf7ceafd76322c8e64e177dbb6299 100644 --- a/server/app/apis/questions.py +++ b/server/app/apis/questions.py @@ -3,87 +3,87 @@ 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 app.core.parsers import sentinel -from flask_restx import Resource, reqparse - -api = QuestionDTO.api -schema = QuestionDTO.schema -list_schema = QuestionDTO.list_schema - -question_parser_add = reqparse.RequestParser() -question_parser_add.add_argument("name", type=str, default=None, location="json") -question_parser_add.add_argument("total_score", type=int, default=None, location="json") -question_parser_add.add_argument("type_id", type=int, required=True, location="json") -question_parser_add.add_argument("correcting_instructions", type=str, default=None, location="json") - -question_parser_edit = reqparse.RequestParser() -question_parser_edit.add_argument("name", type=str, default=sentinel, location="json") -question_parser_edit.add_argument("total_score", type=int, default=sentinel, location="json") -question_parser_edit.add_argument("type_id", type=int, default=sentinel, location="json") -question_parser_edit.add_argument("correcting_instructions", type=str, default=sentinel, location="json") - - -@api.route("/questions") -@api.param("competition_id") -class QuestionList(Resource): - @protect_route(allowed_roles=["*"]) - def get(self, competition_id): - """ Gets all questions in the specified competition. """ +from app.apis import protect_route +from app.core import ma +from app.core.schemas import BaseSchema, QuestionSchema, SlideSchema +from app.database import models +from app.database.controller.edit import default +from flask.views import MethodView +from flask_smorest import Blueprint + +from . import http_codes + +blp = Blueprint( + "question", + "question", + url_prefix="/api/competitions/<competition_id>/slides/<slide_id>/questions", + description="Adding, updating and deleting questions", +) + + +class QuestionAddArgsSchema(BaseSchema): + class Meta(BaseSchema.Meta): + model = models.Question + + name = ma.auto_field(required=False, missing="") + total_score = ma.auto_field(required=False, missing=None) + type_id = ma.auto_field(required=True) + correcting_instructions = ma.auto_field(required=False, missing=None) + + +class QuestionEditArgsSchema(BaseSchema): + class Meta(BaseSchema.Meta): + model = models.Question - items = dbc.get.question_list_for_competition(competition_id) - return list_response(list_schema.dump(items)) + name = ma.auto_field(required=False) + total_score = ma.auto_field(required=False) + type_id = ma.auto_field(required=False) + correcting_instructions = ma.auto_field(required=False) -@api.route("/slides/<slide_id>/questions") -@api.param("competition_id, slide_id") -class QuestionListForSlide(Resource): +@blp.route("") +class Questions(MethodView): @protect_route(allowed_roles=["*"]) + @blp.response(http_codes.OK, QuestionSchema(many=True)) 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)) + return dbc.get.question_list(competition_id, slide_id) @protect_route(allowed_roles=["*"]) - def post(self, competition_id, slide_id): + @blp.arguments(QuestionAddArgsSchema) + @blp.response(http_codes.OK, QuestionSchema) + @blp.alt_response(http_codes.CONFLICT, None, description="Could not add question") + def post(self, args, competition_id, slide_id): """ 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) - return item_response(schema.dump(item)) + return dbc.add.question(slide_id=slide_id, **args) -@api.route("/slides/<slide_id>/questions/<question_id>") -@api.param("competition_id, slide_id, question_id") -class QuestionById(Resource): +@blp.route("/<question_id>") +class QuestionById(MethodView): @protect_route(allowed_roles=["*"]) + @blp.response(http_codes.OK, QuestionSchema) + @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find question") 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)) + return dbc.get.question(competition_id, slide_id, question_id) @protect_route(allowed_roles=["*"]) - def put(self, competition_id, slide_id, question_id): + @blp.arguments(QuestionEditArgsSchema) + @blp.response(http_codes.OK, QuestionSchema) + @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find question") + @blp.alt_response(http_codes.CONFLICT, None, description="Could not edit question") + def put(self, args, 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) - item_question = dbc.edit.default(item_question, **args) - - return item_response(schema.dump(item_question)) + return dbc.edit.default(dbc.get.question(competition_id, slide_id, question_id), **args) @protect_route(allowed_roles=["*"]) + @blp.response(http_codes.NO_CONTENT, QuestionSchema) + @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find question") + @blp.alt_response(http_codes.CONFLICT, None, description="Could not delete question") 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 + dbc.delete.question(dbc.get.question(competition_id, slide_id, question_id)) + return None