diff --git a/server/app/apis/__init__.py b/server/app/apis/__init__.py index 7ba6eb54ec6db8a86c282ef00a131748f06033c3..c5395c7cddb2256b4b209f7a139c2f09ce4a5b4d 100644 --- a/server/app/apis/__init__.py +++ b/server/app/apis/__init__.py @@ -132,6 +132,7 @@ flask_api = Api() def init_api(): + from .alternatives import blp as alternative_blp from .auth import blp as auth_blp from .codes import blp as code_blp from .competitions import blp as competition_blp @@ -151,3 +152,4 @@ def init_api(): flask_api.register_blueprint(question_blp) flask_api.register_blueprint(team_blp) flask_api.register_blueprint(code_blp) + flask_api.register_blueprint(alternative_blp) diff --git a/server/app/apis/alternatives.py b/server/app/apis/alternatives.py index d3ae5d9d4be9f3a5835c4bb4c6f00ee5c38aa34d..a3143a7cea6093d1fe9d06210f780c7930c70f1d 100644 --- a/server/app/apis/alternatives.py +++ b/server/app/apis/alternatives.py @@ -3,79 +3,84 @@ All API calls concerning question alternatives. Default route: /api/competitions/<competition_id>/slides/<slide_id>/questions/<question_id>/alternatives """ -from os import abort -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 app.core.parsers import sentinel +from app.apis import protect_route +from app.core import ma +from app.core.schemas import BaseSchema, QuestionAlternativeSchema +from app.database import models from app.database.models import Question, QuestionAlternative -from flask_restx import Resource, reqparse +from flask.views import MethodView +from flask_smorest import Blueprint, abort -api = QuestionAlternativeDTO.api -schema = QuestionAlternativeDTO.schema -list_schema = QuestionAlternativeDTO.list_schema +from . import http_codes -alternative_parser_add = reqparse.RequestParser() -alternative_parser_add.add_argument("alternative", type=str, default="", location="json") -alternative_parser_add.add_argument("correct", type=str, default="", location="json") +blp = Blueprint( + "alternative", + "alternative", + url_prefix="/api/competitions/<competition_id>/slides/<slide_id>/questions/<question_id>/alternatives", + description="Adding, updating, deleting and copy alternatives", +) -alternative_parser_edit = reqparse.RequestParser() -alternative_parser_edit.add_argument("alternative", type=str, default=sentinel, location="json") -alternative_parser_edit.add_argument("alternative_order", type=int, default=sentinel, location="json") -alternative_parser_edit.add_argument("correct", type=str, default=sentinel, location="json") -alternative_parser_edit.add_argument("correct_order", type=int, default=sentinel, location="json") +class AlternativeAddArgsSchema(BaseSchema): + class Meta(BaseSchema.Meta): + model = models.QuestionAlternative -@api.route("") -@api.param("competition_id, slide_id, question_id") -class QuestionAlternativeList(Resource): + alternative = ma.auto_field(required=False, missing="") + correct = ma.auto_field(required=False, missing="") + + +class AlternativeEditArgsSchema(BaseSchema): + class Meta(BaseSchema.Meta): + model = models.QuestionAlternative + + alternative = ma.auto_field(required=False) + alternative_order = ma.auto_field(required=False, missing=None) + correct = ma.auto_field(required=False) + correct_order = ma.auto_field(required=False, missing=None) + + +@blp.route("") +class Alternatives(MethodView): @protect_route(allowed_roles=["*"], allowed_views=["*"]) + @blp.response(http_codes.OK, QuestionAlternativeSchema(many=True)) 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)) + return dbc.get.question_alternative_list(competition_id, slide_id, question_id) @protect_route(allowed_roles=["*"]) - def post(self, competition_id, slide_id, question_id): + @blp.arguments(AlternativeAddArgsSchema) + @blp.response(http_codes.OK, QuestionAlternativeSchema) + @blp.alt_response(http_codes.CONFLICT, None, description="Could not add alternative") + def post(self, args, competition_id, slide_id, question_id): """ 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) - return item_response(schema.dump(item)) + return dbc.add.question_alternative(**args, question_id=question_id) -@api.route("/<alternative_id>") -@api.param("competition_id, slide_id, question_id, alternative_id") -class QuestionAlternatives(Resource): +@blp.route("/<alternative_id>") +class QuestionAlternatives(MethodView): @protect_route(allowed_roles=["*"], allowed_views=["*"]) + @blp.response(http_codes.OK, QuestionAlternativeSchema) + @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find alternative") 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)) + return dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id) @protect_route(allowed_roles=["*"]) - def put(self, competition_id, slide_id, question_id, alternative_id): + @blp.arguments(AlternativeEditArgsSchema) + @blp.response(http_codes.OK, QuestionAlternativeSchema) + @blp.alt_response(http_codes.BAD_REQUEST, None, description="Paramters to edit alternative with is incorrect") + @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find alternative") + @blp.alt_response(http_codes.CONFLICT, None, description="Could not edit alternative with the given values") + def put(self, args, competition_id, slide_id, question_id, alternative_id): """ Edits the specified question alternative using the provided arguments. """ - args = alternative_parser_edit.parse_args(strict=True) item = dbc.get.question_alternative( competition_id, slide_id, @@ -84,9 +89,12 @@ class QuestionAlternatives(Resource): ) new_alternative_order = args.pop("alternative_order") - if new_alternative_order is not sentinel and item.alternative_order != new_alternative_order: + if new_alternative_order is not None and item.alternative_order != new_alternative_order: if not (0 <= new_alternative_order < dbc.utils.count(QuestionAlternative, {"question_id": question_id})): - abort(codes.BAD_REQUEST, f"Cant change to invalid slide order '{new_alternative_order}'") + abort( + http_codes.BAD_REQUEST, + message=f"Kan inte ändra till ogiltigt sidordning '{new_alternative_order}'", + ) item_question = dbc.get.one(Question, question_id) dbc.utils.move_order( @@ -94,25 +102,20 @@ class QuestionAlternatives(Resource): ) new_correct_order = args.pop("correct_order") - if new_correct_order is not sentinel and item.correct_order != new_correct_order: + if new_correct_order is not None and item.correct_order != new_correct_order: if not (0 <= new_correct_order < dbc.utils.count(QuestionAlternative, {"question_id": question_id})): - abort(codes.BAD_REQUEST, f"Cant change to invalid slide order '{new_correct_order}'") + abort(http_codes.BAD_REQUEST, message=f"Kan inte ändra till ogiltigt sidordning '{new_correct_order}'") item_question = dbc.get.one(Question, question_id) dbc.utils.move_order(item_question.alternatives, "correct_order", item.correct_order, new_correct_order) - item = dbc.edit.default(item, **args) - return item_response(schema.dump(item)) + return dbc.edit.default(item, **args) @protect_route(allowed_roles=["*"]) + @blp.response(http_codes.NO_CONTENT, None) + @blp.alt_response(http_codes.NOT_FOUND, None, description="Could not find alternative") + @blp.alt_response(http_codes.CONFLICT, None, description="Could not delete alternative") 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 + dbc.delete.default(dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id)) + return None