Старт

This commit is contained in:
2026-03-13 14:39:43 +08:00
commit a2cc480644
88 changed files with 18526 additions and 0 deletions

187
backend/app/models.py Normal file
View File

@@ -0,0 +1,187 @@
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")