Exemplo n.º 1
0
class QuizAnswer(BaseModel):
    __tablename__ = "quiz_answer"

    id = db.Column(db.String(length=32),
                   nullable=False,
                   unique=True,
                   default=generate_uuid)
    internal_id = db.Column(db.BigInteger,
                            primary_key=True,
                            autoincrement=True)
    question_id = db.Column(db.String(length=32),
                            db.ForeignKey("quiz_question.id"),
                            nullable=False)
    attempt_id = db.Column(db.String(length=32),
                           db.ForeignKey("quiz_attempt.id"),
                           nullable=False)
    user_id = db.Column(db.String(length=32),
                        db.ForeignKey("user.id"),
                        nullable=False)
    selected_option = db.Column(db.SmallInteger)
    created_at = db.Column(db.BigInteger, nullable=False, default=unix_time)
    updated_at = db.Column(db.BigInteger, onupdate=unix_time)

    # Index
    _idx_quiz_answer_id = db.Index("idx_quiz_answer_id", "id")
    _idx_quiz_answer_question_attempt_user = db.Index(
        "idx_quiz_answer_question_attempt_user", "question_id", "attempt_id",
        "user_id")
Exemplo n.º 2
0
class QuizAttempt(BaseModel):
    __tablename__ = "quiz_attempt"

    id = db.Column(db.String(length=32),
                   nullable=False,
                   unique=True,
                   default=generate_uuid)
    internal_id = db.Column(db.BigInteger,
                            primary_key=True,
                            autoincrement=True)
    quiz_id = db.Column(db.String(length=32),
                        db.ForeignKey("quiz.id"),
                        nullable=False)
    user_id = db.Column(db.String(length=32),
                        db.ForeignKey("user.id"),
                        nullable=False)
    score = db.Column(db.BigInteger, nullable=True)
    is_finished = db.Column(db.Boolean, nullable=False, default=False)
    created_at = db.Column(db.BigInteger, nullable=False, default=unix_time)
    updated_at = db.Column(db.BigInteger, onupdate=unix_time)

    # Index
    _idx_quiz_attempt_id = db.Index("idx_quiz_attempt_id", "id")
    _idx_quiz_attempt_quiz_id_score = db.Index(
        "idx_quiz_attempt_quiz_id_score", "quiz_id", "score")

    @classmethod
    async def get_latest_or_add(cls, **kwargs):
        created_attempt = await get_one_latest(QuizAttempt, **kwargs)

        # Update the number of attempts in Quiz, if no previous attempts of the user are found
        if not created_attempt:
            current_num_attempts = (await
                                    get_one(Quiz,
                                            id=kwargs["quiz_id"])).num_attempts
            await Quiz.modify({"id": kwargs["quiz_id"]},
                              {"num_attempts": current_num_attempts + 1})
            data = await create_one(cls, **kwargs)
            return serialize_to_dict(data)

        return serialize_to_dict(created_attempt)

    @classmethod
    async def remove(cls, **kwargs):
        if "attempt_id" not in kwargs:
            raise KeyError("Missing key 'attempt_id' in kwargs.")

        # Delete all the answers linked to the attempt
        await delete_many(QuizAnswer, attempt_id=kwargs["attempt_id"])
        return await super(QuizAttempt, cls).remove(**kwargs)
Exemplo n.º 3
0
class Quiz(BaseModel):
    __tablename__ = "quiz"

    id = db.Column(db.String(length=32),
                   nullable=False,
                   unique=True,
                   default=generate_uuid)
    internal_id = db.Column(db.BigInteger,
                            primary_key=True,
                            autoincrement=True)
    title = db.Column(db.String, nullable=False)
    description = db.Column(db.String)
    num_attempts = db.Column(db.BigInteger, nullable=False, default=0)
    creator_id = db.Column(db.String(length=32),
                           db.ForeignKey("user.id"),
                           nullable=False)
    questions = db.Column(ARRAY(db.String), server_default="{}")
    created_at = db.Column(db.BigInteger, nullable=False, default=unix_time)
    updated_at = db.Column(db.BigInteger, onupdate=unix_time)

    # Index
    _idx_quiz_id = db.Index("idx_quiz_id", "id")
    _idx_quiz_creator = db.Index("idx_quiz_creator", "creator_id")

    @classmethod
    async def add(cls, creator_id=None, **kwargs):
        return await super(Quiz, cls).add(creator_id=creator_id, **kwargs)

    @classmethod
    async def remove(cls, **kwargs):
        if "id" not in kwargs:
            raise KeyError("Missing key 'id' in query parameter.")

        # Delete all the attempts linked to the quiz
        await delete_many(QuizAttempt, quiz_id=kwargs["id"])

        # Delete all the questions linked to the quiz
        await delete_many(QuizQuestion, quiz_id=kwargs["id"])

        return await super(Quiz, cls).remove(**kwargs)
Exemplo n.º 4
0
class QuizQuestion(BaseModel):
    __tablename__ = "quiz_question"

    id = db.Column(db.String(length=32),
                   nullable=False,
                   unique=True,
                   default=generate_uuid)
    internal_id = db.Column(db.BigInteger,
                            primary_key=True,
                            autoincrement=True)
    quiz_id = db.Column(db.String(length=32),
                        db.ForeignKey("quiz.id"),
                        nullable=False)
    text = db.Column(db.String, nullable=False)
    options = db.Column(ARRAY(db.String), server_default="{}")
    correct_option = db.Column(db.SmallInteger, nullable=False, default=0)
    created_at = db.Column(db.BigInteger, nullable=False, default=unix_time)
    updated_at = db.Column(db.BigInteger, onupdate=unix_time)

    # Index
    _idx_quiz_question_id = db.Index("idx_quiz_question_id", "id")
    _idx_quiz_question_quiz_id = db.Index("idx_quiz_question_quiz_id",
                                          "quiz_id")
Exemplo n.º 5
0
class User(BaseModel):
    __tablename__ = "user"

    id = db.Column(db.String(length=32),
                   nullable=False,
                   unique=True,
                   default=generate_uuid)
    internal_id = db.Column(db.BigInteger,
                            primary_key=True,
                            autoincrement=True)
    full_name = db.Column(db.String, nullable=False)
    display_name = db.Column(db.String)
    email = db.Column(db.String, unique=True, nullable=False)
    password = db.Column(db.String, nullable=False)
    disabled = db.Column(db.Boolean, nullable=False, default=False)
    created_at = db.Column(db.BigInteger, nullable=False, default=unix_time)
    updated_at = db.Column(db.BigInteger, onupdate=unix_time)

    # Index
    _idx_user_id = db.Index("idx_user_id", "id")
    _idx_user_email = db.Index("idx_user_email", "email")

    @classmethod
    async def add(cls, password=None, **kwargs):
        # Follow guidelines from OWASP
        # https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
        if password:
            validate_password_strength(password)
            password = hash_password(password)

        return await super(User, cls).add(password=password, **kwargs)

    @classmethod
    async def modify(cls, get_kwargs, update_kwargs):
        if "password" in update_kwargs:
            password = update_kwargs["password"]
            validate_password_strength(password)
            update_kwargs["password"] = hash_password(password)

        return await super(User, cls).modify(get_kwargs, update_kwargs)

    @classmethod
    async def remove(cls, **kwargs):
        """For User, only disabled it, without completely delete it."""
        if "id" not in kwargs:
            raise InvalidUsage("Missing field 'id' in query parameter")

        await super(User, cls).modify(kwargs, {"disabled": True})

    @classmethod
    async def login(cls, email=None, password=None, **kwargs):
        if not email:
            raise InvalidUsage("Missing field 'email' in request's body.")
        if not password:
            raise InvalidUsage("Missing field 'password' in request's body.")

        validate_password_strength(password)
        password = hash_password(password)

        user = None
        try:
            user = await cls.get(email=email, password=password, **kwargs)
        except NotFound:
            pass

        if not user:
            raise LoginFailureError()
        return user