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

Add api for competition codes

parent 0f4818a4
No related branches found
No related tags found
2 merge requests!63Resolve "Add components",!62Resolve "Use data from database in editor"
...@@ -43,6 +43,7 @@ def item_response(item, code=codes.OK): ...@@ -43,6 +43,7 @@ def item_response(item, code=codes.OK):
from flask_restx import Api from flask_restx import Api
from .auth import api as auth_ns from .auth import api as auth_ns
from .codes import api as code_ns
from .competitions import api as comp_ns from .competitions import api as comp_ns
from .components import api as component_ns from .components import api as component_ns
from .media import api as media_ns from .media import api as media_ns
...@@ -60,5 +61,6 @@ flask_api.add_namespace(auth_ns, path="/api/auth") ...@@ -60,5 +61,6 @@ flask_api.add_namespace(auth_ns, path="/api/auth")
flask_api.add_namespace(comp_ns, path="/api/competitions") flask_api.add_namespace(comp_ns, path="/api/competitions")
flask_api.add_namespace(slide_ns, path="/api/competitions/<CID>/slides") flask_api.add_namespace(slide_ns, path="/api/competitions/<CID>/slides")
flask_api.add_namespace(team_ns, path="/api/competitions/<CID>/teams") 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>") flask_api.add_namespace(question_ns, path="/api/competitions/<CID>")
flask_api.add_namespace(component_ns, path="/api/competitions/<CID>/slides/<SID>/components/") flask_api.add_namespace(component_ns, path="/api/competitions/<CID>/slides/<SID>/components/")
import app.core.http_codes as codes import app.core.http_codes as codes
import app.database.controller as dbc import app.database.controller as dbc
from app.apis import admin_required, item_response, text_response from app.apis import admin_required, item_response, text_response
from app.core.dto import AuthDTO from app.core.codes import verify_code
from app.core.dto import AuthDTO, CodeDTO
from app.core.parsers import create_user_parser, login_parser from app.core.parsers import create_user_parser, login_parser
from app.database.models import User from app.database.models import User
from flask_jwt_extended import ( from flask_jwt_extended import (
...@@ -69,6 +70,20 @@ class AuthLogin(Resource): ...@@ -69,6 +70,20 @@ class AuthLogin(Resource):
return response return response
@api.route("/login/<code>")
@api.param("code")
class AuthLogin(Resource):
def post(self, code):
if not verify_code(code):
api.abort(codes.BAD_REQUEST, "Invalid code")
item_code = dbc.get.code_by_code(code)
if not item_code:
api.abort(codes.UNAUTHORIZED, "A presentation with that code does not exist")
return item_response(CodeDTO.schema.dump(item_code)), codes.OK
@api.route("/logout") @api.route("/logout")
class AuthLogout(Resource): class AuthLogout(Resource):
@jwt_required @jwt_required
......
import app.database.controller as dbc
from app.apis import admin_required, item_response, list_response
from app.core import http_codes as codes
from app.core.codes import generate_code
from app.core.dto import CodeDTO
from app.core.parsers import code_parser
from app.database.models import Competition
from flask_jwt_extended import jwt_required
from flask_restx import Resource
api = CodeDTO.api
schema = CodeDTO.schema
list_schema = CodeDTO.list_schema
@api.route("/")
@api.param("CID")
class CodesList(Resource):
@jwt_required
def get(self, CID):
items = dbc.get.code_list(CID)
return list_response(list_schema.dump(items), len(items)), codes.OK
@jwt_required
def post(self, CID):
args = code_parser.parse_args(strict=True)
item = dbc.add.code(**args)
return item_response(schema.dump(item)), codes.OK
@api.route("/<code_id>")
@api.param("CID, code_id")
class Competitions(Resource):
@jwt_required
def put(self, CID, code_id):
item_code = dbc.get.code(code_id)
item_code = dbc.edit.generate_new_code(item_code)
return item_response(schema.dump(item_code)), codes.OK
# @jwt_required
# def delete(self, CID, code_id):
# item_code = dbc.get.code(code_id)
# dbc.delete.code(item_code)
# return {}, http_codes.NOT_FOUND
import random
import re
import string
CODE_LENGTH = 6
ALLOWED_CHARS = string.ascii_uppercase + string.digits
CODE_RE = re.compile(f"^[{ALLOWED_CHARS}]{{{CODE_LENGTH}}}$")
def generate_code():
return "".join(random.choices(ALLOWED_CHARS, k=CODE_LENGTH))
def verify_code(c):
return CODE_RE.search(c.upper()) is not None
...@@ -37,6 +37,12 @@ class CompetitionDTO: ...@@ -37,6 +37,12 @@ class CompetitionDTO:
list_schema = schemas.CompetitionSchema(many=True) list_schema = schemas.CompetitionSchema(many=True)
class CodeDTO:
api = Namespace("codes")
schema = rich_schemas.CodeSchemaRich(many=False)
list_schema = schemas.CodeSchema(many=True)
class SlideDTO: class SlideDTO:
api = Namespace("slides") api = Namespace("slides")
schema = schemas.SlideSchema(many=False) schema = schemas.SlideSchema(many=False)
......
...@@ -60,6 +60,12 @@ question_parser.add_argument("total_score", type=int, default=None, location="js ...@@ -60,6 +60,12 @@ 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("type_id", type=int, default=None, location="json")
question_parser.add_argument("slide_id", type=int, location="json") question_parser.add_argument("slide_id", type=int, location="json")
###QUESTION####
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")
###TEAM#### ###TEAM####
team_parser = reqparse.RequestParser() team_parser = reqparse.RequestParser()
team_parser.add_argument("name", type=str, location="json") team_parser.add_argument("name", type=str, location="json")
......
...@@ -43,6 +43,16 @@ class TeamSchemaRich(RichSchema): ...@@ -43,6 +43,16 @@ class TeamSchemaRich(RichSchema):
question_answers = fields.Nested(schemas.QuestionAnswerSchema, many=True) question_answers = fields.Nested(schemas.QuestionAnswerSchema, many=True)
class CodeSchemaRich(RichSchema):
class Meta(RichSchema.Meta):
model = models.Code
id = ma.auto_field()
code = ma.auto_field()
pointer = ma.auto_field()
view_type = fields.Nested(schemas.ViewTypeSchema, many=False)
class SlideSchemaRich(RichSchema): class SlideSchemaRich(RichSchema):
class Meta(RichSchema.Meta): class Meta(RichSchema.Meta):
model = models.Slide model = models.Slide
......
...@@ -31,6 +31,16 @@ class ComponentTypeSchema(IdNameSchema): ...@@ -31,6 +31,16 @@ class ComponentTypeSchema(IdNameSchema):
model = models.ComponentType model = models.ComponentType
class CodeSchema(IdNameSchema):
class Meta(BaseSchema.Meta):
model = models.Code
id = ma.auto_field()
code = ma.auto_field()
pointer = ma.auto_field()
view_type_id = ma.auto_field()
class ViewTypeSchema(IdNameSchema): class ViewTypeSchema(IdNameSchema):
class Meta(BaseSchema.Meta): class Meta(BaseSchema.Meta):
model = models.ViewType model = models.ViewType
......
import app.core.http_codes as codes import app.core.http_codes as codes
from app.core import db from app.core import db
from app.core.codes import generate_code
from app.database.models import ( from app.database.models import (
Blacklist, Blacklist,
City, City,
Code,
Competition, Competition,
Component, Component,
ComponentType, ComponentType,
...@@ -75,6 +77,11 @@ def team(name, item_competition): ...@@ -75,6 +77,11 @@ def team(name, item_competition):
return Team(name, item_competition.id) return Team(name, item_competition.id)
@db_add
def code(pointer, view_type_id):
return Code(pointer, view_type_id)
@db_add @db_add
def mediaType(name): def mediaType(name):
return MediaType(name) return MediaType(name)
......
from app.core import db from app.core import db
from app.core.codes import generate_code
from app.database.models import Code
def switch_order(item1, item2): def switch_order(item1, item2):
...@@ -109,3 +111,16 @@ def question(item_question, name=None, total_score=None, type_id=None, slide_id= ...@@ -109,3 +111,16 @@ def question(item_question, name=None, total_score=None, type_id=None, slide_id=
db.session.refresh(item_question) db.session.refresh(item_question)
return item_question return item_question
def generate_new_code(item_code):
code = generate_code()
while db.session.query(Code).filter(Code.code == code).count():
code = generate_code()
item_code.code = code
db.session.commit()
db.session.refresh(item_code)
return item_code
from app.core import db from app.core import db
from app.database.models import ( from app.database.models import (
City, City,
Code,
Competition, Competition,
Component, Component,
ComponentType, ComponentType,
...@@ -11,8 +12,11 @@ from app.database.models import ( ...@@ -11,8 +12,11 @@ from app.database.models import (
Slide, Slide,
Team, Team,
User, User,
ViewType,
) )
team_view_id = ViewType.query.filter(ViewType.name == "Team").one().id
def all(db_type): def all(db_type):
return db_type.query.all() return db_type.query.all()
...@@ -34,6 +38,25 @@ def competition(CID, required=True, error_msg=None): ...@@ -34,6 +38,25 @@ def competition(CID, required=True, error_msg=None):
return Competition.query.filter(Competition.id == CID).first_extended(required, error_msg) return Competition.query.filter(Competition.id == CID).first_extended(required, error_msg)
def code(code_id, required=True, error_msg=None):
return Code.query.filter(Code.id == code_id).first_extended(required, error_msg)
def code_by_code(code):
return Code.query.filter(Code.code == code.upper()).first()
def code_list(competition_id):
return (
Code.query.join(Team, (Code.view_type_id == team_view_id) & (Team.id == Code.pointer), isouter=True)
.filter(
((Code.view_type_id != team_view_id) & (Code.pointer == competition_id))
| ((Code.view_type_id == team_view_id) & (competition_id == Team.competition_id))
)
.all()
)
def user(UID, required=True, error_msg=None): def user(UID, required=True, error_msg=None):
return User.query.filter(User.id == UID).first_extended(required, error_msg) return User.query.filter(User.id == UID).first_extended(required, error_msg)
......
import json import json
from app.core import bcrypt, db from app.core import bcrypt, db
from app.core.codes import generate_code
from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property from sqlalchemy.ext.hybrid import hybrid_method, hybrid_property
from sqlalchemy.orm import backref from sqlalchemy.orm import backref
from sqlalchemy.types import TypeDecorator from sqlalchemy.types import TypeDecorator
...@@ -230,7 +231,12 @@ class Code(db.Model): ...@@ -230,7 +231,12 @@ class Code(db.Model):
view_type_id = db.Column(db.Integer, db.ForeignKey("view_type.id"), nullable=False) view_type_id = db.Column(db.Integer, db.ForeignKey("view_type.id"), nullable=False)
def __init__(self, code, pointer, view_type_id): def __init__(self, pointer, view_type_id):
code = generate_code()
while db.session.query(Code).filter(Code.code == code).count():
code = generate_code()
self.code = code self.code = code
self.pointer = pointer self.pointer = pointer
self.view_type_id = view_type_id self.view_type_id = view_type_id
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment