struckt
This commit is contained in:
208
app.api/database/models.py
Normal file
208
app.api/database/models.py
Normal file
@@ -0,0 +1,208 @@
|
||||
"""
|
||||
Базовые модели для работы с базой данных
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text, BigInteger, ForeignKey, LargeBinary
|
||||
from sqlalchemy.orm import relationship
|
||||
from sqlalchemy.sql import func
|
||||
from database.database import Base
|
||||
|
||||
class User(Base):
|
||||
"""Модель пользователя телеграм бота"""
|
||||
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
telegram_id = Column(BigInteger, unique=True, index=True, nullable=False)
|
||||
username = Column(String(255), nullable=True)
|
||||
first_name = Column(String(255), nullable=True)
|
||||
last_name = Column(String(255), nullable=True)
|
||||
language_code = Column(String(10), default='ru')
|
||||
is_active = Column(Boolean, default=True)
|
||||
is_admin = Column(Boolean, default=False)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# Связь с результатами тестов
|
||||
test_results = relationship("TestResult", back_populates="user")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<User(id={self.id}, telegram_id={self.telegram_id}, username={self.username})>"
|
||||
|
||||
class Message(Base):
|
||||
"""Модель сообщений"""
|
||||
|
||||
__tablename__ = "messages"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
telegram_message_id = Column(BigInteger, nullable=False)
|
||||
user_id = Column(Integer, nullable=False) # Внешний ключ на users.id
|
||||
text = Column(Text, nullable=True)
|
||||
message_type = Column(String(50), default='text') # text, photo, document, etc.
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Message(id={self.id}, user_id={self.user_id}, type={self.message_type})>"
|
||||
|
||||
class BotSettings(Base):
|
||||
"""Модель настроек бота"""
|
||||
|
||||
__tablename__ = "bot_settings"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
key = Column(String(255), unique=True, nullable=False)
|
||||
value = Column(Text, nullable=True)
|
||||
description = Column(Text, nullable=True)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
def __repr__(self):
|
||||
return f"<BotSettings(key={self.key}, value={self.value})>"
|
||||
|
||||
|
||||
# ===== МОДЕЛИ ДЛЯ СИСТЕМЫ ТЕСТИРОВАНИЯ =====
|
||||
|
||||
class TestCategory(Base):
|
||||
"""Категории тестов"""
|
||||
|
||||
__tablename__ = "test_categories"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
name = Column(String(255), unique=True, nullable=False) # "Тест Климова", "Профориентация" и т.д.
|
||||
description = Column(Text, nullable=True)
|
||||
is_active = Column(Boolean, default=True)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# Связь с тестами
|
||||
tests = relationship("Test", back_populates="category")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<TestCategory(id={self.id}, name={self.name})>"
|
||||
|
||||
|
||||
class Test(Base):
|
||||
"""Конкретные тесты"""
|
||||
|
||||
__tablename__ = "tests"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
category_id = Column(Integer, ForeignKey("test_categories.id"), nullable=False)
|
||||
title = Column(String(255), nullable=False) # "📘 Тест Климова"
|
||||
description = Column(Text, nullable=True)
|
||||
instructions = Column(Text, nullable=True) # Инструкции перед тестом
|
||||
is_active = Column(Boolean, default=True)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# Связи
|
||||
category = relationship("TestCategory", back_populates="tests")
|
||||
questions = relationship("Question", back_populates="test", cascade="all, delete-orphan")
|
||||
user_results = relationship("TestResult", back_populates="test")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Test(id={self.id}, title={self.title})>"
|
||||
|
||||
|
||||
class Image(Base):
|
||||
"""Хранение изображений в базе данных"""
|
||||
|
||||
__tablename__ = "images"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
filename = Column(String(255), nullable=False) # Оригинальное имя файла
|
||||
content_type = Column(String(100), nullable=False) # image/jpeg, image/png и т.д.
|
||||
file_size = Column(Integer, nullable=False) # Размер в байтах
|
||||
image_data = Column(LargeBinary, nullable=False) # Бинарные данные изображения
|
||||
alt_text = Column(String(255), nullable=True) # Альтернативный текст для доступности
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# Связь с вопросами
|
||||
questions = relationship("Question", back_populates="image")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Image(id={self.id}, filename={self.filename}, size={self.file_size})>"
|
||||
|
||||
|
||||
class Question(Base):
|
||||
"""Вопросы тестов"""
|
||||
|
||||
__tablename__ = "questions"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
test_id = Column(Integer, ForeignKey("tests.id"), nullable=False)
|
||||
order_index = Column(Integer, nullable=False) # Порядок вопроса в тесте
|
||||
question_text = Column(Text, nullable=True) # Текст вопроса (может быть пустым если только картинка)
|
||||
image_id = Column(Integer, ForeignKey("images.id"), nullable=True) # Привязка к изображению
|
||||
is_active = Column(Boolean, default=True)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# Связи
|
||||
test = relationship("Test", back_populates="questions")
|
||||
image = relationship("Image", back_populates="questions")
|
||||
answer_options = relationship("AnswerOption", back_populates="question", cascade="all, delete-orphan")
|
||||
user_answers = relationship("UserAnswer", back_populates="question")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Question(id={self.id}, test_id={self.test_id}, order={self.order_index})>"
|
||||
|
||||
|
||||
class AnswerOption(Base):
|
||||
"""Варианты ответов на вопросы"""
|
||||
|
||||
__tablename__ = "answer_options"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
question_id = Column(Integer, ForeignKey("questions.id"), nullable=False)
|
||||
option_text = Column(Text, nullable=False) # Текст варианта ответа
|
||||
category = Column(String(255), nullable=True) # Категория для подсчета результатов (например, "Человек-природа")
|
||||
order_index = Column(Integer, nullable=False) # Порядок варианта (1, 2, 3...)
|
||||
is_correct = Column(Boolean, default=False) # Для тестов с правильными ответами
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# Связи
|
||||
question = relationship("Question", back_populates="answer_options")
|
||||
user_answers = relationship("UserAnswer", back_populates="selected_option")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<AnswerOption(id={self.id}, question_id={self.question_id}, text='{self.option_text[:50]}')>"
|
||||
|
||||
|
||||
class TestResult(Base):
|
||||
"""Результаты прохождения тестов пользователями"""
|
||||
|
||||
__tablename__ = "test_results"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||
test_id = Column(Integer, ForeignKey("tests.id"), nullable=False)
|
||||
started_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
completed_at = Column(DateTime(timezone=True), nullable=True)
|
||||
is_completed = Column(Boolean, default=False)
|
||||
result_data = Column(Text, nullable=True) # JSON с результатами по категориям
|
||||
|
||||
# Связи
|
||||
user = relationship("User", back_populates="test_results")
|
||||
test = relationship("Test", back_populates="user_results")
|
||||
answers = relationship("UserAnswer", back_populates="test_result", cascade="all, delete-orphan")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<TestResult(id={self.id}, user_id={self.user_id}, test_id={self.test_id}, completed={self.is_completed})>"
|
||||
|
||||
|
||||
class UserAnswer(Base):
|
||||
"""Ответы пользователей на конкретные вопросы"""
|
||||
|
||||
__tablename__ = "user_answers"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
test_result_id = Column(Integer, ForeignKey("test_results.id"), nullable=False)
|
||||
question_id = Column(Integer, ForeignKey("questions.id"), nullable=False)
|
||||
selected_option_id = Column(Integer, ForeignKey("answer_options.id"), nullable=False)
|
||||
answered_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# Связи
|
||||
test_result = relationship("TestResult", back_populates="answers")
|
||||
question = relationship("Question", back_populates="user_answers")
|
||||
selected_option = relationship("AnswerOption", back_populates="user_answers")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<UserAnswer(id={self.id}, question_id={self.question_id}, option_id={self.selected_option_id})>"
|
||||
Reference in New Issue
Block a user