Skip to content
Snippets Groups Projects
Commit ba129d7c authored by Victor Löfgren's avatar Victor Löfgren
Browse files

Resolve "Add api for alternatives and answers"

parent c1bb0cbc
No related branches found
No related tags found
1 merge request!77Resolve "Add api for alternatives and answers"
Pipeline #41590 passed
......@@ -55,6 +55,8 @@ def item_response(item, code=http_codes.OK):
from flask_restx import Api
from .alternatives import api as alternative_ns
from .answers import api as answer_ns
from .auth import api as auth_ns
from .codes import api as code_ns
from .competitions import api as comp_ns
......@@ -73,6 +75,8 @@ flask_api.add_namespace(user_ns, path="/api/users")
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/<CID>/slides")
flask_api.add_namespace(alternative_ns, path="/api/competitions/<CID>/slides/<SOrder>/questions/<QID>/alternatives")
flask_api.add_namespace(answer_ns, path="/api/competitions/<CID>/teams/<TID>/answers")
flask_api.add_namespace(team_ns, path="/api/competitions/<CID>/teams")
flask_api.add_namespace(code_ns, path="/api/competitions/<CID>/codes")
flask_api.add_namespace(question_ns, path="/api/competitions/<CID>")
......
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import check_jwt, item_response, list_response
from app.core.dto import QuestionAlternativeDTO, QuestionDTO
from app.core.parsers import question_alternative_parser
from app.core.schemas import QuestionAlternativeSchema
from app.database.controller.add import question_alternative
from app.database.controller.get import question_alternatives
from app.database.models import Question, QuestionAlternative
from flask_jwt_extended import jwt_required
from flask_restx import Resource
api = QuestionAlternativeDTO.api
schema = QuestionAlternativeDTO.schema
list_schema = QuestionAlternativeDTO.list_schema
@api.route("/")
@api.param("CID, SOrder, QID")
class QuestionAlternativeList(Resource):
@check_jwt(editor=True)
def get(self, CID, SOrder, QID):
items = dbc.get.question_alternatives(QID)
return list_response(list_schema.dump(items))
@check_jwt(editor=True)
def post(self, CID, SOrder, QID):
args = question_alternative_parser.parse_args(strict=True)
item = dbc.add.question_alternative(**args, question_id=QID)
return item_response(schema.dump(item))
@api.route("/<AID>")
@api.param("CID, SOrder, QID, AID")
class QuestionAlternatives(Resource):
@check_jwt(editor=True)
def put(self, CID, SOrder, QID, AID):
args = question_alternative_parser.parse_args(strict=True)
item = dbc.get.one(QuestionAlternative, AID)
item = dbc.edit.question_alternative(item, **args)
return item_response(schema.dump(item))
@check_jwt(editor=True)
def delete(self, CID, SOrder, QID, AID):
item = dbc.get.one(QuestionAlternative, AID)
dbc.delete.default(item)
return {}, codes.NO_CONTENT
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import check_jwt, item_response, list_response
from app.core.dto import QuestionAnswerDTO
from app.core.parsers import question_answer_edit_parser, question_answer_parser
from app.core.schemas import QuestionAlternativeSchema
from app.database.controller.add import question_alternative
from app.database.controller.get import question_alternatives
from app.database.models import Question, QuestionAlternative, QuestionAnswer
from flask_jwt_extended import jwt_required
from flask_restx import Resource
api = QuestionAnswerDTO.api
schema = QuestionAnswerDTO.schema
list_schema = QuestionAnswerDTO.list_schema
@api.route("/")
@api.param("CID, TID")
class QuestionAnswerList(Resource):
@check_jwt(editor=True)
def get(self, CID, TID):
items = dbc.get.question_answers(TID)
return list_response(list_schema.dump(items))
@check_jwt(editor=True)
def post(self, CID, TID):
args = question_answer_parser.parse_args(strict=True)
item = dbc.add.question_answer(**args, team_id=TID)
return item_response(schema.dump(item))
@api.route("/<AID>")
@api.param("CID, TID, AID")
class QuestionAnswers(Resource):
@check_jwt(editor=True)
def put(self, CID, TID, AID):
args = question_answer_edit_parser.parse_args(strict=True)
item = dbc.get.one(QuestionAnswer, AID)
item = dbc.edit.question_answer(item, **args)
return item_response(schema.dump(item))
......@@ -14,7 +14,7 @@ list_schema = QuestionDTO.list_schema
@api.route("/questions")
@api.param("CID")
class QuestionsList(Resource):
class QuestionList(Resource):
@check_jwt(editor=True)
def get(self, CID):
items = dbc.get.question_list(CID)
......@@ -23,7 +23,7 @@ class QuestionsList(Resource):
@api.route("/slides/<SID>/questions")
@api.param("CID, SID")
class QuestionsList(Resource):
class QuestionListForSlide(Resource):
@check_jwt(editor=True)
def post(self, SID, CID):
args = question_parser.parse_args(strict=True)
......@@ -37,7 +37,7 @@ class QuestionsList(Resource):
@api.route("/slides/<SID>/questions/<QID>")
@api.param("CID, SID, QID")
class Questions(Resource):
class QuestionById(Resource):
@check_jwt(editor=True)
def get(self, CID, SID, QID):
item_question = dbc.get.question(CID, SID, QID)
......
......@@ -68,3 +68,15 @@ class QuestionDTO:
api = Namespace("questions")
schema = schemas.QuestionSchema(many=False)
list_schema = schemas.QuestionSchema(many=True)
class QuestionAlternativeDTO:
api = Namespace("alternatives")
schema = schemas.QuestionAlternativeSchema(many=False)
list_schema = schemas.QuestionAlternativeSchema(many=True)
class QuestionAnswerDTO:
api = Namespace("answers")
schema = schemas.QuestionAnswerSchema(many=False)
list_schema = schemas.QuestionAnswerSchema(many=True)
......@@ -60,7 +60,24 @@ question_parser.add_argument("total_score", type=int, default=None, location="js
question_parser.add_argument("type_id", type=int, default=None, location="json")
question_parser.add_argument("slide_id", type=int, location="json")
###QUESTION####
###QUESTION ALTERNATIVES####
question_alternative_parser = reqparse.RequestParser()
question_alternative_parser.add_argument("text", type=str, default=None, location="json")
question_alternative_parser.add_argument("value", type=int, default=None, location="json")
###QUESTION ANSWERS####
question_answer_parser = reqparse.RequestParser()
question_answer_parser.add_argument("data", type=dict, required=True, location="json")
question_answer_parser.add_argument("score", type=int, required=True, location="json")
question_answer_parser.add_argument("question_id", type=int, required=True, location="json")
###QUESTION ANSWERS EDIT####
question_answer_edit_parser = reqparse.RequestParser()
question_answer_edit_parser.add_argument("data", type=dict, default=None, location="json")
question_answer_edit_parser.add_argument("score", type=int, default=None, location="json")
###CODE####
code_parser = reqparse.RequestParser()
code_parser.add_argument("pointer", type=str, default=None, location="json")
code_parser.add_argument("view_type_id", type=int, default=None, location="json")
......
......@@ -20,7 +20,7 @@ class QuestionSchemaRich(RichSchema):
total_score = ma.auto_field()
slide_id = ma.auto_field()
type_id = ma.auto_field()
alternatives = fields.Nested(schemas.QuestionAlternative, many=True)
alternatives = fields.Nested(schemas.QuestionAlternativeSchema, many=True)
class TeamSchemaRich(RichSchema):
......
......@@ -62,13 +62,13 @@ class QuestionAnswerSchema(BaseSchema):
model = models.QuestionAnswer
id = ma.auto_field()
data = ma.auto_field()
data = ma.Function(lambda obj: obj.data)
score = ma.auto_field()
question_id = ma.auto_field()
team_id = ma.auto_field()
class QuestionAlternative(BaseSchema):
class QuestionAlternativeSchema(BaseSchema):
class Meta(BaseSchema.Meta):
model = models.QuestionAlternative
......
......@@ -15,6 +15,8 @@ from app.database.models import (
Media,
MediaType,
Question,
QuestionAlternative,
QuestionAnswer,
QuestionType,
Role,
Slide,
......@@ -114,6 +116,14 @@ def question(name, total_score, type_id, item_slide):
return db_add(Question(name, total_score, type_id, item_slide.id))
def question_alternative(text, value, question_id):
return db_add(QuestionAlternative(text, value, question_id))
def question_answer(data, score, question_id, team_id):
return db_add(QuestionAnswer(data, score, question_id, team_id))
def code(pointer, view_type_id):
""" Adds a code to the database using the provided arguments. """
......
......@@ -6,10 +6,15 @@ from app.database.controller import add, get, search, utils
from app.database.models import Question
def _alternative(item_old, question_id):
"""Internal function. Makes a copy of the provided question alternative"""
return add.question_alternative(item_old.text, item_old.value, question_id)
def _question(item_question_old, slide_id):
"""
Internal function. Makes a copy of the provided question item to the
specified slide. Does not copy team, question answers or alternatives.
specified slide. Does not copy team, question answers.
"""
item_question_new = add.db_add(
......@@ -21,9 +26,8 @@ def _question(item_question_old, slide_id):
)
)
# TODO: Add question alternatives
# for item_alternatives in item_question_old.alternatives:
# dbc.add.alternatives()
for item_alternative in item_question_old.alternatives:
_alternative(item_alternative, item_question_new.id)
return item_question_new
......@@ -48,7 +52,7 @@ def _component(item_component, item_slide_new):
def slide(item_slide_old):
"""
Deep copies a slide to the same competition.
Does not copy team, question answers or alternatives.
Does not copy team, question answers.
"""
item_competition = get.competition(item_slide_old.competition_id)
......@@ -59,7 +63,7 @@ def slide(item_slide_old):
def slide_to_competition(item_slide_old, item_competition):
"""
Deep copies a slide to the provided competition.
Does not copy team, question answers or alternatives.
Does not copy team, question answers.
"""
item_slide_new = add.slide(item_competition)
......@@ -85,7 +89,7 @@ def slide_to_competition(item_slide_old, item_competition):
def competition(item_competition_old):
"""
Adds a deep-copy of the provided competition.
Will not copy teams, question answers or alternatives.
Will not copy teams, question answers.
"""
name = "Kopia av " + item_competition_old.name
......
......@@ -125,3 +125,31 @@ def question(item_question, name=None, total_score=None, type_id=None, slide_id=
db.session.refresh(item_question)
return item_question
def question_alternative(item, text=None, value=None):
if text:
item.text = text
if value:
item.value = value
db.session.commit()
db.session.refresh(item)
return item
def question_answer(item, data=None, score=None):
if data:
item.data = data
if score:
item.score = score
db.session.commit()
db.session.refresh(item)
return item
......@@ -12,6 +12,8 @@ from app.database.models import (
ComponentType,
MediaType,
Question,
QuestionAlternative,
QuestionAnswer,
QuestionType,
Role,
Slide,
......@@ -78,6 +80,16 @@ def question(CID, SOrder, QID, required=True, error_msg=None):
return Question.query.join(Slide, join_filters).filter(Question.id == QID).first_extended(required, error_msg)
def question_alternatives(QID):
# join_filters = (Slide.competition_id == CID) & (Slide.order == SOrder)
return QuestionAlternative.query.filter(QuestionAlternative.question_id == QID).all()
def question_answers(TID):
# join_filters = (Slide.competition_id == CID) & (Slide.order == SOrder)
return QuestionAnswer.query.filter(QuestionAnswer.team_id == TID).all()
def competition(CID):
""" Get Competition and all it's sub-entities """
""" HOT PATH """
......
......@@ -160,7 +160,7 @@ class Question(db.Model):
class QuestionAlternative(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String(STRING_SIZE), nullable=False)
value = db.Column(db.Boolean, nullable=False)
value = db.Column(db.Integer, nullable=False)
question_id = db.Column(db.Integer, db.ForeignKey("question.id"), nullable=False)
def __init__(self, text, value, question_id):
......@@ -172,8 +172,9 @@ class QuestionAlternative(db.Model):
class QuestionAnswer(db.Model):
__table_args__ = (db.UniqueConstraint("question_id", "team_id"),)
id = db.Column(db.Integer, primary_key=True)
data = db.Column(db.Text, nullable=False)
data = db.Column(Dictionary(), nullable=False)
score = db.Column(db.Integer, nullable=False, default=0)
question_id = db.Column(db.Integer, db.ForeignKey("question.id"), nullable=False)
team_id = db.Column(db.Integer, db.ForeignKey("team.id"), nullable=False)
......
......@@ -63,13 +63,16 @@ def _add_items():
dbc.utils.commit_and_refresh(item_slide)
# Add question to competition
dbc.add.question(
item_question = dbc.add.question(
name=f"Question {j}: {question_types_items[j].name}",
total_score=j,
type_id=question_types_items[j].id,
item_slide=item_slide,
)
for i in range(3):
dbc.add.question_alternative(f"Alternative {i}", 0, item_question.id)
# Add text components
# TODO: Add images as components
for k in range(3):
......
......@@ -80,9 +80,11 @@ def check_slides_copy(item_slide_original, item_slide_copy, num_slides, order):
assert item_slide_copy.settings == item_slide_original.settings
# Checks that all components were correctly copied
assert len(item_slide_copy.components) == len(item_slide_original.components)
for i, c1 in enumerate(item_slide_original.components):
c2 = item_slide_copy.components[i]
components = item_slide_original.components
components_copy = item_slide_copy.components
assert len(components) == len(components_copy)
for c1, c2 in zip(components, components_copy):
assert c1 != c2
assert c1.x == c2.x
assert c1.y == c2.y
......@@ -94,16 +96,28 @@ def check_slides_copy(item_slide_original, item_slide_copy, num_slides, order):
assert c1.type_id == c2.type_id
# Checks that all questions were correctly copied
assert len(item_slide_copy.questions) == len(item_slide_original.questions)
for i, q1 in enumerate(item_slide_original.questions):
q2 = item_slide_copy.questions[i]
questions = item_slide_original.questions
questions_copy = item_slide_copy.questions
assert len(questions) == len(questions_copy)
for q1, q2 in zip(questions, questions_copy):
assert q1 != q2
assert q1.name == q2.name
assert q1.total_score == q2.total_score
assert q1.type_id == q2.type_id
assert q1.slide_id == item_slide_original.id
assert q2.slide_id == item_slide_copy.id
# TODO: Assert alternatives
# Assert alternatives
alternatives = q1.alternatives
alternatives_copy = q2.alternatives
assert len(alternatives) == len(alternatives_copy)
for a1, a2 in zip(alternatives, alternatives_copy):
assert a1.text == a2.text
assert a1.value == a2.value
assert a1.quesiton_id == q1.id
assert a2.quesiton_id == q2.id
# Checks that the copy put the slide in the database
item_slides, total = dbc.search.slide(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment