121 lines
5.6 KiB
Python
121 lines
5.6 KiB
Python
"""initial
|
|
|
|
Revision ID: 0001_initial
|
|
Revises:
|
|
Create Date: 2026-02-03 18:30:00
|
|
|
|
"""
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
revision = "0001_initial"
|
|
down_revision = None
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.create_table(
|
|
"users",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column("email", sa.String(length=255), nullable=False),
|
|
sa.Column("password_hash", sa.String(length=255), nullable=False),
|
|
sa.Column("role", sa.Enum("admin", "learner", name="role_enum"), nullable=False),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
|
)
|
|
op.create_index(op.f("ix_users_email"), "users", ["email"], unique=True)
|
|
|
|
op.create_table(
|
|
"courses",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column("title", sa.String(length=255), nullable=False),
|
|
sa.Column("description", sa.Text()),
|
|
sa.Column("is_published", sa.Boolean(), nullable=False, server_default=sa.false()),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
|
)
|
|
|
|
op.create_table(
|
|
"modules",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column("course_id", sa.Integer(), sa.ForeignKey("courses.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("order_index", sa.Integer(), nullable=False),
|
|
sa.Column("type", sa.Enum("content", "quiz", name="module_type_enum"), nullable=False),
|
|
sa.Column("title", sa.String(length=255), nullable=False),
|
|
sa.Column("content_text", sa.Text()),
|
|
sa.Column("pass_score", sa.Integer()),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
|
)
|
|
op.create_index(op.f("ix_modules_course_id"), "modules", ["course_id"])
|
|
|
|
op.create_table(
|
|
"quiz_questions",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column("module_id", sa.Integer(), sa.ForeignKey("modules.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("prompt", sa.Text(), nullable=False),
|
|
sa.Column("question_type", sa.Enum("mcq", name="question_type_enum"), nullable=False),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
|
)
|
|
op.create_index(op.f("ix_quiz_questions_module_id"), "quiz_questions", ["module_id"])
|
|
|
|
op.create_table(
|
|
"quiz_choices",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column("question_id", sa.Integer(), sa.ForeignKey("quiz_questions.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("text", sa.Text(), nullable=False),
|
|
sa.Column("is_correct", sa.Boolean(), nullable=False, server_default=sa.false()),
|
|
)
|
|
op.create_index(op.f("ix_quiz_choices_question_id"), "quiz_choices", ["question_id"])
|
|
|
|
op.create_table(
|
|
"enrollments",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column("user_id", sa.Integer(), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("course_id", sa.Integer(), sa.ForeignKey("courses.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("enrolled_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
|
sa.UniqueConstraint("user_id", "course_id", name="uq_enrollment"),
|
|
)
|
|
op.create_index(op.f("ix_enrollments_user_id"), "enrollments", ["user_id"])
|
|
op.create_index(op.f("ix_enrollments_course_id"), "enrollments", ["course_id"])
|
|
|
|
op.create_table(
|
|
"module_progress",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column("user_id", sa.Integer(), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("module_id", sa.Integer(), sa.ForeignKey("modules.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("status", sa.Enum("locked", "unlocked", "completed", name="progress_status_enum"), nullable=False),
|
|
sa.Column("completed_at", sa.DateTime(timezone=True)),
|
|
sa.Column("score", sa.Float()),
|
|
sa.UniqueConstraint("user_id", "module_id", name="uq_module_progress"),
|
|
)
|
|
op.create_index(op.f("ix_module_progress_user_id"), "module_progress", ["user_id"])
|
|
op.create_index(op.f("ix_module_progress_module_id"), "module_progress", ["module_id"])
|
|
|
|
op.create_table(
|
|
"quiz_attempts",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column("user_id", sa.Integer(), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("module_id", sa.Integer(), sa.ForeignKey("modules.id", ondelete="CASCADE"), nullable=False),
|
|
sa.Column("submitted_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
|
sa.Column("score", sa.Float(), nullable=False),
|
|
sa.Column("passed", sa.Boolean(), nullable=False),
|
|
sa.Column("answers_json", sa.JSON(), nullable=False),
|
|
)
|
|
op.create_index(op.f("ix_quiz_attempts_user_id"), "quiz_attempts", ["user_id"])
|
|
op.create_index(op.f("ix_quiz_attempts_module_id"), "quiz_attempts", ["module_id"])
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_table("quiz_attempts")
|
|
op.drop_table("module_progress")
|
|
op.drop_table("enrollments")
|
|
op.drop_table("quiz_choices")
|
|
op.drop_table("quiz_questions")
|
|
op.drop_table("modules")
|
|
op.drop_table("courses")
|
|
op.drop_table("users")
|
|
op.execute("DROP TYPE IF EXISTS role_enum")
|
|
op.execute("DROP TYPE IF EXISTS module_type_enum")
|
|
op.execute("DROP TYPE IF EXISTS progress_status_enum")
|
|
op.execute("DROP TYPE IF EXISTS question_type_enum")
|