import uuid import django.db.models.deletion from django.conf import settings from django.db import migrations, models import apps.content.validators class Migration(migrations.Migration): initial = True dependencies = [ ("taxonomy", "0001_initial"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ # ------------------------------------------------------------------ # Problem (container) — current_version added after ProblemVersion. # ------------------------------------------------------------------ migrations.CreateModel( name="Problem", fields=[ ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), ("deleted_at", models.DateTimeField(blank=True, null=True)), ("slug", models.SlugField(max_length=160, unique=True)), ("title", models.CharField(max_length=255)), ("problem_type", models.CharField(choices=[("multiple_choice", "Multiple choice"), ("numeric", "Numeric answer"), ("symbolic", "Symbolic expression"), ("proof", "Proof / free response"), ("code", "Code challenge"), ("ordering", "Ordering"), ("matching", "Matching"), ("widget", "Interactive widget")], max_length=32)), ("difficulty", models.PositiveSmallIntegerField(choices=[(1, "Introductory"), (2, "Easy"), (3, "Medium"), (4, "Hard"), (5, "Expert")], default=3)), ("status", models.CharField(choices=[("draft", "Draft"), ("published", "Published"), ("archived", "Archived")], default="draft", max_length=16)), ("license", models.CharField(choices=[("CC-BY-SA-4.0", "Creative Commons Attribution-ShareAlike 4.0"), ("CC-BY-4.0", "Creative Commons Attribution 4.0"), ("CC0-1.0", "CC0 1.0 Public Domain Dedication")], default="CC-BY-SA-4.0", max_length=32)), ("language", models.CharField(default="en", max_length=12)), ("attempt_count", models.PositiveIntegerField(default=0)), ("solve_count", models.PositiveIntegerField(default=0)), ("vote_score", models.IntegerField(default=0)), ("forked_from", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="forks", to="content.problem")), ("owner", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="owned_problems", to=settings.AUTH_USER_MODEL)), ("prerequisites", models.ManyToManyField(blank=True, related_name="unlocked_by", symmetrical=False, to="content.problem")), ("tags", models.ManyToManyField(blank=True, related_name="problems", to="taxonomy.tag")), ("topics", models.ManyToManyField(blank=True, related_name="problems", to="taxonomy.topic")), ], options={"ordering": ["-created_at"]}, ), migrations.CreateModel( name="ProblemVersion", fields=[ ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), ("version_number", models.PositiveIntegerField()), ("status", models.CharField(choices=[("draft", "Draft"), ("submitted", "Submitted"), ("in_review", "In review"), ("changes_requested", "Changes requested"), ("accepted", "Accepted"), ("rejected", "Rejected")], default="draft", max_length=32)), ("title", models.CharField(max_length=255)), ("statement", models.JSONField(validators=[apps.content.validators.validate_document])), ("answer_spec", models.JSONField()), ("widget_ref", models.CharField(blank=True, default="", max_length=128)), ("changelog", models.TextField(blank=True, default="")), ("checksum", models.CharField(blank=True, editable=False, max_length=64)), ("submitted_at", models.DateTimeField(blank=True, null=True)), ("reviewed_at", models.DateTimeField(blank=True, null=True)), ("published_at", models.DateTimeField(blank=True, null=True)), ("author", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="authored_problem_versions", to=settings.AUTH_USER_MODEL)), ("parent_version", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="children", to="content.problemversion")), ("problem", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="versions", to="content.problem")), ], options={"ordering": ["-version_number"]}, ), migrations.AddField( model_name="problem", name="current_version", field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="+", to="content.problemversion"), ), migrations.AddIndex( model_name="problem", index=models.Index(fields=["status", "difficulty"], name="problem_status_diff_idx"), ), migrations.AddIndex( model_name="problem", index=models.Index(fields=["problem_type"], name="problem_type_idx"), ), migrations.AddIndex( model_name="problem", index=models.Index(fields=["language"], name="problem_language_idx"), ), migrations.AddIndex( model_name="problem", index=models.Index(fields=["owner", "status"], name="problem_owner_status_idx"), ), migrations.AddConstraint( model_name="problem", constraint=models.CheckConstraint(condition=models.Q(("difficulty__gte", 1)) & models.Q(("difficulty__lte", 5)), name="problem_difficulty_range"), ), migrations.AddIndex( model_name="problemversion", index=models.Index(fields=["problem", "status"], name="pversion_problem_status_idx"), ), migrations.AddIndex( model_name="problemversion", index=models.Index(fields=["status"], name="pversion_status_idx"), ), migrations.AddConstraint( model_name="problemversion", constraint=models.UniqueConstraint(fields=("problem", "version_number"), name="problemversion_unique_number"), ), migrations.AddConstraint( model_name="problemversion", constraint=models.CheckConstraint(condition=models.Q(("version_number__gte", 1)), name="problemversion_number_gte_1"), ), # ------------------------------------------------------------------ # Hint / Solution # ------------------------------------------------------------------ migrations.CreateModel( name="Hint", fields=[ ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), ("order", models.PositiveSmallIntegerField()), ("body", models.JSONField(validators=[apps.content.validators.validate_document])), ("penalty_percent", models.PositiveSmallIntegerField(default=0)), ("problem_version", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="hints", to="content.problemversion")), ], options={"ordering": ["order"]}, ), migrations.AddConstraint( model_name="hint", constraint=models.UniqueConstraint(fields=("problem_version", "order"), name="hint_unique_order"), ), migrations.AddConstraint( model_name="hint", constraint=models.CheckConstraint(condition=models.Q(("penalty_percent__lte", 100)), name="hint_penalty_lte_100"), ), migrations.CreateModel( name="Solution", fields=[ ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), ("order", models.PositiveSmallIntegerField(default=1)), ("is_official", models.BooleanField(default=True)), ("body", models.JSONField(validators=[apps.content.validators.validate_document])), ("author", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="problem_solutions", to=settings.AUTH_USER_MODEL)), ("problem_version", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="solutions", to="content.problemversion")), ], options={"ordering": ["order"]}, ), migrations.AddConstraint( model_name="solution", constraint=models.UniqueConstraint(fields=("problem_version", "order"), name="solution_unique_order"), ), # ------------------------------------------------------------------ # ProblemAttempt # ------------------------------------------------------------------ migrations.CreateModel( name="ProblemAttempt", fields=[ ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), ("attempt_number", models.PositiveIntegerField()), ("submitted_answer", models.JSONField()), ("is_correct", models.BooleanField(blank=True, null=True)), ("grading_status", models.CharField(choices=[("pending", "Pending"), ("graded", "Graded"), ("manual_review", "Awaiting manual review"), ("error", "Grading error")], default="pending", max_length=16)), ("score", models.DecimalField(blank=True, decimal_places=4, max_digits=6, null=True)), ("hints_used", models.PositiveSmallIntegerField(default=0)), ("time_spent_ms", models.PositiveIntegerField(blank=True, null=True)), ("feedback", models.JSONField(blank=True, null=True)), ("problem", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="attempts", to="content.problem")), ("problem_version", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="attempts", to="content.problemversion")), ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="problem_attempts", to=settings.AUTH_USER_MODEL)), ], options={"ordering": ["-created_at"]}, ), migrations.AddIndex( model_name="problemattempt", index=models.Index(fields=["user", "problem"], name="attempt_user_problem_idx"), ), migrations.AddIndex( model_name="problemattempt", index=models.Index(fields=["problem", "is_correct"], name="attempt_problem_correct_idx"), ), migrations.AddIndex( model_name="problemattempt", index=models.Index(fields=["grading_status"], name="attempt_grading_idx"), ), migrations.AddConstraint( model_name="problemattempt", constraint=models.UniqueConstraint(fields=("user", "problem", "attempt_number"), name="attempt_unique_number"), ), migrations.AddConstraint( model_name="problemattempt", constraint=models.CheckConstraint(condition=models.Q(("score__isnull", True)) | (models.Q(("score__gte", 0)) & models.Q(("score__lte", 1))), name="attempt_score_range"), ), # ------------------------------------------------------------------ # Course (container) — current_version added after CourseVersion. # ------------------------------------------------------------------ migrations.CreateModel( name="Course", fields=[ ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), ("deleted_at", models.DateTimeField(blank=True, null=True)), ("slug", models.SlugField(max_length=160, unique=True)), ("title", models.CharField(max_length=255)), ("difficulty", models.PositiveSmallIntegerField(choices=[(1, "Introductory"), (2, "Easy"), (3, "Medium"), (4, "Hard"), (5, "Expert")], default=3)), ("status", models.CharField(choices=[("draft", "Draft"), ("published", "Published"), ("archived", "Archived")], default="draft", max_length=16)), ("license", models.CharField(choices=[("CC-BY-SA-4.0", "Creative Commons Attribution-ShareAlike 4.0"), ("CC-BY-4.0", "Creative Commons Attribution 4.0"), ("CC0-1.0", "CC0 1.0 Public Domain Dedication")], default="CC-BY-SA-4.0", max_length=32)), ("language", models.CharField(default="en", max_length=12)), ("enrollment_count", models.PositiveIntegerField(default=0)), ("vote_score", models.IntegerField(default=0)), ("forked_from", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="forks", to="content.course")), ("owner", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="owned_courses", to=settings.AUTH_USER_MODEL)), ("tags", models.ManyToManyField(blank=True, related_name="courses", to="taxonomy.tag")), ("topics", models.ManyToManyField(blank=True, related_name="courses", to="taxonomy.topic")), ], options={"ordering": ["-created_at"]}, ), migrations.CreateModel( name="CourseVersion", fields=[ ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), ("version_number", models.PositiveIntegerField()), ("status", models.CharField(choices=[("draft", "Draft"), ("submitted", "Submitted"), ("in_review", "In review"), ("changes_requested", "Changes requested"), ("accepted", "Accepted"), ("rejected", "Rejected")], default="draft", max_length=32)), ("title", models.CharField(max_length=255)), ("summary", models.CharField(blank=True, default="", max_length=500)), ("overview", models.JSONField(blank=True, default=dict)), ("estimated_hours", models.PositiveSmallIntegerField(blank=True, null=True)), ("changelog", models.TextField(blank=True, default="")), ("checksum", models.CharField(blank=True, editable=False, max_length=64)), ("submitted_at", models.DateTimeField(blank=True, null=True)), ("reviewed_at", models.DateTimeField(blank=True, null=True)), ("published_at", models.DateTimeField(blank=True, null=True)), ("author", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="authored_course_versions", to=settings.AUTH_USER_MODEL)), ("course", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="versions", to="content.course")), ("parent_version", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="children", to="content.courseversion")), ], options={"ordering": ["-version_number"]}, ), migrations.AddField( model_name="course", name="current_version", field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="+", to="content.courseversion"), ), migrations.AddIndex( model_name="course", index=models.Index(fields=["status", "difficulty"], name="course_status_diff_idx"), ), migrations.AddIndex( model_name="course", index=models.Index(fields=["owner", "status"], name="course_owner_status_idx"), ), migrations.AddIndex( model_name="course", index=models.Index(fields=["language"], name="course_language_idx"), ), migrations.AddConstraint( model_name="course", constraint=models.CheckConstraint(condition=models.Q(("difficulty__gte", 1)) & models.Q(("difficulty__lte", 5)), name="course_difficulty_range"), ), migrations.AddIndex( model_name="courseversion", index=models.Index(fields=["course", "status"], name="cversion_course_status_idx"), ), migrations.AddConstraint( model_name="courseversion", constraint=models.UniqueConstraint(fields=("course", "version_number"), name="courseversion_unique_number"), ), migrations.AddConstraint( model_name="courseversion", constraint=models.CheckConstraint(condition=models.Q(("version_number__gte", 1)), name="courseversion_number_gte_1"), ), # ------------------------------------------------------------------ # Module / Lesson / LessonProblem # ------------------------------------------------------------------ migrations.CreateModel( name="Module", fields=[ ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), ("order", models.PositiveSmallIntegerField()), ("title", models.CharField(max_length=255)), ("summary", models.CharField(blank=True, default="", max_length=500)), ("course_version", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="modules", to="content.courseversion")), ], options={"ordering": ["order"]}, ), migrations.AddConstraint( model_name="module", constraint=models.UniqueConstraint(fields=("course_version", "order"), name="module_unique_order"), ), migrations.CreateModel( name="Lesson", fields=[ ("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), ("order", models.PositiveSmallIntegerField()), ("title", models.CharField(max_length=255)), ("lesson_type", models.CharField(choices=[("lesson", "Lesson"), ("problem_set", "Problem set"), ("quiz", "Quiz"), ("project", "Project")], default="lesson", max_length=16)), ("body", models.JSONField(blank=True, default=dict)), ("duration_minutes", models.PositiveSmallIntegerField(blank=True, null=True)), ("module", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="lessons", to="content.module")), ], options={"ordering": ["order"]}, ), migrations.AddConstraint( model_name="lesson", constraint=models.UniqueConstraint(fields=("module", "order"), name="lesson_unique_order"), ), migrations.CreateModel( name="LessonProblem", fields=[ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("order", models.PositiveSmallIntegerField()), ("is_required", models.BooleanField(default=True)), ("points", models.PositiveSmallIntegerField(default=1)), ("lesson", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="lesson_problems", to="content.lesson")), ("problem", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="lesson_links", to="content.problem")), ], options={"ordering": ["order"]}, ), migrations.AddConstraint( model_name="lessonproblem", constraint=models.UniqueConstraint(fields=("lesson", "order"), name="lessonproblem_unique_order"), ), migrations.AddConstraint( model_name="lessonproblem", constraint=models.UniqueConstraint(fields=("lesson", "problem"), name="lessonproblem_unique_problem"), ), migrations.AddField( model_name="lesson", name="problems", field=models.ManyToManyField(blank=True, related_name="lessons", through="content.LessonProblem", to="content.problem"), ), ]