Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • tddd96-grupp11/teknikattan-scoring-system
1 result
Show changes
Showing
with 283 additions and 136 deletions
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.dto import MediaDTO
from app.core.parsers import media_parser_search
from app.database.models import City, Media, MediaType, QuestionType, Role
from flask import request
from flask_jwt_extended import get_jwt_identity, jwt_required
from flask_restx import Resource, reqparse
from flask_uploads import UploadNotAllowed
from PIL import Image
api = MediaDTO.api
image_set = MediaDTO.image_set
schema = MediaDTO.schema
list_schema = MediaDTO.list_schema
def generate_thumbnail(filename):
with Image.open(f"./static/images/{filename}") as im:
im.thumbnail((120, 120))
im.save(f"./static/images/thumbnail_{filename}")
@api.route("/images")
class ImageList(Resource):
@jwt_required
def get(self):
args = media_parser_search.parse_args(strict=True)
items, total = dbc.search.image(**args)
return list_response(list_schema.dump(items), total)
@jwt_required
def post(self):
if "image" not in request.files:
api.abort(codes.BAD_REQUEST, "Missing image in request.files")
try:
filename = image_set.save(request.files["image"])
generate_thumbnail(filename)
print(filename)
item = dbc.add.image(filename, get_jwt_identity())
return item_response(schema.dump(item))
except UploadNotAllowed:
api.abort(codes.BAD_REQUEST, "Could not save the image")
except:
api.abort(codes.INTERNAL_SERVER_ERROR, "Something went wrong when trying to save image")
import app.core.controller as dbc
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.dto import MiscDTO
from app.core.models import City, MediaType, QuestionType, Role
from app.database.models import City, MediaType, QuestionType, Role
from flask_jwt_extended import jwt_required
from flask_restx import Resource, reqparse
......
import app.core.controller as dbc
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.controller.add import competition
from app.core.dto import QuestionDTO
from app.core.models import Question
from app.core.parsers import question_parser
from flask_jwt_extended import get_jwt_identity, jwt_required
from flask_restx import Namespace, Resource
from app.database.models import Question
from flask_jwt_extended import jwt_required
from flask_restx import Resource
api = QuestionDTO.api
schema = QuestionDTO.schema
......@@ -18,8 +17,8 @@ list_schema = QuestionDTO.list_schema
class QuestionsList(Resource):
@jwt_required
def get(self, CID):
items, total = dbc.get.search_questions(competition_id=CID)
return list_response(list_schema.dump(items), total)
items = dbc.get.question_list(CID)
return list_response(list_schema.dump(items))
@jwt_required
def post(self, CID):
......@@ -41,25 +40,14 @@ class QuestionsList(Resource):
class Questions(Resource):
@jwt_required
def get(self, CID, QID):
item_question = Question.query.filter(Question.id == QID).first()
if item_question is None:
api.abort(codes.NOT_FOUND, f"Could not find question with id {QID}.")
if item_question.slide.competition.id != int(CID):
api.abort(codes.NOT_FOUND, f"Could not find question with id {QID} in competition with id {CID}.")
item_question = dbc.get.question(CID, QID)
return item_response(schema.dump(item_question))
@jwt_required
def put(self, CID, QID):
args = question_parser.parse_args(strict=True)
print(f"questions 54: {args=}")
item_question = Question.query.filter(Question.id == QID).first()
if item_question.slide.competition.id != int(CID):
api.abort(codes.NOT_FOUND, f"Could not find question with id {QID} in competition with id {CID}.")
item_question = dbc.get.question(CID, QID)
item_question = dbc.edit.question(item_question, **args)
return item_response(schema.dump(item_question))
......@@ -67,8 +55,5 @@ class Questions(Resource):
@jwt_required
def delete(self, CID, QID):
item_question = dbc.get.question(CID, QID)
if not item_question:
return {"response": "No content found"}, codes.NOT_FOUND
dbc.delete.question(item_question)
return {}, codes.NO_CONTENT
import app.core.controller as dbc
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.dto import SlideDTO
from app.core.models import Competition, Slide
from app.core.parsers import slide_parser
from app.database.models import Competition, Slide
from flask_jwt_extended import jwt_required
from flask_restx import Resource
......@@ -12,22 +12,19 @@ schema = SlideDTO.schema
list_schema = SlideDTO.list_schema
def get_comp(CID):
return Competition.query.filter(Competition.id == CID).first()
@api.route("/")
@api.param("CID")
class SlidesList(Resource):
@jwt_required
def get(self, CID):
item_comp = get_comp(CID)
return list_response(list_schema.dump(item_comp.slides))
items = dbc.get.slide_list(CID)
return list_response(list_schema.dump(items))
@jwt_required
def post(self, CID):
item_comp = get_comp(CID)
dbc.add.slide(item_comp)
item_comp = dbc.get.competition(CID)
item_slide = dbc.add.slide(item_comp)
dbc.add.question(f"Fråga {item_slide.order + 1}", 10, 0, item_slide)
dbc.refresh(item_comp)
return list_response(list_schema.dump(item_comp.slides))
......@@ -54,8 +51,6 @@ class Slides(Resource):
@jwt_required
def delete(self, CID, SID):
item_slide = dbc.get.slide(CID, SID)
if not item_slide:
return {"response": "No content found"}, codes.NOT_FOUND
dbc.delete.slide(item_slide)
return {}, codes.NO_CONTENT
......@@ -72,10 +67,10 @@ class SlidesOrder(Resource):
item_slide = dbc.get.slide(CID, SID)
if order == item_slide.order:
api.abort(codes.BAD_REQUEST)
return item_response(schema.dump(item_slide))
# clamp order between 0 and max
order_count = Slide.query.filter(Slide.competition_id == item_slide.competition_id).count()
order_count = dbc.get.slide_count(CID)
if order < 0:
order = 0
elif order >= order_count - 1:
......
import app.core.controller as dbc
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.dto import TeamDTO
from app.core.models import Competition, Team
from app.core.parsers import team_parser
from app.database.models import Competition, Team
from flask_jwt_extended import get_jwt_identity, jwt_required
from flask_restx import Namespace, Resource, reqparse
......@@ -12,22 +12,18 @@ schema = TeamDTO.schema
list_schema = TeamDTO.list_schema
def get_comp(CID):
return Competition.query.filter(Competition.id == CID).first()
@api.route("/")
@api.param("CID")
class TeamsList(Resource):
@jwt_required
def get(self, CID):
item_comp = get_comp(CID)
return list_response(list_schema.dump(item_comp.teams))
items = dbc.get.team_list(CID)
return list_response(list_schema.dump(items))
@jwt_required
def post(self, CID):
args = team_parser.parse_args(strict=True)
item_comp = get_comp(CID)
item_comp = dbc.get.competition(CID)
item_team = dbc.add.team(args["name"], item_comp)
return item_response(schema.dump(item_team))
......@@ -43,8 +39,6 @@ class Teams(Resource):
@jwt_required
def delete(self, CID, TID):
item_team = dbc.get.team(CID, TID)
if not item_team:
api.abort(codes.NOT_FOUND, f"Could not find team with id {TID} in competition with id {CID}.")
dbc.delete.team(item_team)
return {}, codes.NO_CONTENT
......@@ -55,8 +49,6 @@ class Teams(Resource):
name = args.get("name")
item_team = dbc.get.team(CID, TID)
if not item_team:
api.abort(codes.NOT_FOUND, f"Could not find team with id {TID} in competition with id {CID}.")
item_team = dbc.edit.team(item_team, name=name, competition_id=CID)
return item_response(schema.dump(item_team))
import app.core.controller as dbc
import app.core.http_codes as codes
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core.dto import UserDTO
from app.core.models import User
from app.core.parsers import user_parser, user_search_parser
from app.database.models import User
from flask import request
from flask_jwt_extended import get_jwt_identity, jwt_required
from flask_restx import Namespace, Resource
......@@ -26,13 +26,13 @@ def edit_user(item_user, args):
class UsersList(Resource):
@jwt_required
def get(self):
item = User.query.filter(User.id == get_jwt_identity()).first()
item = dbc.get.user(get_jwt_identity())
return item_response(schema.dump(item))
@jwt_required
def put(self):
args = user_parser.parse_args(strict=True)
item = User.query.filter(User.id == get_jwt_identity()).first()
item = dbc.get.user(get_jwt_identity())
item = edit_user(item, args)
return item_response(schema.dump(item))
......@@ -42,13 +42,13 @@ class UsersList(Resource):
class Users(Resource):
@jwt_required
def get(self, ID):
item = User.query.filter(User.id == ID).first()
item = dbc.get.user(ID)
return item_response(schema.dump(item))
@jwt_required
def put(self, ID):
args = user_parser.parse_args(strict=True)
item = User.query.filter(User.id == ID).first()
item = dbc.get.user(ID)
item = edit_user(item, args)
return item_response(schema.dump(item))
......@@ -58,5 +58,5 @@ class UserSearch(Resource):
@jwt_required
def get(self):
args = user_search_parser.parse_args(strict=True)
items, total = dbc.get.search_user(**args)
items, total = dbc.search.user(**args)
return list_response(list_schema.dump(items), total)
import sqlalchemy as sa
from app.database.base import Base, ExtendedQuery
from flask_bcrypt import Bcrypt
from flask_jwt_extended.jwt_manager import JWTManager
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy.model import Model
from sqlalchemy.sql import func
class Base(Model):
__abstract__ = True
_created = sa.Column(sa.DateTime(timezone=True), server_default=func.now())
_updated = sa.Column(sa.DateTime(timezone=True), onupdate=func.now())
db = SQLAlchemy(model_class=Base)
db = SQLAlchemy(model_class=Base, query_class=ExtendedQuery)
bcrypt = Bcrypt()
jwt = JWTManager()
ma = Marshmallow()
......@@ -2,6 +2,14 @@ import app.core.rich_schemas as rich_schemas
import app.core.schemas as schemas
import marshmallow as ma
from flask_restx import Namespace, fields
from flask_uploads import IMAGES, UploadSet
class MediaDTO:
api = Namespace("media")
image_set = UploadSet("photos", IMAGES)
schema = schemas.MediaSchema(many=False)
list_schema = schemas.MediaSchema(many=True)
class AuthDTO:
......
......@@ -64,3 +64,7 @@ question_parser.add_argument("type_id", type=int, default=None, location="json")
###TEAM####
team_parser = reqparse.RequestParser()
team_parser.add_argument("name", type=str, location="json")
###SEARCH_COMPETITION####
media_parser_search = search_parser.copy()
media_parser_search.add_argument("filename", type=str, default=None, location="args")
import app.core.models as models
import app.core.schemas as schemas
import app.database.models as models
from app.core import ma
from marshmallow_sqlalchemy import fields
......@@ -29,8 +29,30 @@ class QuestionSchemaRich(RichSchema):
id = ma.auto_field()
name = ma.auto_field()
total_score = ma.auto_field()
slide_id = ma.auto_field()
type = fields.Nested(schemas.QuestionTypeSchema, many=False)
slide = fields.Nested(schemas.SlideSchema, many=False)
class TeamSchemaRich(RichSchema):
class Meta(RichSchema.Meta):
model = models.Team
id = ma.auto_field()
name = ma.auto_field()
competition_id = ma.auto_field()
question_answers = fields.Nested(schemas.QuestionAnswerSchema, many=True)
class SlideSchemaRich(RichSchema):
class Meta(RichSchema.Meta):
model = models.Slide
id = ma.auto_field()
order = ma.auto_field()
title = ma.auto_field()
timer = ma.auto_field()
competition_id = ma.auto_field()
questions = fields.Nested(QuestionSchemaRich, many=True)
class CompetitionSchemaRich(RichSchema):
......@@ -40,5 +62,9 @@ class CompetitionSchemaRich(RichSchema):
id = ma.auto_field()
name = ma.auto_field()
year = ma.auto_field()
slides = fields.Nested(schemas.SlideSchema, many=True)
city = fields.Nested(schemas.CitySchema, many=False)
slides = fields.Nested(
SlideSchemaRich,
many=True,
)
teams = fields.Nested(TeamSchemaRich, many=True)
import app.core.models as models
import app.database.models as models
from app.core import ma
from marshmallow_sqlalchemy import fields
......@@ -29,6 +29,17 @@ class QuestionSchema(BaseSchema):
slide_id = ma.auto_field()
class QuestionAnswerSchema(BaseSchema):
class Meta(BaseSchema.Meta):
model = models.QuestionAnswer
id = ma.auto_field()
data = ma.auto_field()
score = ma.auto_field()
question_id = ma.auto_field()
team_id = ma.auto_field()
class MediaTypeSchema(BaseSchema):
class Meta(BaseSchema.Meta):
model = models.MediaType
......
import app.core.http_codes as codes
import sqlalchemy as sa
from flask_restx import abort
from flask_sqlalchemy import BaseQuery, SQLAlchemy
from flask_sqlalchemy.model import Model
from sqlalchemy.sql import func
class Base(Model):
__abstract__ = True
_created = sa.Column(sa.DateTime(timezone=True), server_default=func.now())
_updated = sa.Column(sa.DateTime(timezone=True), onupdate=func.now())
class ExtendedQuery(BaseQuery):
def first_extended(self, required=True, error_message=None, error_code=codes.NOT_FOUND):
item = self.first()
if required and not item:
if not error_message:
error_message = "Object not found"
abort(error_code, error_message)
return item
def pagination(self, page=0, page_size=15, order_column=None, order=1):
query = self
if order_column:
if order == 1:
query = query.order_by(order_column)
else:
query = query.order_by(order_column.desc())
total = query.count()
query = query.limit(page_size).offset(page * page_size)
items = query.all()
return items, total
# import add, get
from app.core import db
from app.core.controller import add, delete, edit, get
from app.database.controller import add, delete, edit, get, search
def commit_and_refresh(item):
......
import app.core.http_codes as codes
from app.core import db
from app.core.models import Blacklist, City, Competition, MediaType, Question, QuestionType, Role, Slide, Team, User
from app.database.models import (
Blacklist,
City,
Competition,
Media,
MediaType,
Question,
QuestionType,
Role,
Slide,
Team,
User,
)
from flask_restx import abort
def db_add(func):
......@@ -8,6 +22,10 @@ def db_add(func):
db.session.add(item)
db.session.commit()
db.session.refresh(item)
if not item:
abort(codes.BAD_REQUEST, f"Object could not be created")
return item
return wrapper
......@@ -18,6 +36,11 @@ def blacklist(jti):
return Blacklist(jti)
@db_add
def image(filename, user_id):
return Media(filename, 1, user_id)
@db_add
def slide(item_competition):
order = Slide.query.filter(Slide.competition_id == item_competition.id).count() # first element has index 0
......
import app.core.controller as dbc
import app.database.controller as dbc
from app.core import db
from app.core.models import Blacklist, City, Competition, Role, Slide, User
from app.database.models import Blacklist, City, Competition, Role, Slide, User
def default(item):
......@@ -17,7 +17,7 @@ def slide(item_slide):
default(item_slide)
# Update slide order for all slides after the deleted slide
slides_in_same_competition, _ = dbc.get.search_slide(competition_id=deleted_slide_competition_id)
slides_in_same_competition = dbc.get.slide_list(deleted_slide_competition_id)
for other_slide in slides_in_same_competition:
if other_slide.order > deleted_slide_order:
other_slide.order -= 1
......
from app.database.models import Competition, Question, Slide, Team, User
from sqlalchemy.sql.expression import outerjoin
def user_exists(email):
return User.query.filter(User.email == email).count() > 0
def competition(CID, required=True, error_msg=None):
return Competition.query.filter(Competition.id == CID).first_extended(required, error_msg)
def user(UID, required=True, error_msg=None):
return User.query.filter(User.id == UID).first_extended(required, error_msg)
def user_by_email(email, required=True, error_msg=None):
return User.query.filter(User.email == email).first_extended(required, error_msg)
def slide_by_order(CID, order, required=True, error_msg=None):
return Slide.query.filter((Slide.competition_id == CID) & (Slide.order == order)).first_extended(
required, error_msg
)
def slide(CID, SID, required=True, error_msg=None):
return Slide.query.filter((Slide.competition_id == CID) & (Slide.id == SID)).first_extended(required, error_msg)
def team(CID, TID, required=True, error_msg=None):
return Team.query.filter((Team.competition_id == CID) & (Team.id == TID)).first_extended(required, error_msg)
def question(CID, QID, required=True, error_msg=None):
return (
Question.query.join(Slide, (Slide.competition_id == CID) & (Slide.id == Question.slide_id))
.filter(Question.id == QID)
.first_extended(required, error_msg)
)
def question_list(CID):
return Question.query.join(Slide, (Slide.competition_id == CID) & (Slide.id == Question.slide_id)).all()
def team_list(CID):
return Team.query.filter(Team.competition_id == CID).all()
def slide_list(CID):
return Slide.query.filter(Slide.competition_id == CID).all()
def slide_count(CID):
return Slide.query.filter(Slide.competition_id == CID).count()
from app.core.models import Competition, Question, Slide, Team, User
from app.database.models import Competition, Media, Question, Slide, Team, User
def slide_by_order(CID, order):
return Slide.query.filter((Slide.competition_id == CID) & (Slide.order == order)).first()
def image(filename, page=0, page_size=15, order=1, order_by=None):
query = Media.query.filter(Media.type_id == 1)
if filename:
query = query.filter(Media.filename.like(f"%{filename}%"))
return query.pagination(page, page_size, None, None)
def slide(CID, SID):
return Slide.query.filter((Slide.competition_id == CID) & (Slide.id == SID)).first()
def team(CID, TID):
return Team.query.filter((Team.competition_id == CID) & (Team.id == TID)).first()
def question(CID, QID):
slide_ids = set(
[x.id for x in Slide.query.filter(Slide.competition_id == CID).all()]
) # TODO: Filter using database instead of creating a set of slide_ids
return Question.query.filter(Question.slide_id.in_(slide_ids) & (Question.id == QID)).first()
def _search(query, order_column, page=0, page_size=15, order=1):
if order == 1:
query = query.order_by(order_column)
else:
query = query.order_by(order_column.desc())
total = query.count()
query = query.limit(page_size).offset(page * page_size)
items = query.all()
return items, total
def search_user(email=None, name=None, city_id=None, role_id=None, page=0, page_size=15, order=1, order_by=None):
def user(email=None, name=None, city_id=None, role_id=None, page=0, page_size=15, order=1, order_by=None):
query = User.query
if name:
query = query.filter(User.name.like(f"%{name}%"))
......@@ -47,12 +24,26 @@ def search_user(email=None, name=None, city_id=None, role_id=None, page=0, page_
if order_by:
order_column = getattr(User.__table__.c, order_by)
return _search(query, order_column, page, page_size, order)
return query.pagination(page, page_size, order_column, order)
def search_slide(
slide_order=None, title=None, body=None, competition_id=None, page=0, page_size=15, order=1, order_by=None
):
def competition(name=None, year=None, city_id=None, page=0, page_size=15, order=1, order_by=None):
query = Competition.query
if name:
query = query.filter(Competition.name.like(f"%{name}%"))
if year:
query = query.filter(Competition.year == year)
if city_id:
query = query.filter(Competition.city_id == city_id)
order_column = Competition.year # Default order_by
if order_by:
order_column = getattr(Competition.columns, order_by)
return query.pagination(page, page_size, order_column, order)
def slide(slide_order=None, title=None, body=None, competition_id=None, page=0, page_size=15, order=1, order_by=None):
query = Slide.query
if slide_order:
query = query.filter(Slide.order == slide_order)
......@@ -67,10 +58,10 @@ def search_slide(
if order_by:
order_column = getattr(Slide.__table__.c, order_by)
return _search(query, order_column, page, page_size, order)
return query.pagination(page, page_size, order_column, order)
def search_questions(
def questions(
name=None,
total_score=None,
type_id=None,
......@@ -91,29 +82,10 @@ def search_questions(
if slide_id:
query = query.filter(Question.slide_id == slide_id)
if competition_id:
slide_ids = set(
[x.id for x in Slide.query.filter(Slide.competition_id == competition_id).all()]
) # TODO: Filter using database instead of creating a set of slide_ids
query = query.filter(Question.slide_id.in_(slide_ids))
query = query.join(Slide, (Slide.competition_id == competition_id) & (Slide.id == Question.slide_id))
order_column = Question.id # Default order_by
if order_by:
order_column = getattr(Question.__table__.c, order_by)
return _search(query, order_column, page, page_size, order)
def search_competitions(name=None, year=None, city_id=None, page=0, page_size=15, order=1, order_by=None):
query = Competition.query
if name:
query = query.filter(Competition.name.like(f"%{name}%"))
if year:
query = query.filter(Competition.year == year)
if city_id:
query = query.filter(Competition.city_id == city_id)
order_column = Competition.year # Default order_by
if order_by:
order_column = getattr(Competition.columns, order_by)
return _search(query, order_column, page, page_size, order)
return query.pagination(page, page_size, order_column, order)