from sqlalchemy import Column, Integer, String, DateTime, Boolean, Text, ForeignKey, Numeric, JSON from sqlalchemy.orm import relationship from sqlalchemy.sql import func from .database import Base class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) email = Column(String(255), unique=True, index=True, nullable=False) hashed_password = Column(String(255), nullable=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) is_active = Column(Boolean, default=True) is_developer = Column(Boolean, default=False) favorite_courses = relationship("FavoriteCourse", back_populates="user", cascade="all, delete-orphan") progress = relationship("Progress", back_populates="user", cascade="all, delete-orphan") class Course(Base): __tablename__ = "courses" id = Column(Integer, primary_key=True, index=True) title = Column(String(255), nullable=False) description = Column(Text, nullable=False) level = Column(String(50), nullable=False) duration = Column(Integer, default=0, nullable=False) content = Column(Text) author_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) status = Column(String(20), default='draft', nullable=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) author = relationship("User") modules = relationship("CourseModule", back_populates="course", cascade="all, delete-orphan") favorites = relationship("FavoriteCourse", back_populates="course", cascade="all, delete-orphan") progress_records = relationship("Progress", back_populates="course", cascade="all, delete-orphan") class CourseModule(Base): __tablename__ = "course_modules" id = Column(Integer, primary_key=True, index=True) course_id = Column(Integer, ForeignKey("courses.id", ondelete="CASCADE"), nullable=False) title = Column(String(255), nullable=False) description = Column(Text) duration = Column(String(100)) # e.g., "2 weeks", "10 hours" order_index = Column(Integer, default=0, nullable=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) course = relationship("Course", back_populates="modules") lessons = relationship("Lesson", back_populates="module", cascade="all, delete-orphan") class Lesson(Base): __tablename__ = "lessons" id = Column(Integer, primary_key=True, index=True) module_id = Column(Integer, ForeignKey("course_modules.id", ondelete="CASCADE"), nullable=False) title = Column(String(255), nullable=False) description = Column(Text) order_index = Column(Integer, default=0, nullable=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) module = relationship("CourseModule", back_populates="lessons") steps = relationship("LessonStep", back_populates="lesson", cascade="all, delete-orphan") class LessonStep(Base): __tablename__ = "lesson_steps" id = Column(Integer, primary_key=True, index=True) lesson_id = Column(Integer, ForeignKey("lessons.id", ondelete="CASCADE"), nullable=False) step_type = Column(String(50), nullable=False) title = Column(String(255)) content = Column(Text) content_html = Column(Text) file_url = Column(String(500)) test_settings = Column(JSON) # {passing_score, max_attempts, time_limit, show_answers} order_index = Column(Integer, default=0, nullable=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) lesson = relationship("Lesson", back_populates="steps") test_questions = relationship("TestQuestion", back_populates="step", cascade="all, delete-orphan") test_attempts = relationship("TestAttempt", back_populates="step", cascade="all, delete-orphan") class FavoriteCourse(Base): __tablename__ = "favorite_courses" user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), primary_key=True) course_id = Column(Integer, ForeignKey("courses.id", ondelete="CASCADE"), primary_key=True) added_at = Column(DateTime(timezone=True), server_default=func.now()) user = relationship("User", back_populates="favorite_courses") course = relationship("Course", back_populates="favorites") class Progress(Base): __tablename__ = "progress" user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), primary_key=True) course_id = Column(Integer, ForeignKey("courses.id", ondelete="CASCADE"), primary_key=True) completed_lessons = Column(Integer, default=0, nullable=False) total_lessons = Column(Integer, default=0, nullable=False) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) user = relationship("User", back_populates="progress") course = relationship("Course") class CourseDraft(Base): __tablename__ = "course_drafts" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) title = Column(String(255), nullable=False) description = Column(Text) level = Column(String(50), nullable=False) content = Column(Text, nullable=False) structure = Column(Text) # JSON строка для структуры модулей created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) user = relationship("User") class TestQuestion(Base): __tablename__ = "test_questions" id = Column(Integer, primary_key=True, index=True) step_id = Column(Integer, ForeignKey("lesson_steps.id", ondelete="CASCADE"), nullable=False) question_text = Column(Text, nullable=False) question_type = Column(String(50), nullable=False) # single_choice, multiple_choice, text_answer, match points = Column(Integer, default=1) order_index = Column(Integer, default=0) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) step = relationship("LessonStep", back_populates="test_questions") answers = relationship("TestAnswer", back_populates="question", cascade="all, delete-orphan") match_items = relationship("TestMatchItem", back_populates="question", cascade="all, delete-orphan") class TestAnswer(Base): __tablename__ = "test_answers" id = Column(Integer, primary_key=True, index=True) question_id = Column(Integer, ForeignKey("test_questions.id", ondelete="CASCADE"), nullable=False) answer_text = Column(Text, nullable=False) is_correct = Column(Boolean, default=False) order_index = Column(Integer, default=0) created_at = Column(DateTime(timezone=True), server_default=func.now()) question = relationship("TestQuestion", back_populates="answers") class TestMatchItem(Base): __tablename__ = "test_match_items" id = Column(Integer, primary_key=True, index=True) question_id = Column(Integer, ForeignKey("test_questions.id", ondelete="CASCADE"), nullable=False) left_text = Column(Text, nullable=False) right_text = Column(Text, nullable=False) order_index = Column(Integer, default=0) created_at = Column(DateTime(timezone=True), server_default=func.now()) question = relationship("TestQuestion", back_populates="match_items") class TestAttempt(Base): __tablename__ = "test_attempts" id = Column(Integer, primary_key=True, index=True) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) step_id = Column(Integer, ForeignKey("lesson_steps.id", ondelete="CASCADE"), nullable=False) score = Column(Numeric(5, 2)) max_score = Column(Numeric(5, 2)) passed = Column(Boolean, default=False) answers = Column(JSON) # {question_id: answer_id или [answer_ids] или text или {left_id: right_id}} started_at = Column(DateTime(timezone=True), server_default=func.now()) completed_at = Column(DateTime(timezone=True)) user = relationship("User") step = relationship("LessonStep", back_populates="test_attempts")