from datetime import datetime from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, Float from sqlalchemy.orm import relationship from app.database import Base class Course(Base): __tablename__ = "courses" id = Column(Integer, primary_key=True, index=True) title = Column(String, nullable=False) description = Column(Text, nullable=True) thumbnail_path = Column(String, nullable=True) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) status = Column(String, default="draft") # draft, published, archived is_featured = Column(Integer, default=0) requires_subscription = Column(Integer, default=0) # 0=free, 1=paid (gate ready) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) modules = relationship("CourseModule", back_populates="course", cascade="all, delete-orphan", order_by="CourseModule.position") enrollments = relationship("CourseEnrollment", back_populates="course", cascade="all, delete-orphan") creator = relationship("User") 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, nullable=False) description = Column(Text, nullable=True) position = Column(Integer, default=0) course = relationship("Course", back_populates="modules") lessons = relationship("CourseLesson", back_populates="module", cascade="all, delete-orphan", order_by="CourseLesson.position") class CourseLesson(Base): __tablename__ = "course_lessons" id = Column(Integer, primary_key=True, index=True) module_id = Column(Integer, ForeignKey("course_modules.id", ondelete="CASCADE"), nullable=False) title = Column(String, nullable=False) description = Column(Text, nullable=True) position = Column(Integer, default=0) lesson_type = Column(String, nullable=False) # text, video, document, quiz, live_session, scorm_package content_text = Column(Text, nullable=True) # markdown for text lessons video_url = Column(String, nullable=True) # Vimeo/YouTube URL video_provider = Column(String, nullable=True) # vimeo, youtube, local local_file_path = Column(String, nullable=True) # uploaded files quiz_id = Column(Integer, ForeignKey("quizzes.id", ondelete="SET NULL"), nullable=True) live_session_url = Column(String, nullable=True) # BBB/Zoom/Meet join link live_session_start = Column(DateTime, nullable=True) live_session_end = Column(DateTime, nullable=True) bbb_meeting_id = Column(String, nullable=True) # for BBB API integration duration_minutes = Column(Integer, nullable=True) is_required = Column(Integer, default=1) # 1=must complete to progress module = relationship("CourseModule", back_populates="lessons") class CourseEnrollment(Base): __tablename__ = "course_enrollments" id = Column(Integer, primary_key=True, index=True) course_id = Column(Integer, ForeignKey("courses.id", ondelete="CASCADE"), nullable=False) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) enrolled_at = Column(DateTime, default=datetime.utcnow) completed_at = Column(DateTime, nullable=True) progress_pct = Column(Float, default=0.0) status = Column(String, default="enrolled") # enrolled, in_progress, completed course = relationship("Course", back_populates="enrollments") user = relationship("User") lesson_progress = relationship("CourseLessonProgress", back_populates="enrollment", cascade="all, delete-orphan") class CourseLessonProgress(Base): __tablename__ = "course_lesson_progress" id = Column(Integer, primary_key=True, index=True) enrollment_id = Column(Integer, ForeignKey("course_enrollments.id", ondelete="CASCADE"), nullable=False) lesson_id = Column(Integer, ForeignKey("course_lessons.id", ondelete="CASCADE"), nullable=False) status = Column(String, default="not_started") # not_started, in_progress, completed started_at = Column(DateTime, nullable=True) completed_at = Column(DateTime, nullable=True) score = Column(Float, nullable=True) # for quiz lessons time_spent_seconds = Column(Integer, default=0) enrollment = relationship("CourseEnrollment", back_populates="lesson_progress")