Files
Quizzi/src/trudex/infrastructure/database/models.py
T
2026-01-02 17:39:56 +03:00

119 lines
4.2 KiB
Python

from datetime import datetime
from enum import Enum
from typing import final
from sqlalchemy import BigInteger, CheckConstraint, ForeignKey, Integer, String, Text, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
class Base(DeclarativeBase):
pass
@final
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
username: Mapped[str | None] = mapped_column(String(32))
first_name: Mapped[str] = mapped_column(String(64))
last_name: Mapped[str | None] = mapped_column(String(64))
group: Mapped[int | None] = mapped_column(CheckConstraint("group >= 1000 AND group <= 9999"))
is_admin: Mapped[bool] = mapped_column(default=False)
created_at: Mapped[datetime] = mapped_column(server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(server_default=func.now(), onupdate=func.now())
class QuestionType(str, Enum):
SINGLE = "single"
MULTIPLE = "multiple"
INPUT = "input"
@final
class Test(Base):
__tablename__ = "tests"
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(255))
description: Mapped[str | None] = mapped_column(Text)
for_group: Mapped[int | None] = mapped_column(default=None)
password: Mapped[str | None] = mapped_column(String(255), default=None)
expires_at: Mapped[datetime | None] = mapped_column(default=None)
is_active: Mapped[bool] = mapped_column(default=True)
created_at: Mapped[datetime] = mapped_column(server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(server_default=func.now(), onupdate=func.now())
questions: Mapped[list["Question"]] = relationship(
back_populates="test",
cascade="all, delete-orphan",
order_by="Question.position"
)
@final
class Question(Base):
__tablename__ = "questions"
id: Mapped[int] = mapped_column(primary_key=True)
test_id: Mapped[int] = mapped_column(ForeignKey("tests.id"))
text: Mapped[str] = mapped_column(Text)
position: Mapped[int] = mapped_column(Integer, default=0)
question_type: Mapped[QuestionType] = mapped_column(default=QuestionType.SINGLE)
tg_file_id: Mapped[str | None] = mapped_column(String(255))
test: Mapped["Test"] = relationship(back_populates="questions")
options: Mapped[list["Option"]] = relationship(
back_populates="question",
cascade="all, delete-orphan"
)
@final
class Option(Base):
__tablename__ = "options"
id: Mapped[int] = mapped_column(primary_key=True)
question_id: Mapped[int] = mapped_column(ForeignKey("questions.id"))
text: Mapped[str] = mapped_column(String(255))
is_correct: Mapped[bool] = mapped_column(default=False)
explanation: Mapped[str | None] = mapped_column(Text)
question: Mapped["Question"] = relationship(back_populates="options")
@final
class TestAttempt(Base):
__tablename__ = "test_attempts"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(BigInteger, index=True)
test_id: Mapped[int] = mapped_column(ForeignKey("tests.id"))
started_at: Mapped[datetime] = mapped_column(server_default=func.now())
finished_at: Mapped[datetime | None] = mapped_column(default=None)
score: Mapped[int] = mapped_column(Integer, default=0)
is_passed: Mapped[bool] = mapped_column(default=False)
test: Mapped["Test"] = relationship()
answers: Mapped[list["UserAnswer"]] = relationship(
back_populates="attempt",
cascade="all, delete-orphan"
)
@final
class UserAnswer(Base):
__tablename__ = "user_answers"
id: Mapped[int] = mapped_column(primary_key=True)
attempt_id: Mapped[int] = mapped_column(ForeignKey("test_attempts.id"))
question_id: Mapped[int] = mapped_column(ForeignKey("questions.id"))
selected_option_id: Mapped[int | None] = mapped_column(ForeignKey("options.id"), default=None)
text_answer: Mapped[str | None] = mapped_column(Text, default=None)
is_correct: Mapped[bool] = mapped_column(default=False)
attempt: Mapped["TestAttempt"] = relationship(back_populates="answers")
question: Mapped["Question"] = relationship()
selected_option: Mapped["Option | None"] = relationship()