diff --git a/server/app/core/parsers.py b/server/app/core/parsers.py index 71f8fdee352c3fe88831124b0c8907fcbc51f505..606462d0e84b29a7387a8ce2b0d0748ede0c04b6 100644 --- a/server/app/core/parsers.py +++ b/server/app/core/parsers.py @@ -97,11 +97,13 @@ component_parser.add_argument("x", type=str, default=None, location="json") component_parser.add_argument("y", type=int, default=None, location="json") component_parser.add_argument("w", type=int, default=None, location="json") component_parser.add_argument("h", type=int, default=None, location="json") -component_parser.add_argument("data", type=dict, default=None, location="json") +# component_parser.add_argument("data", type=dict, default=None, location="json") component_create_parser = component_parser.copy() -component_create_parser.replace_argument("data", type=dict, required=True, location="json") +# component_create_parser.replace_argument("data", type=dict, required=True, location="json") component_create_parser.add_argument("type_id", type=int, required=True, location="json") +component_create_parser.add_argument("text", type=str, required=False, location="json") +component_create_parser.add_argument("image_id", type=str, required=False, location="json") login_code_parser = reqparse.RequestParser() login_code_parser.add_argument("code", type=str, location="json") diff --git a/server/app/core/schemas.py b/server/app/core/schemas.py index 79bdbc1c48fd7460e85fe18f13a744f45a0a57e1..6f128ec8a414b64418a36e121c9010f61905829b 100644 --- a/server/app/core/schemas.py +++ b/server/app/core/schemas.py @@ -1,3 +1,4 @@ +from marshmallow.decorators import pre_load import app.database.models as models from app.core import ma from marshmallow_sqlalchemy import fields @@ -154,6 +155,8 @@ class ComponentSchema(BaseSchema): y = ma.auto_field() w = ma.auto_field() h = ma.auto_field() - data = ma.Function(lambda obj: obj.data) slide_id = ma.auto_field() type_id = ma.auto_field() + + text = fields.fields.String() + image_id = fields.fields.Integer() diff --git a/server/app/database/__init__.py b/server/app/database/__init__.py index 784c006b8fab59ca1dcc9e6dbac8ebffe91cb013..e0c78ad3af3f3376fdecdae4eae219aa1c62be24 100644 --- a/server/app/database/__init__.py +++ b/server/app/database/__init__.py @@ -6,6 +6,7 @@ from flask_sqlalchemy.model import Model from sqlalchemy import Column, DateTime, Text from sqlalchemy.sql import func from sqlalchemy.types import TypeDecorator +from sqlalchemy import event class Base(Model): diff --git a/server/app/database/controller/add.py b/server/app/database/controller/add.py index 1dbcf44230e508c3d51b3aa8faa4ba2c84e97914..db4133e5a62f97370ed17e08b4688fc62fe03925 100644 --- a/server/app/database/controller/add.py +++ b/server/app/database/controller/add.py @@ -5,7 +5,7 @@ This file contains functionality to add data to the database. from sqlalchemy.orm.session import sessionmaker import app.core.http_codes as codes from app.core import db -from app.database.controller import utils +from app.database.controller import edit, utils from app.database.models import ( Blacklist, City, @@ -13,6 +13,7 @@ from app.database.models import ( Competition, Component, ComponentType, + ImageComponent, Media, MediaType, Question, @@ -22,11 +23,13 @@ from app.database.models import ( Role, Slide, Team, + TextComponent, User, ViewType, ) from flask_restx import abort from sqlalchemy import exc +from sqlalchemy.orm import with_polymorphic def db_add(item): @@ -50,85 +53,23 @@ def db_add(item): return item -def blacklist(jti): - """ Adds a blacklist to the database. """ - - return db_add(Blacklist(jti)) - - -def mediaType(name): - """ Adds a media type to the database. """ - - return db_add(MediaType(name)) - - -def questionType(name): - """ Adds a question type to the database. """ - - return db_add(QuestionType(name)) - - -def componentType(name): - """ Adds a component type to the database. """ - - return db_add(ComponentType(name)) - - -def viewType(name): - """ Adds a view type to the database. """ - - return db_add(ViewType(name)) - - -def role(name): - """ Adds a role to the database. """ - - return db_add(Role(name)) - - -def city(name): - """ Adds a city to the database. """ - - return db_add(City(name)) - - -def component(type_id, slide_id, data, x=0, y=0, w=0, h=0): +def component(type_id, slide_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 . """ - return db_add(Component(slide_id, type_id, data, x, y, w, h)) - - -def image(filename, user_id): - """ - Adds an image to the database and keeps track of who called the function. - """ - - return db_add(Media(filename, 1, user_id)) - - -def user(email, password, role_id, city_id, name=None): - """ Adds a user to the database using the provided arguments. """ - - return db_add(User(email, password, role_id, city_id, name)) - - -def question(name, total_score, type_id, slide_id): - """ - Adds a question to the specified slide using the provided arguments. - """ - - return db_add(Question(name, total_score, type_id, slide_id)) - - -def question_alternative(text, value, question_id): - return db_add(QuestionAlternative(text, value, question_id)) + if type_id == 1: + item = db_add(TextComponent(slide_id, type_id, x, y, w, h)) + item.text = data.get("text") + elif type_id == 2: + item = db_add(ImageComponent(slide_id, type_id, x, y, w, h)) + item.image_id = data.get("image_id") + else: + abort(codes.BAD_REQUEST, f"Invalid type_id{type_id}") - -def question_answer(data, score, question_id, team_id): - return db_add(QuestionAnswer(data, score, question_id, team_id)) + item = utils.commit_and_refresh(item) + return item def code(pointer, view_type_id): @@ -216,3 +157,75 @@ def _competition_no_slides(name, year, city_id, font=None): item_competition = utils.refresh(item_competition) return item_competition + + +def blacklist(jti): + """ Adds a blacklist to the database. """ + + return db_add(Blacklist(jti)) + + +def mediaType(name): + """ Adds a media type to the database. """ + + return db_add(MediaType(name)) + + +def questionType(name): + """ Adds a question type to the database. """ + + return db_add(QuestionType(name)) + + +def componentType(name): + """ Adds a component type to the database. """ + + return db_add(ComponentType(name)) + + +def viewType(name): + """ Adds a view type to the database. """ + + return db_add(ViewType(name)) + + +def role(name): + """ Adds a role to the database. """ + + return db_add(Role(name)) + + +def city(name): + """ Adds a city to the database. """ + + return db_add(City(name)) + + +def image(filename, user_id): + """ + Adds an image to the database and keeps track of who called the function. + """ + + return db_add(Media(filename, 1, user_id)) + + +def user(email, password, role_id, city_id, name=None): + """ Adds a user to the database using the provided arguments. """ + + return db_add(User(email, password, role_id, city_id, name)) + + +def question(name, total_score, type_id, slide_id): + """ + Adds a question to the specified slide using the provided arguments. + """ + + return db_add(Question(name, total_score, type_id, 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)) diff --git a/server/app/database/controller/copy.py b/server/app/database/controller/copy.py index 79a4df0f4a7f308b7bc40b5b9ab20e61423ffb14..8e0a39103ecbc195a76f41357ef4ef643ed1740a 100644 --- a/server/app/database/controller/copy.py +++ b/server/app/database/controller/copy.py @@ -41,7 +41,6 @@ def _component(item_component, item_slide_new): add.component( item_component.type_id, item_slide_new.id, - item_component.data, item_component.x, item_component.y, item_component.w, diff --git a/server/app/database/controller/get.py b/server/app/database/controller/get.py index 96405b5ad28584bfb62268da894af9dd27a533a8..14b2714bacf22fde56646d6921e42629cd50bc9a 100644 --- a/server/app/database/controller/get.py +++ b/server/app/database/controller/get.py @@ -2,17 +2,20 @@ 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 ( Code, Competition, Component, + ImageComponent, Question, QuestionAlternative, QuestionAnswer, Slide, Team, + TextComponent, User, ) from sqlalchemy.orm import joinedload, subqueryload @@ -200,7 +203,15 @@ def component(competition_id, slide_id, component_id): join_competition = Competition.id == Slide.competition_id join_slide = Slide.id == Component.slide_id filters = (Competition.id == competition_id) & (Slide.id == slide_id) & (Component.id == component_id) - return Component.query.join(Competition, join_competition).join(Slide, join_slide).filter(filters).first_extended() + + poly = with_polymorphic(Component, [TextComponent, ImageComponent]) + return ( + db.session.query(poly) + .join(Competition, join_competition) + .join(Slide, join_slide) + .filter(filters) + .first_extended() + ) def component_list(competition_id, slide_id): diff --git a/server/app/database/models.py b/server/app/database/models.py index cded9ddf7aa00d2b5545a2ae5f40cf28eecba209..625370657c1ef5b2a57993667a7f1fcb6e741b14 100644 --- a/server/app/database/models.py +++ b/server/app/database/models.py @@ -2,6 +2,8 @@ from app.core import bcrypt, db from app.database import Dictionary from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property +from app.database.types import ID_IMAGE_COMPONENT, ID_TEXT_COMPONENT + STRING_SIZE = 254 @@ -190,20 +192,34 @@ class Component(db.Model): y = db.Column(db.Integer, nullable=False, default=0) w = db.Column(db.Integer, nullable=False, default=1) h = db.Column(db.Integer, nullable=False, default=1) - data = db.Column(Dictionary()) slide_id = db.Column(db.Integer, db.ForeignKey("slide.id"), nullable=False) type_id = db.Column(db.Integer, db.ForeignKey("component_type.id"), nullable=False) - def __init__(self, slide_id, type_id, data, x=0, y=0, w=1, h=1): + __mapper_args__ = {"polymorphic_on": type_id} + + def __init__(self, slide_id, type_id, x=0, y=0, w=1, h=1): self.x = x self.y = y self.w = w self.h = h - self.data = data self.slide_id = slide_id self.type_id = type_id +class TextComponent(Component): + text = db.Column(db.Text, default="", nullable=False) + + # __tablename__ = None + __mapper_args__ = {"polymorphic_identity": ID_TEXT_COMPONENT} + + +class ImageComponent(Component): + image_id = db.Column(db.Integer, db.ForeignKey("media.id"), nullable=True) + + # __tablename__ = None + __mapper_args__ = {"polymorphic_identity": ID_IMAGE_COMPONENT} + + class Code(db.Model): table_args = (db.UniqueConstraint("pointer", "type"),) id = db.Column(db.Integer, primary_key=True) diff --git a/server/app/database/types.py b/server/app/database/types.py new file mode 100644 index 0000000000000000000000000000000000000000..236e50f5278d03684f2cbdcc05c2e7637e21a057 --- /dev/null +++ b/server/app/database/types.py @@ -0,0 +1,2 @@ +ID_TEXT_COMPONENT = 1 +ID_IMAGE_COMPONENT = 2 diff --git a/server/populate.py b/server/populate.py index 9ca3c95e7f683decfac5e9f697d0cfdbff958e1e..71538f1513048f9e9cc64f39ef34f0df221db293 100644 --- a/server/populate.py +++ b/server/populate.py @@ -82,7 +82,7 @@ def _add_items(): y = random.randrange(1, 500) w = random.randrange(150, 400) h = random.randrange(150, 400) - dbc.add.component(1, item_slide.id, {"text": f"hej{k}"}, x, y, w, h) + dbc.add.component(1, item_slide.id, x, y, w, h, text=f"hej{k}") # item_slide = dbc.add.slide(item_comp) # item_slide.title = f"Slide {len(item_comp.slides)}"