""" Базовые модели для работы с базой данных """ 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"" 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"" 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"" # ===== МОДЕЛИ ДЛЯ СИСТЕМЫ ТЕСТИРОВАНИЯ ===== 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"" 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"" 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"" 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"" 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"" 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"" 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""