diff --git a/core/apps/account/migrations/0001_initial.py b/core/apps/account/migrations/0001_initial.py index ee2662e..868430e 100644 --- a/core/apps/account/migrations/0001_initial.py +++ b/core/apps/account/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import apps.common.storage import apps.common.util diff --git a/core/apps/assignment/migrations/0001_initial.py b/core/apps/assignment/migrations/0001_initial.py index 0272c68..ef04c2c 100644 --- a/core/apps/assignment/migrations/0001_initial.py +++ b/core/apps/assignment/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import apps.common.util import django.contrib.postgres.fields @@ -86,30 +86,6 @@ class Migration(migrations.Migration): 'abstract': False, }, ), - migrations.CreateModel( - name='Solution', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('explanation', models.TextField(blank=True, default='', verbose_name='Explanation')), - ], - options={ - 'verbose_name': 'Solution', - 'verbose_name_plural': 'Solutions', - }, - ), - migrations.CreateModel( - name='SolutionEvent', - fields=[ - ('pgh_id', models.AutoField(primary_key=True, serialize=False)), - ('pgh_created_at', models.DateTimeField(auto_now_add=True)), - ('pgh_label', models.TextField(help_text='The event label.')), - ('id', models.BigIntegerField()), - ('explanation', models.TextField(blank=True, default='', verbose_name='Explanation')), - ], - options={ - 'abstract': False, - }, - ), migrations.CreateModel( name='Submission', fields=[ @@ -157,10 +133,11 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('grade_due_days', models.PositiveSmallIntegerField(verbose_name='Grading Due Days')), ('appeal_deadline_days', models.PositiveSmallIntegerField(verbose_name='Appeal Deadline Days')), ('confirm_due_days', models.PositiveSmallIntegerField(verbose_name='Confirm Due Days')), + ('sample_attachment', models.FileField(blank=True, max_length=255, null=True, upload_to='', verbose_name='Sample Attachment')), ('honor_code', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='operation.honorcode', verbose_name='Honor Code')), ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner')), ], @@ -304,7 +281,6 @@ class Migration(migrations.Migration): ('supplement', models.TextField(blank=True, default='', verbose_name='Supplement')), ('attachment_file_count', models.PositiveSmallIntegerField(default=1, verbose_name='Attachment File Count')), ('attachment_file_types', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=10), blank=True, default=list, verbose_name='Attachment File Types')), - ('sample_attachment', models.FileField(blank=True, max_length=255, null=True, upload_to='', verbose_name='Sample Attachment')), ('plagiarism_threshold', models.PositiveSmallIntegerField(verbose_name='Plagiarism Threshold Percentage')), ('attachments', models.ManyToManyField(blank=True, related_name='+', to='operation.attachment', verbose_name='Attachments')), ], @@ -364,7 +340,6 @@ class Migration(migrations.Migration): ('supplement', models.TextField(blank=True, default='', verbose_name='Supplement')), ('attachment_file_count', models.PositiveSmallIntegerField(default=1, verbose_name='Attachment File Count')), ('attachment_file_types', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=10), blank=True, default=list, verbose_name='Attachment File Types')), - ('sample_attachment', models.FileField(blank=True, max_length=255, null=True, upload_to='', verbose_name='Sample Attachment')), ('plagiarism_threshold', models.PositiveSmallIntegerField(verbose_name='Plagiarism Threshold Percentage')), ('pgh_context', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pghistory.context')), ('pgh_obj', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='assignment.question')), @@ -379,6 +354,28 @@ class Migration(migrations.Migration): name='pool', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='assignment.questionpool', verbose_name='Question Pool'), ), + migrations.AddField( + model_name='assignment', + name='question_pool', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assignment.questionpool', verbose_name='Question Pool'), + ), + migrations.CreateModel( + name='QuestionPoolEvent', + fields=[ + ('pgh_id', models.AutoField(primary_key=True, serialize=False)), + ('pgh_created_at', models.DateTimeField(auto_now_add=True)), + ('pgh_label', models.TextField(help_text='The event label.')), + ('id', models.BigIntegerField()), + ('title', models.CharField(max_length=255, verbose_name='Title')), + ('description', models.TextField(blank=True, default='', verbose_name='Description')), + ('owner', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Owner')), + ('pgh_context', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pghistory.context')), + ('pgh_obj', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='assignment.questionpool')), + ], + options={ + 'abstract': False, + }, + ), migrations.CreateModel( name='AssignmentEvent', fields=[ @@ -398,15 +395,17 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('grade_due_days', models.PositiveSmallIntegerField(verbose_name='Grading Due Days')), ('appeal_deadline_days', models.PositiveSmallIntegerField(verbose_name='Appeal Deadline Days')), ('confirm_due_days', models.PositiveSmallIntegerField(verbose_name='Confirm Due Days')), + ('sample_attachment', models.FileField(blank=True, max_length=255, null=True, upload_to='', verbose_name='Sample Attachment')), ('honor_code', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='operation.honorcode', verbose_name='Honor Code')), ('owner', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Owner')), ('pgh_context', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pghistory.context')), ('pgh_obj', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='assignment.assignment')), ('question_pool', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='assignment.questionpool', verbose_name='Question Pool')), + ('rubric', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='assignment.rubric', verbose_name='Rubric')), ], options={ 'abstract': False, @@ -414,25 +413,8 @@ class Migration(migrations.Migration): ), migrations.AddField( model_name='assignment', - name='question_pool', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assignment.questionpool', verbose_name='Question Pool'), - ), - migrations.CreateModel( - name='QuestionPoolEvent', - fields=[ - ('pgh_id', models.AutoField(primary_key=True, serialize=False)), - ('pgh_created_at', models.DateTimeField(auto_now_add=True)), - ('pgh_label', models.TextField(help_text='The event label.')), - ('id', models.BigIntegerField()), - ('title', models.CharField(max_length=255, verbose_name='Title')), - ('description', models.TextField(blank=True, default='', verbose_name='Description')), - ('owner', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Owner')), - ('pgh_context', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pghistory.context')), - ('pgh_obj', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='assignment.questionpool')), - ], - options={ - 'abstract': False, - }, + name='rubric', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assignment.rubric', verbose_name='Rubric'), ), pgtrigger.migrations.AddTrigger( model_name='rubric', @@ -486,36 +468,6 @@ class Migration(migrations.Migration): name='pgh_obj', field=models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='assignment.rubric'), ), - migrations.AddField( - model_name='solution', - name='question', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='assignment.question', verbose_name='Question'), - ), - migrations.AddField( - model_name='solution', - name='rubric', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assignment.rubric', verbose_name='Rubric'), - ), - migrations.AddField( - model_name='solutionevent', - name='pgh_context', - field=models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pghistory.context'), - ), - migrations.AddField( - model_name='solutionevent', - name='pgh_obj', - field=models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='assignment.solution'), - ), - migrations.AddField( - model_name='solutionevent', - name='question', - field=models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='assignment.question', verbose_name='Question'), - ), - migrations.AddField( - model_name='solutionevent', - name='rubric', - field=models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='assignment.rubric', verbose_name='Rubric'), - ), migrations.AddField( model_name='submission', name='attachments', @@ -623,15 +575,19 @@ class Migration(migrations.Migration): ), pgtrigger.migrations.AddTrigger( model_name='question', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_questionevent" ("attachment_file_count", "attachment_file_types", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "plagiarism_threshold", "pool_id", "question", "sample_attachment", "supplement") VALUES (NEW."attachment_file_count", NEW."attachment_file_types", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."plagiarism_threshold", NEW."pool_id", NEW."question", NEW."sample_attachment", NEW."supplement"); RETURN NULL;', hash='264356de2d836c6c58d3c92712043cde6af008a5', operation='INSERT', pgid='pgtrigger_insert_insert_ec4a4', table='assignment_question', when='AFTER')), + trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_questionevent" ("attachment_file_count", "attachment_file_types", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "plagiarism_threshold", "pool_id", "question", "supplement") VALUES (NEW."attachment_file_count", NEW."attachment_file_types", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."plagiarism_threshold", NEW."pool_id", NEW."question", NEW."supplement"); RETURN NULL;', hash='ac65eeb67c670d74ad31e29e2cdec60661b4dedf', operation='INSERT', pgid='pgtrigger_insert_insert_ec4a4', table='assignment_question', when='AFTER')), ), pgtrigger.migrations.AddTrigger( model_name='question', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD.* IS DISTINCT FROM NEW.*)', func='INSERT INTO "assignment_questionevent" ("attachment_file_count", "attachment_file_types", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "plagiarism_threshold", "pool_id", "question", "sample_attachment", "supplement") VALUES (NEW."attachment_file_count", NEW."attachment_file_types", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."plagiarism_threshold", NEW."pool_id", NEW."question", NEW."sample_attachment", NEW."supplement"); RETURN NULL;', hash='ee2a4bed5b1fb8a491a91223ffb55c797c01fe4d', operation='UPDATE', pgid='pgtrigger_update_update_6ef6c', table='assignment_question', when='AFTER')), + trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD.* IS DISTINCT FROM NEW.*)', func='INSERT INTO "assignment_questionevent" ("attachment_file_count", "attachment_file_types", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "plagiarism_threshold", "pool_id", "question", "supplement") VALUES (NEW."attachment_file_count", NEW."attachment_file_types", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."plagiarism_threshold", NEW."pool_id", NEW."question", NEW."supplement"); RETURN NULL;', hash='eff17638cb2cc8f38a72a5929750b25e7ab7272f', operation='UPDATE', pgid='pgtrigger_update_update_6ef6c', table='assignment_question', when='AFTER')), ), pgtrigger.migrations.AddTrigger( model_name='question', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_questionevent" ("attachment_file_count", "attachment_file_types", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "plagiarism_threshold", "pool_id", "question", "sample_attachment", "supplement") VALUES (OLD."attachment_file_count", OLD."attachment_file_types", OLD."id", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."plagiarism_threshold", OLD."pool_id", OLD."question", OLD."sample_attachment", OLD."supplement"); RETURN NULL;', hash='d057a810f491c52d085c4d84bb5b211851b8da19', operation='DELETE', pgid='pgtrigger_delete_delete_dc36d', table='assignment_question', when='AFTER')), + trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_questionevent" ("attachment_file_count", "attachment_file_types", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "plagiarism_threshold", "pool_id", "question", "supplement") VALUES (OLD."attachment_file_count", OLD."attachment_file_types", OLD."id", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."plagiarism_threshold", OLD."pool_id", OLD."question", OLD."supplement"); RETURN NULL;', hash='3ec3e58da7ff73b07683a37d8a7c7348328ba765', operation='DELETE', pgid='pgtrigger_delete_delete_dc36d', table='assignment_question', when='AFTER')), + ), + pgtrigger.migrations.AddTrigger( + model_name='questionpoolevent', + trigger=pgtrigger.compiler.Trigger(name='append_only', sql=pgtrigger.compiler.UpsertTriggerSql(func="RAISE EXCEPTION 'pgtrigger: Cannot update or delete rows from % table', TG_TABLE_NAME;", hash='504060dafa7151ff525a8edd59b4af31a8611c7d', operation='UPDATE OR DELETE', pgid='pgtrigger_append_only_ef48c', table='assignment_questionpoolevent', when='BEFORE')), ), pgtrigger.migrations.AddTrigger( model_name='assignmentevent', @@ -643,19 +599,15 @@ class Migration(migrations.Migration): ), pgtrigger.migrations.AddTrigger( model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."published", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='6b6bcae561b473c3ac90486f6db1be3c1aa8fc21', operation='INSERT', pgid='pgtrigger_insert_insert_e3a8d', table='assignment_assignment', when='AFTER')), + trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "rubric_id", "sample_attachment", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."published", NEW."question_pool_id", NEW."rubric_id", NEW."sample_attachment", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='21ac5a1dbcdaca2a2780136fbf794154a596c62c', operation='INSERT', pgid='pgtrigger_insert_insert_e3a8d', table='assignment_assignment', when='AFTER')), ), pgtrigger.migrations.AddTrigger( model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."appeal_deadline_days" IS DISTINCT FROM (NEW."appeal_deadline_days") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."confirm_due_days" IS DISTINCT FROM (NEW."confirm_due_days") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."grade_due_days" IS DISTINCT FROM (NEW."grade_due_days") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."published" IS DISTINCT FROM (NEW."published") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."published", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='b7a74042167cd7453f1abf532d6f66d3cb7496b0', operation='UPDATE', pgid='pgtrigger_update_update_629e4', table='assignment_assignment', when='AFTER')), + trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."appeal_deadline_days" IS DISTINCT FROM (NEW."appeal_deadline_days") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."confirm_due_days" IS DISTINCT FROM (NEW."confirm_due_days") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."grade_due_days" IS DISTINCT FROM (NEW."grade_due_days") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."published" IS DISTINCT FROM (NEW."published") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."rubric_id" IS DISTINCT FROM (NEW."rubric_id") OR OLD."sample_attachment" IS DISTINCT FROM (NEW."sample_attachment") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "rubric_id", "sample_attachment", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."published", NEW."question_pool_id", NEW."rubric_id", NEW."sample_attachment", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='31e50859d395027d1fe45fdebb78d526da55e3b8', operation='UPDATE', pgid='pgtrigger_update_update_629e4', table='assignment_assignment', when='AFTER')), ), pgtrigger.migrations.AddTrigger( model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (OLD."appeal_deadline_days", OLD."audience", OLD."confirm_due_days", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."grade_due_days", OLD."honor_code_id", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."published", OLD."question_pool_id", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='1899dea81dcda8400f32ea559e2117dea2405f8c', operation='DELETE', pgid='pgtrigger_delete_delete_1054f', table='assignment_assignment', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='questionpoolevent', - trigger=pgtrigger.compiler.Trigger(name='append_only', sql=pgtrigger.compiler.UpsertTriggerSql(func="RAISE EXCEPTION 'pgtrigger: Cannot update or delete rows from % table', TG_TABLE_NAME;", hash='504060dafa7151ff525a8edd59b4af31a8611c7d', operation='UPDATE OR DELETE', pgid='pgtrigger_append_only_ef48c', table='assignment_questionpoolevent', when='BEFORE')), + trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "rubric_id", "sample_attachment", "thumbnail", "title", "verification_required") VALUES (OLD."appeal_deadline_days", OLD."audience", OLD."confirm_due_days", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."grade_due_days", OLD."honor_code_id", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."published", OLD."question_pool_id", OLD."rubric_id", OLD."sample_attachment", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='bd3967fe3520f4bc3eb34aa5628ee090a3020e92', operation='DELETE', pgid='pgtrigger_delete_delete_1054f', table='assignment_assignment', when='AFTER')), ), migrations.AddConstraint( model_name='rubriccriterion', @@ -705,22 +657,6 @@ class Migration(migrations.Migration): model_name='rubricevent', trigger=pgtrigger.compiler.Trigger(name='append_only', sql=pgtrigger.compiler.UpsertTriggerSql(func="RAISE EXCEPTION 'pgtrigger: Cannot update or delete rows from % table', TG_TABLE_NAME;", hash='f10fef20e08264b0188f59a66fcb147eedd24822', operation='UPDATE OR DELETE', pgid='pgtrigger_append_only_76901', table='assignment_rubricevent', when='BEFORE')), ), - pgtrigger.migrations.AddTrigger( - model_name='solution', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_solutionevent" ("explanation", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_id", "rubric_id") VALUES (NEW."explanation", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."question_id", NEW."rubric_id"); RETURN NULL;', hash='d0d2be8105e5de228de09162674e2d0c0eae32dc', operation='INSERT', pgid='pgtrigger_insert_insert_1c170', table='assignment_solution', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='solution', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD.* IS DISTINCT FROM NEW.*)', func='INSERT INTO "assignment_solutionevent" ("explanation", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_id", "rubric_id") VALUES (NEW."explanation", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."question_id", NEW."rubric_id"); RETURN NULL;', hash='3700daefd633a153c792ac4794cb6e91c8fd92f1', operation='UPDATE', pgid='pgtrigger_update_update_aeb29', table='assignment_solution', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='solution', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_solutionevent" ("explanation", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_id", "rubric_id") VALUES (OLD."explanation", OLD."id", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."question_id", OLD."rubric_id"); RETURN NULL;', hash='751df5972bf7a829b96da3dab9efe56404930717', operation='DELETE', pgid='pgtrigger_delete_delete_e1f79', table='assignment_solution', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='solutionevent', - trigger=pgtrigger.compiler.Trigger(name='append_only', sql=pgtrigger.compiler.UpsertTriggerSql(func="RAISE EXCEPTION 'pgtrigger: Cannot update or delete rows from % table', TG_TABLE_NAME;", hash='bc3b6a7f3f384814ef83dba2b43b8081df477fd4', operation='UPDATE OR DELETE', pgid='pgtrigger_append_only_09dc1', table='assignment_solutionevent', when='BEFORE')), - ), pgtrigger.migrations.AddTrigger( model_name='submission', trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_submissionevent" ("answer", "attempt_id", "created", "extracted_text", "id", "modified", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id") VALUES (NEW."answer", NEW."attempt_id", NEW."created", NEW."extracted_text", NEW."id", NEW."modified", _pgh_attach_context(), NOW(), \'insert\', NEW."id"); RETURN NULL;', hash='c0813c5d90a6e945fd50db3c583d9e293a65c25d', operation='INSERT', pgid='pgtrigger_insert_insert_3d5fe', table='assignment_submission', when='AFTER')), diff --git a/core/apps/assignment/migrations/0002_remove_assignment_insert_insert_and_more.py b/core/apps/assignment/migrations/0002_remove_assignment_insert_insert_and_more.py deleted file mode 100644 index caaeb2f..0000000 --- a/core/apps/assignment/migrations/0002_remove_assignment_insert_insert_and_more.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:51 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('assignment', '0001_initial'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='delete_delete', - ), - migrations.RemoveField( - model_name='assignment', - name='published', - ), - migrations.RemoveField( - model_name='assignmentevent', - name='published', - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='fb53e6fd7f6e48244ad68fc0acb91b1ee6d995eb', operation='INSERT', pgid='pgtrigger_insert_insert_e3a8d', table='assignment_assignment', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."appeal_deadline_days" IS DISTINCT FROM (NEW."appeal_deadline_days") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."confirm_due_days" IS DISTINCT FROM (NEW."confirm_due_days") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."grade_due_days" IS DISTINCT FROM (NEW."grade_due_days") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='4a7ea5378235d3cc8ac57edbcd9dcb2c499906d4', operation='UPDATE', pgid='pgtrigger_update_update_629e4', table='assignment_assignment', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (OLD."appeal_deadline_days", OLD."audience", OLD."confirm_due_days", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."grade_due_days", OLD."honor_code_id", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."question_pool_id", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='a664227325ec3bff43b642bedc46c55314d73b8b', operation='DELETE', pgid='pgtrigger_delete_delete_1054f', table='assignment_assignment', when='AFTER')), - ), - ] diff --git a/core/apps/assignment/migrations/0003_remove_assignment_insert_insert_and_more.py b/core/apps/assignment/migrations/0003_remove_assignment_insert_insert_and_more.py deleted file mode 100644 index 99db262..0000000 --- a/core/apps/assignment/migrations/0003_remove_assignment_insert_insert_and_more.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:55 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('assignment', '0002_remove_assignment_insert_insert_and_more'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='delete_delete', - ), - migrations.AddField( - model_name='assignment', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - migrations.AddField( - model_name='assignmentevent', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."published", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='6b6bcae561b473c3ac90486f6db1be3c1aa8fc21', operation='INSERT', pgid='pgtrigger_insert_insert_e3a8d', table='assignment_assignment', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."appeal_deadline_days" IS DISTINCT FROM (NEW."appeal_deadline_days") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."confirm_due_days" IS DISTINCT FROM (NEW."confirm_due_days") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."grade_due_days" IS DISTINCT FROM (NEW."grade_due_days") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."published" IS DISTINCT FROM (NEW."published") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."published", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='b7a74042167cd7453f1abf532d6f66d3cb7496b0', operation='UPDATE', pgid='pgtrigger_update_update_629e4', table='assignment_assignment', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (OLD."appeal_deadline_days", OLD."audience", OLD."confirm_due_days", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."grade_due_days", OLD."honor_code_id", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."published", OLD."question_pool_id", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='1899dea81dcda8400f32ea559e2117dea2405f8c', operation='DELETE', pgid='pgtrigger_delete_delete_1054f', table='assignment_assignment', when='AFTER')), - ), - ] diff --git a/core/apps/assignment/migrations/0004_remove_solution_question_remove_solution_rubric_and_more.py b/core/apps/assignment/migrations/0004_remove_solution_question_remove_solution_rubric_and_more.py deleted file mode 100644 index e856db7..0000000 --- a/core/apps/assignment/migrations/0004_remove_solution_question_remove_solution_rubric_and_more.py +++ /dev/null @@ -1,82 +0,0 @@ -# Generated by Django 6.0.3 on 2026-03-06 09:25 - -import django.db.models.deletion -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('assignment', '0003_remove_assignment_insert_insert_and_more'), - ] - - operations = [ - migrations.RemoveField( - model_name='solution', - name='question', - ), - migrations.RemoveField( - model_name='solution', - name='rubric', - ), - migrations.RemoveField( - model_name='solutionevent', - name='pgh_obj', - ), - migrations.RemoveField( - model_name='solutionevent', - name='pgh_context', - ), - migrations.RemoveField( - model_name='solutionevent', - name='question', - ), - migrations.RemoveField( - model_name='solutionevent', - name='rubric', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='delete_delete', - ), - migrations.AddField( - model_name='assignment', - name='rubric', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='assignment.rubric', verbose_name='Rubric'), - preserve_default=False, - ), - migrations.AddField( - model_name='assignmentevent', - name='rubric', - field=models.ForeignKey(db_constraint=False, default=1, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='assignment.rubric', verbose_name='Rubric'), - preserve_default=False, - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "rubric_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."published", NEW."question_pool_id", NEW."rubric_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='1cd6f2ed24dcbe90b52e9632cc7b10b27259160e', operation='INSERT', pgid='pgtrigger_insert_insert_e3a8d', table='assignment_assignment', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."appeal_deadline_days" IS DISTINCT FROM (NEW."appeal_deadline_days") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."confirm_due_days" IS DISTINCT FROM (NEW."confirm_due_days") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."grade_due_days" IS DISTINCT FROM (NEW."grade_due_days") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."published" IS DISTINCT FROM (NEW."published") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."rubric_id" IS DISTINCT FROM (NEW."rubric_id") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "rubric_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."published", NEW."question_pool_id", NEW."rubric_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='9303206dde629794b00824d3a5367925f1fdd159', operation='UPDATE', pgid='pgtrigger_update_update_629e4', table='assignment_assignment', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "rubric_id", "thumbnail", "title", "verification_required") VALUES (OLD."appeal_deadline_days", OLD."audience", OLD."confirm_due_days", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."grade_due_days", OLD."honor_code_id", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."published", OLD."question_pool_id", OLD."rubric_id", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='da98a79a403fb93d0acea3abcf35aa374ae18916', operation='DELETE', pgid='pgtrigger_delete_delete_1054f', table='assignment_assignment', when='AFTER')), - ), - migrations.DeleteModel( - name='Solution', - ), - migrations.DeleteModel( - name='SolutionEvent', - ), - ] diff --git a/core/apps/assignment/migrations/0005_remove_assignment_insert_insert_and_more.py b/core/apps/assignment/migrations/0005_remove_assignment_insert_insert_and_more.py deleted file mode 100644 index d7973d7..0000000 --- a/core/apps/assignment/migrations/0005_remove_assignment_insert_insert_and_more.py +++ /dev/null @@ -1,81 +0,0 @@ -# Generated by Django 6.0.3 on 2026-03-06 10:50 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('assignment', '0004_remove_solution_question_remove_solution_rubric_and_more'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='assignment', - name='delete_delete', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='question', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='question', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='question', - name='delete_delete', - ), - migrations.RemoveField( - model_name='question', - name='sample_attachment', - ), - migrations.RemoveField( - model_name='questionevent', - name='sample_attachment', - ), - migrations.AddField( - model_name='assignment', - name='sample_attachment', - field=models.FileField(blank=True, max_length=255, null=True, upload_to='', verbose_name='Sample Attachment'), - ), - migrations.AddField( - model_name='assignmentevent', - name='sample_attachment', - field=models.FileField(blank=True, max_length=255, null=True, upload_to='', verbose_name='Sample Attachment'), - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "rubric_id", "sample_attachment", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."published", NEW."question_pool_id", NEW."rubric_id", NEW."sample_attachment", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='21ac5a1dbcdaca2a2780136fbf794154a596c62c', operation='INSERT', pgid='pgtrigger_insert_insert_e3a8d', table='assignment_assignment', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."appeal_deadline_days" IS DISTINCT FROM (NEW."appeal_deadline_days") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."confirm_due_days" IS DISTINCT FROM (NEW."confirm_due_days") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."grade_due_days" IS DISTINCT FROM (NEW."grade_due_days") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."published" IS DISTINCT FROM (NEW."published") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."rubric_id" IS DISTINCT FROM (NEW."rubric_id") OR OLD."sample_attachment" IS DISTINCT FROM (NEW."sample_attachment") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "rubric_id", "sample_attachment", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."published", NEW."question_pool_id", NEW."rubric_id", NEW."sample_attachment", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='31e50859d395027d1fe45fdebb78d526da55e3b8', operation='UPDATE', pgid='pgtrigger_update_update_629e4', table='assignment_assignment', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='assignment', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_assignmentevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "rubric_id", "sample_attachment", "thumbnail", "title", "verification_required") VALUES (OLD."appeal_deadline_days", OLD."audience", OLD."confirm_due_days", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."grade_due_days", OLD."honor_code_id", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."published", OLD."question_pool_id", OLD."rubric_id", OLD."sample_attachment", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='bd3967fe3520f4bc3eb34aa5628ee090a3020e92', operation='DELETE', pgid='pgtrigger_delete_delete_1054f', table='assignment_assignment', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='question', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_questionevent" ("attachment_file_count", "attachment_file_types", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "plagiarism_threshold", "pool_id", "question", "supplement") VALUES (NEW."attachment_file_count", NEW."attachment_file_types", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."plagiarism_threshold", NEW."pool_id", NEW."question", NEW."supplement"); RETURN NULL;', hash='ac65eeb67c670d74ad31e29e2cdec60661b4dedf', operation='INSERT', pgid='pgtrigger_insert_insert_ec4a4', table='assignment_question', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='question', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD.* IS DISTINCT FROM NEW.*)', func='INSERT INTO "assignment_questionevent" ("attachment_file_count", "attachment_file_types", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "plagiarism_threshold", "pool_id", "question", "supplement") VALUES (NEW."attachment_file_count", NEW."attachment_file_types", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."plagiarism_threshold", NEW."pool_id", NEW."question", NEW."supplement"); RETURN NULL;', hash='eff17638cb2cc8f38a72a5929750b25e7ab7272f', operation='UPDATE', pgid='pgtrigger_update_update_6ef6c', table='assignment_question', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='question', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "assignment_questionevent" ("attachment_file_count", "attachment_file_types", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "plagiarism_threshold", "pool_id", "question", "supplement") VALUES (OLD."attachment_file_count", OLD."attachment_file_types", OLD."id", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."plagiarism_threshold", OLD."pool_id", OLD."question", OLD."supplement"); RETURN NULL;', hash='3ec3e58da7ff73b07683a37d8a7c7348328ba765', operation='DELETE', pgid='pgtrigger_delete_delete_dc36d', table='assignment_question', when='AFTER')), - ), - ] diff --git a/core/apps/assistant/migrations/0001_initial.py b/core/apps/assistant/migrations/0001_initial.py index 108fd18..f7c6aad 100644 --- a/core/apps/assistant/migrations/0001_initial.py +++ b/core/apps/assistant/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import django.db.models.deletion import pgtrigger.compiler diff --git a/core/apps/competency/migrations/0001_initial.py b/core/apps/competency/migrations/0001_initial.py index ded1752..ab34855 100644 --- a/core/apps/competency/migrations/0001_initial.py +++ b/core/apps/competency/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import django.contrib.postgres.fields import django.db.models.deletion diff --git a/core/apps/content/migrations/0001_initial.py b/core/apps/content/migrations/0001_initial.py index 53f848e..940bebc 100644 --- a/core/apps/content/migrations/0001_initial.py +++ b/core/apps/content/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import apps.common.util import apps.content.models @@ -35,7 +35,7 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('thumbnail', models.ImageField(upload_to='', verbose_name='Thumbnail')), ('format', models.CharField(choices=[('video', 'Video'), ('short', 'Short'), ('ebook', 'Ebook'), ('html', 'HTML'), ('pdf', 'PDF'), ('live', 'Live')], max_length=30, verbose_name='Format')), ('duration', models.DurationField(verbose_name='Duration')), @@ -66,7 +66,7 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('thumbnail', models.ImageField(upload_to='', verbose_name='Thumbnail')), ('format', models.CharField(choices=[('video', 'Video'), ('short', 'Short'), ('ebook', 'Ebook'), ('html', 'HTML'), ('pdf', 'PDF'), ('live', 'Live')], max_length=30, verbose_name='Format')), ('duration', models.DurationField(verbose_name='Duration')), diff --git a/core/apps/content/migrations/0002_remove_media_insert_insert_and_more.py b/core/apps/content/migrations/0002_remove_media_insert_insert_and_more.py deleted file mode 100644 index 0a63742..0000000 --- a/core/apps/content/migrations/0002_remove_media_insert_insert_and_more.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:51 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('content', '0001_initial'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='media', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='media', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='media', - name='delete_delete', - ), - migrations.RemoveField( - model_name='media', - name='published', - ), - migrations.RemoveField( - model_name='mediaevent', - name='published', - ), - pgtrigger.migrations.AddTrigger( - model_name='media', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "content_mediaevent" ("audience", "channel", "created", "description", "duration", "featured", "format", "id", "license", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "thumbnail", "title", "url", "verification_required") VALUES (NEW."audience", NEW."channel", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."license", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."thumbnail", NEW."title", NEW."url", NEW."verification_required"); RETURN NULL;', hash='73e9da282a56fbefb7cc0f03e726580575df7c79', operation='INSERT', pgid='pgtrigger_insert_insert_fc9e0', table='content_media', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='media', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."channel" IS DISTINCT FROM (NEW."channel") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."license" IS DISTINCT FROM (NEW."license") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."url" IS DISTINCT FROM (NEW."url") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "content_mediaevent" ("audience", "channel", "created", "description", "duration", "featured", "format", "id", "license", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "thumbnail", "title", "url", "verification_required") VALUES (NEW."audience", NEW."channel", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."license", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."thumbnail", NEW."title", NEW."url", NEW."verification_required"); RETURN NULL;', hash='65c05b9f07d97cdc2795f6a8778290195fb1b600', operation='UPDATE', pgid='pgtrigger_update_update_56c38', table='content_media', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='media', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "content_mediaevent" ("audience", "channel", "created", "description", "duration", "featured", "format", "id", "license", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "thumbnail", "title", "url", "verification_required") VALUES (OLD."audience", OLD."channel", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."id", OLD."license", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."thumbnail", OLD."title", OLD."url", OLD."verification_required"); RETURN NULL;', hash='21df30024e8832ad1cd19ece895e1ad369ada6a1', operation='DELETE', pgid='pgtrigger_delete_delete_5e45c', table='content_media', when='AFTER')), - ), - ] diff --git a/core/apps/content/migrations/0003_remove_media_insert_insert_and_more.py b/core/apps/content/migrations/0003_remove_media_insert_insert_and_more.py deleted file mode 100644 index 97c2ca5..0000000 --- a/core/apps/content/migrations/0003_remove_media_insert_insert_and_more.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:55 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('content', '0002_remove_media_insert_insert_and_more'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='media', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='media', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='media', - name='delete_delete', - ), - migrations.AddField( - model_name='media', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - migrations.AddField( - model_name='mediaevent', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - pgtrigger.migrations.AddTrigger( - model_name='media', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "content_mediaevent" ("audience", "channel", "created", "description", "duration", "featured", "format", "id", "license", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "thumbnail", "title", "url", "verification_required") VALUES (NEW."audience", NEW."channel", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."license", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."published", NEW."thumbnail", NEW."title", NEW."url", NEW."verification_required"); RETURN NULL;', hash='d0e286c74db1799bd9047f85b12b8606d5210f67', operation='INSERT', pgid='pgtrigger_insert_insert_fc9e0', table='content_media', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='media', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."channel" IS DISTINCT FROM (NEW."channel") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."license" IS DISTINCT FROM (NEW."license") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."published" IS DISTINCT FROM (NEW."published") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."url" IS DISTINCT FROM (NEW."url") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "content_mediaevent" ("audience", "channel", "created", "description", "duration", "featured", "format", "id", "license", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "thumbnail", "title", "url", "verification_required") VALUES (NEW."audience", NEW."channel", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."license", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."published", NEW."thumbnail", NEW."title", NEW."url", NEW."verification_required"); RETURN NULL;', hash='e4ffd628e26761e6f9a30e02d092cc7724f546e4', operation='UPDATE', pgid='pgtrigger_update_update_56c38', table='content_media', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='media', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "content_mediaevent" ("audience", "channel", "created", "description", "duration", "featured", "format", "id", "license", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "thumbnail", "title", "url", "verification_required") VALUES (OLD."audience", OLD."channel", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."id", OLD."license", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."published", OLD."thumbnail", OLD."title", OLD."url", OLD."verification_required"); RETURN NULL;', hash='1b4b164ddb12ef8493ece66bcfd2f785e22fb9cb', operation='DELETE', pgid='pgtrigger_delete_delete_5e45c', table='content_media', when='AFTER')), - ), - ] diff --git a/core/apps/course/migrations/0001_initial.py b/core/apps/course/migrations/0001_initial.py index 2cd78a5..69dfc2d 100644 --- a/core/apps/course/migrations/0001_initial.py +++ b/core/apps/course/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import apps.common.util import django.contrib.postgres.fields @@ -86,13 +86,13 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('objective', models.TextField(blank=True, default='', verbose_name='Objective')), ('preview_url', models.URLField(blank=True, null=True, verbose_name='Preview URL')), ('effort_hours', models.PositiveSmallIntegerField(verbose_name='Effort Hours')), ('level', models.CharField(choices=[('beginner', 'Beginner'), ('intermediate', 'Intermediate'), ('advanced', 'Advanced'), ('common', 'Common')], max_length=20, verbose_name='Level')), - ('faq', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='operation.faq', verbose_name='FAQ')), - ('honor_code', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='operation.honorcode', verbose_name='Honor Code')), + ('faq', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='operation.faq', verbose_name='FAQ')), + ('honor_code', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='operation.honorcode', verbose_name='Honor Code')), ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner')), ('message_preset', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='course.messagepreset', verbose_name='Message Preset')), ], @@ -497,13 +497,13 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('objective', models.TextField(blank=True, default='', verbose_name='Objective')), ('preview_url', models.URLField(blank=True, null=True, verbose_name='Preview URL')), ('effort_hours', models.PositiveSmallIntegerField(verbose_name='Effort Hours')), ('level', models.CharField(choices=[('beginner', 'Beginner'), ('intermediate', 'Intermediate'), ('advanced', 'Advanced'), ('common', 'Common')], max_length=20, verbose_name='Level')), - ('faq', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='operation.faq', verbose_name='FAQ')), - ('honor_code', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='operation.honorcode', verbose_name='Honor Code')), + ('faq', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='operation.faq', verbose_name='FAQ')), + ('honor_code', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='operation.honorcode', verbose_name='Honor Code')), ('owner', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Owner')), ('pgh_context', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pghistory.context')), ('pgh_obj', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='course.course')), diff --git a/core/apps/course/migrations/0002_alter_course_faq_alter_course_honor_code_and_more.py b/core/apps/course/migrations/0002_alter_course_faq_alter_course_honor_code_and_more.py deleted file mode 100644 index e577fd3..0000000 --- a/core/apps/course/migrations/0002_alter_course_faq_alter_course_honor_code_and_more.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:17 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('course', '0001_initial'), - ('operation', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='course', - name='faq', - field=models.ForeignKey(default='', on_delete=django.db.models.deletion.CASCADE, to='operation.faq', verbose_name='FAQ'), - preserve_default=False, - ), - migrations.AlterField( - model_name='course', - name='honor_code', - field=models.ForeignKey(default='', on_delete=django.db.models.deletion.CASCADE, to='operation.honorcode', verbose_name='Honor Code'), - preserve_default=False, - ), - migrations.AlterField( - model_name='courseevent', - name='faq', - field=models.ForeignKey(db_constraint=False, default='', on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='operation.faq', verbose_name='FAQ'), - preserve_default=False, - ), - migrations.AlterField( - model_name='courseevent', - name='honor_code', - field=models.ForeignKey(db_constraint=False, default='', on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='operation.honorcode', verbose_name='Honor Code'), - preserve_default=False, - ), - ] diff --git a/core/apps/course/migrations/0003_remove_course_insert_insert_and_more.py b/core/apps/course/migrations/0003_remove_course_insert_insert_and_more.py deleted file mode 100644 index cfb0141..0000000 --- a/core/apps/course/migrations/0003_remove_course_insert_insert_and_more.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:51 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('course', '0002_alter_course_faq_alter_course_honor_code_and_more'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='course', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='course', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='course', - name='delete_delete', - ), - migrations.RemoveField( - model_name='course', - name='published', - ), - migrations.RemoveField( - model_name='courseevent', - name='published', - ), - pgtrigger.migrations.AddTrigger( - model_name='course', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "course_courseevent" ("audience", "created", "description", "duration", "effort_hours", "faq_id", "featured", "format", "honor_code_id", "id", "level", "max_attempts", "message_preset_id", "modified", "objective", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "preview_url", "thumbnail", "title", "verification_required") VALUES (NEW."audience", NEW."created", NEW."description", NEW."duration", NEW."effort_hours", NEW."faq_id", NEW."featured", NEW."format", NEW."honor_code_id", NEW."id", NEW."level", NEW."max_attempts", NEW."message_preset_id", NEW."modified", NEW."objective", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."preview_url", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='9fe85073007a09e5148ab6b30524dcb7503f4153', operation='INSERT', pgid='pgtrigger_insert_insert_b0bd1', table='course_course', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='course', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."effort_hours" IS DISTINCT FROM (NEW."effort_hours") OR OLD."faq_id" IS DISTINCT FROM (NEW."faq_id") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."level" IS DISTINCT FROM (NEW."level") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."message_preset_id" IS DISTINCT FROM (NEW."message_preset_id") OR OLD."objective" IS DISTINCT FROM (NEW."objective") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."preview_url" IS DISTINCT FROM (NEW."preview_url") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "course_courseevent" ("audience", "created", "description", "duration", "effort_hours", "faq_id", "featured", "format", "honor_code_id", "id", "level", "max_attempts", "message_preset_id", "modified", "objective", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "preview_url", "thumbnail", "title", "verification_required") VALUES (NEW."audience", NEW."created", NEW."description", NEW."duration", NEW."effort_hours", NEW."faq_id", NEW."featured", NEW."format", NEW."honor_code_id", NEW."id", NEW."level", NEW."max_attempts", NEW."message_preset_id", NEW."modified", NEW."objective", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."preview_url", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='a373dee0bf482e9ed118f5213782c7a604677733', operation='UPDATE', pgid='pgtrigger_update_update_8ff8f', table='course_course', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='course', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "course_courseevent" ("audience", "created", "description", "duration", "effort_hours", "faq_id", "featured", "format", "honor_code_id", "id", "level", "max_attempts", "message_preset_id", "modified", "objective", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "preview_url", "thumbnail", "title", "verification_required") VALUES (OLD."audience", OLD."created", OLD."description", OLD."duration", OLD."effort_hours", OLD."faq_id", OLD."featured", OLD."format", OLD."honor_code_id", OLD."id", OLD."level", OLD."max_attempts", OLD."message_preset_id", OLD."modified", OLD."objective", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."preview_url", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='14a8249c4e43a125c597698d1a45c61d71bab83e', operation='DELETE', pgid='pgtrigger_delete_delete_36642', table='course_course', when='AFTER')), - ), - ] diff --git a/core/apps/course/migrations/0004_remove_course_insert_insert_and_more.py b/core/apps/course/migrations/0004_remove_course_insert_insert_and_more.py deleted file mode 100644 index 95e91fa..0000000 --- a/core/apps/course/migrations/0004_remove_course_insert_insert_and_more.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:55 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('course', '0003_remove_course_insert_insert_and_more'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='course', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='course', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='course', - name='delete_delete', - ), - migrations.AddField( - model_name='course', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - migrations.AddField( - model_name='courseevent', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - pgtrigger.migrations.AddTrigger( - model_name='course', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "course_courseevent" ("audience", "created", "description", "duration", "effort_hours", "faq_id", "featured", "format", "honor_code_id", "id", "level", "max_attempts", "message_preset_id", "modified", "objective", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "preview_url", "published", "thumbnail", "title", "verification_required") VALUES (NEW."audience", NEW."created", NEW."description", NEW."duration", NEW."effort_hours", NEW."faq_id", NEW."featured", NEW."format", NEW."honor_code_id", NEW."id", NEW."level", NEW."max_attempts", NEW."message_preset_id", NEW."modified", NEW."objective", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."preview_url", NEW."published", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='aabd0bb049bc09bd40f2f86de5d072499bf68196', operation='INSERT', pgid='pgtrigger_insert_insert_b0bd1', table='course_course', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='course', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."effort_hours" IS DISTINCT FROM (NEW."effort_hours") OR OLD."faq_id" IS DISTINCT FROM (NEW."faq_id") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."level" IS DISTINCT FROM (NEW."level") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."message_preset_id" IS DISTINCT FROM (NEW."message_preset_id") OR OLD."objective" IS DISTINCT FROM (NEW."objective") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."preview_url" IS DISTINCT FROM (NEW."preview_url") OR OLD."published" IS DISTINCT FROM (NEW."published") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "course_courseevent" ("audience", "created", "description", "duration", "effort_hours", "faq_id", "featured", "format", "honor_code_id", "id", "level", "max_attempts", "message_preset_id", "modified", "objective", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "preview_url", "published", "thumbnail", "title", "verification_required") VALUES (NEW."audience", NEW."created", NEW."description", NEW."duration", NEW."effort_hours", NEW."faq_id", NEW."featured", NEW."format", NEW."honor_code_id", NEW."id", NEW."level", NEW."max_attempts", NEW."message_preset_id", NEW."modified", NEW."objective", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."preview_url", NEW."published", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='f47ae042c6e7e7a10354192d997e81b5934e029b', operation='UPDATE', pgid='pgtrigger_update_update_8ff8f', table='course_course', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='course', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "course_courseevent" ("audience", "created", "description", "duration", "effort_hours", "faq_id", "featured", "format", "honor_code_id", "id", "level", "max_attempts", "message_preset_id", "modified", "objective", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "preview_url", "published", "thumbnail", "title", "verification_required") VALUES (OLD."audience", OLD."created", OLD."description", OLD."duration", OLD."effort_hours", OLD."faq_id", OLD."featured", OLD."format", OLD."honor_code_id", OLD."id", OLD."level", OLD."max_attempts", OLD."message_preset_id", OLD."modified", OLD."objective", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."preview_url", OLD."published", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='2d9743409281694ed38d213212c6c0d5697ce7bf', operation='DELETE', pgid='pgtrigger_delete_delete_36642', table='course_course', when='AFTER')), - ), - ] diff --git a/core/apps/discussion/migrations/0001_initial.py b/core/apps/discussion/migrations/0001_initial.py index c01ae88..e27d662 100644 --- a/core/apps/discussion/migrations/0001_initial.py +++ b/core/apps/discussion/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import apps.common.util import django.db.models.deletion @@ -36,7 +36,7 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('grade_due_days', models.PositiveSmallIntegerField(verbose_name='Grading Due Days')), ('appeal_deadline_days', models.PositiveSmallIntegerField(verbose_name='Appeal Deadline Days')), ('confirm_due_days', models.PositiveSmallIntegerField(verbose_name='Confirm Due Days')), @@ -257,7 +257,7 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('grade_due_days', models.PositiveSmallIntegerField(verbose_name='Grading Due Days')), ('appeal_deadline_days', models.PositiveSmallIntegerField(verbose_name='Appeal Deadline Days')), ('confirm_due_days', models.PositiveSmallIntegerField(verbose_name='Confirm Due Days')), diff --git a/core/apps/discussion/migrations/0002_remove_discussion_insert_insert_and_more.py b/core/apps/discussion/migrations/0002_remove_discussion_insert_insert_and_more.py deleted file mode 100644 index 32728eb..0000000 --- a/core/apps/discussion/migrations/0002_remove_discussion_insert_insert_and_more.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:51 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('discussion', '0001_initial'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='discussion', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='discussion', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='discussion', - name='delete_delete', - ), - migrations.RemoveField( - model_name='discussion', - name='published', - ), - migrations.RemoveField( - model_name='discussionevent', - name='published', - ), - pgtrigger.migrations.AddTrigger( - model_name='discussion', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "discussion_discussionevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='eda4996ce9a2223ce41c4ce4bef98a394ea20cf3', operation='INSERT', pgid='pgtrigger_insert_insert_f0c80', table='discussion_discussion', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='discussion', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."appeal_deadline_days" IS DISTINCT FROM (NEW."appeal_deadline_days") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."confirm_due_days" IS DISTINCT FROM (NEW."confirm_due_days") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."grade_due_days" IS DISTINCT FROM (NEW."grade_due_days") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "discussion_discussionevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='f61e4720e2724a926b6937271c52263662d026c2', operation='UPDATE', pgid='pgtrigger_update_update_a3185', table='discussion_discussion', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='discussion', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "discussion_discussionevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (OLD."appeal_deadline_days", OLD."audience", OLD."confirm_due_days", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."grade_due_days", OLD."honor_code_id", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."question_pool_id", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='3bf94773eace204f27d2e22e6ef131dc5d523930', operation='DELETE', pgid='pgtrigger_delete_delete_a1d65', table='discussion_discussion', when='AFTER')), - ), - ] diff --git a/core/apps/discussion/migrations/0003_remove_discussion_insert_insert_and_more.py b/core/apps/discussion/migrations/0003_remove_discussion_insert_insert_and_more.py deleted file mode 100644 index 898cc81..0000000 --- a/core/apps/discussion/migrations/0003_remove_discussion_insert_insert_and_more.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:55 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('discussion', '0002_remove_discussion_insert_insert_and_more'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='discussion', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='discussion', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='discussion', - name='delete_delete', - ), - migrations.AddField( - model_name='discussion', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - migrations.AddField( - model_name='discussionevent', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - pgtrigger.migrations.AddTrigger( - model_name='discussion', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "discussion_discussionevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."published", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='2b6c62087495152dc9dbe739b3e046586395d219', operation='INSERT', pgid='pgtrigger_insert_insert_f0c80', table='discussion_discussion', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='discussion', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."appeal_deadline_days" IS DISTINCT FROM (NEW."appeal_deadline_days") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."confirm_due_days" IS DISTINCT FROM (NEW."confirm_due_days") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."grade_due_days" IS DISTINCT FROM (NEW."grade_due_days") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."published" IS DISTINCT FROM (NEW."published") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "discussion_discussionevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."published", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='fd3dba4e8c7ab982d57cc6422b147fd013acb029', operation='UPDATE', pgid='pgtrigger_update_update_a3185', table='discussion_discussion', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='discussion', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "discussion_discussionevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (OLD."appeal_deadline_days", OLD."audience", OLD."confirm_due_days", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."grade_due_days", OLD."honor_code_id", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."published", OLD."question_pool_id", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='c2cd7907ec6a057ca116329cfcf04070420a4e8a', operation='DELETE', pgid='pgtrigger_delete_delete_a1d65', table='discussion_discussion', when='AFTER')), - ), - ] diff --git a/core/apps/exam/migrations/0001_initial.py b/core/apps/exam/migrations/0001_initial.py index 4835758..fde618b 100644 --- a/core/apps/exam/migrations/0001_initial.py +++ b/core/apps/exam/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import apps.common.util import django.contrib.postgres.fields @@ -52,7 +52,7 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('grade_due_days', models.PositiveSmallIntegerField(verbose_name='Grading Due Days')), ('appeal_deadline_days', models.PositiveSmallIntegerField(verbose_name='Appeal Deadline Days')), ('confirm_due_days', models.PositiveSmallIntegerField(verbose_name='Confirm Due Days')), @@ -219,7 +219,7 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('grade_due_days', models.PositiveSmallIntegerField(verbose_name='Grading Due Days')), ('appeal_deadline_days', models.PositiveSmallIntegerField(verbose_name='Appeal Deadline Days')), ('confirm_due_days', models.PositiveSmallIntegerField(verbose_name='Confirm Due Days')), diff --git a/core/apps/exam/migrations/0002_remove_exam_insert_insert_remove_exam_update_update_and_more.py b/core/apps/exam/migrations/0002_remove_exam_insert_insert_remove_exam_update_update_and_more.py deleted file mode 100644 index dbc9003..0000000 --- a/core/apps/exam/migrations/0002_remove_exam_insert_insert_remove_exam_update_update_and_more.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:51 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('exam', '0001_initial'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='exam', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='exam', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='exam', - name='delete_delete', - ), - migrations.RemoveField( - model_name='exam', - name='published', - ), - migrations.RemoveField( - model_name='examevent', - name='published', - ), - pgtrigger.migrations.AddTrigger( - model_name='exam', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "exam_examevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='1375a0d2e60696035a475bd21b8a697d49f2ddb6', operation='INSERT', pgid='pgtrigger_insert_insert_8cb8f', table='exam_exam', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='exam', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."appeal_deadline_days" IS DISTINCT FROM (NEW."appeal_deadline_days") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."confirm_due_days" IS DISTINCT FROM (NEW."confirm_due_days") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."grade_due_days" IS DISTINCT FROM (NEW."grade_due_days") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "exam_examevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='3c2d835341815a8e0f6d7033915aad21bb3fce28', operation='UPDATE', pgid='pgtrigger_update_update_e5bfc', table='exam_exam', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='exam', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "exam_examevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (OLD."appeal_deadline_days", OLD."audience", OLD."confirm_due_days", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."grade_due_days", OLD."honor_code_id", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."question_pool_id", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='46f54dde7462afe5c8c2330c7066c39905624df1', operation='DELETE', pgid='pgtrigger_delete_delete_2a8e1', table='exam_exam', when='AFTER')), - ), - ] diff --git a/core/apps/exam/migrations/0003_remove_exam_insert_insert_remove_exam_update_update_and_more.py b/core/apps/exam/migrations/0003_remove_exam_insert_insert_remove_exam_update_update_and_more.py deleted file mode 100644 index c72873e..0000000 --- a/core/apps/exam/migrations/0003_remove_exam_insert_insert_remove_exam_update_update_and_more.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:55 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('exam', '0002_remove_exam_insert_insert_remove_exam_update_update_and_more'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='exam', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='exam', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='exam', - name='delete_delete', - ), - migrations.AddField( - model_name='exam', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - migrations.AddField( - model_name='examevent', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - pgtrigger.migrations.AddTrigger( - model_name='exam', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "exam_examevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."published", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='5adc4b0676721b87449e2ea3180e614bcb1a439e', operation='INSERT', pgid='pgtrigger_insert_insert_8cb8f', table='exam_exam', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='exam', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."appeal_deadline_days" IS DISTINCT FROM (NEW."appeal_deadline_days") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."confirm_due_days" IS DISTINCT FROM (NEW."confirm_due_days") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."grade_due_days" IS DISTINCT FROM (NEW."grade_due_days") OR OLD."honor_code_id" IS DISTINCT FROM (NEW."honor_code_id") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."published" IS DISTINCT FROM (NEW."published") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "exam_examevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."appeal_deadline_days", NEW."audience", NEW."confirm_due_days", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."grade_due_days", NEW."honor_code_id", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."published", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='5be4f60c66f843a00c156abb20bece952a006ab6', operation='UPDATE', pgid='pgtrigger_update_update_e5bfc', table='exam_exam', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='exam', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "exam_examevent" ("appeal_deadline_days", "audience", "confirm_due_days", "created", "description", "duration", "featured", "format", "grade_due_days", "honor_code_id", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (OLD."appeal_deadline_days", OLD."audience", OLD."confirm_due_days", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."grade_due_days", OLD."honor_code_id", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."published", OLD."question_pool_id", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='c2062d3017089850968f3ea37831838ad566659e', operation='DELETE', pgid='pgtrigger_delete_delete_2a8e1', table='exam_exam', when='AFTER')), - ), - ] diff --git a/core/apps/learning/migrations/0001_initial.py b/core/apps/learning/migrations/0001_initial.py index 6f813bc..23c4756 100644 --- a/core/apps/learning/migrations/0001_initial.py +++ b/core/apps/learning/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import django.db.models.deletion import django.utils.timezone diff --git a/core/apps/operation/migrations/0001_initial.py b/core/apps/operation/migrations/0001_initial.py index 041e5f8..f10aed9 100644 --- a/core/apps/operation/migrations/0001_initial.py +++ b/core/apps/operation/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import django.contrib.postgres.fields import django.db.models.deletion @@ -288,7 +288,7 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='Created')), ('modified', models.DateTimeField(auto_now=True, db_index=True, verbose_name='Modified')), - ('title', models.CharField(max_length=255, verbose_name='Title')), + ('title', models.CharField(max_length=255, unique=True, verbose_name='Title')), ('code', models.TextField(verbose_name='Code')), ], options={ diff --git a/core/apps/operation/migrations/0002_alter_faq_name.py b/core/apps/operation/migrations/0002_alter_faq_name.py deleted file mode 100644 index 0cd14dc..0000000 --- a/core/apps/operation/migrations/0002_alter_faq_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 6.0.3 on 2026-03-04 01:48 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('operation', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='faq', - name='name', - field=models.CharField(max_length=255, verbose_name='Name'), - ), - ] diff --git a/core/apps/operation/migrations/0003_alter_faq_name_alter_honorcode_title.py b/core/apps/operation/migrations/0003_alter_faq_name_alter_honorcode_title.py deleted file mode 100644 index 9797b40..0000000 --- a/core/apps/operation/migrations/0003_alter_faq_name_alter_honorcode_title.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 6.0.3 on 2026-03-06 09:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('operation', '0002_alter_faq_name'), - ] - - operations = [ - migrations.AlterField( - model_name='faq', - name='name', - field=models.CharField(max_length=255, unique=True, verbose_name='Name'), - ), - migrations.AlterField( - model_name='honorcode', - name='title', - field=models.CharField(max_length=255, unique=True, verbose_name='Title'), - ), - ] diff --git a/core/apps/partner/migrations/0001_initial.py b/core/apps/partner/migrations/0001_initial.py index 8ebbb75..13733a4 100644 --- a/core/apps/partner/migrations/0001_initial.py +++ b/core/apps/partner/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import django.db.models.deletion import pgtrigger.compiler diff --git a/core/apps/quiz/migrations/0001_initial.py b/core/apps/quiz/migrations/0001_initial.py index 546c1c6..b4840cd 100644 --- a/core/apps/quiz/migrations/0001_initial.py +++ b/core/apps/quiz/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import apps.common.util import django.contrib.postgres.fields @@ -177,7 +177,7 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner')), ('question_pool', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='quiz.questionpool', verbose_name='Question Pool')), ], @@ -231,7 +231,7 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('owner', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Owner')), ('pgh_context', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pghistory.context')), ('pgh_obj', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='quiz.quiz')), diff --git a/core/apps/quiz/migrations/0002_remove_quiz_insert_insert_remove_quiz_update_update_and_more.py b/core/apps/quiz/migrations/0002_remove_quiz_insert_insert_remove_quiz_update_update_and_more.py deleted file mode 100644 index fdd2123..0000000 --- a/core/apps/quiz/migrations/0002_remove_quiz_insert_insert_remove_quiz_update_update_and_more.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:51 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('quiz', '0001_initial'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='quiz', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='quiz', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='quiz', - name='delete_delete', - ), - migrations.RemoveField( - model_name='quiz', - name='published', - ), - migrations.RemoveField( - model_name='quizevent', - name='published', - ), - pgtrigger.migrations.AddTrigger( - model_name='quiz', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "quiz_quizevent" ("audience", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."audience", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='e1abb8363684986fd0f9709e30efb82a1fb7c3ef', operation='INSERT', pgid='pgtrigger_insert_insert_433ea', table='quiz_quiz', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='quiz', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "quiz_quizevent" ("audience", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."audience", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='d5cd748c9f8c3af9c6f9b4bab91b646ffbdfa00b', operation='UPDATE', pgid='pgtrigger_update_update_44448', table='quiz_quiz', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='quiz', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "quiz_quizevent" ("audience", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (OLD."audience", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."question_pool_id", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='d6ad87bf29cff4a1441c2118693294ec1382f6fa', operation='DELETE', pgid='pgtrigger_delete_delete_56331', table='quiz_quiz', when='AFTER')), - ), - ] diff --git a/core/apps/quiz/migrations/0003_remove_quiz_insert_insert_remove_quiz_update_update_and_more.py b/core/apps/quiz/migrations/0003_remove_quiz_insert_insert_remove_quiz_update_update_and_more.py deleted file mode 100644 index 9a90e9d..0000000 --- a/core/apps/quiz/migrations/0003_remove_quiz_insert_insert_remove_quiz_update_update_and_more.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:55 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('quiz', '0002_remove_quiz_insert_insert_remove_quiz_update_update_and_more'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='quiz', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='quiz', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='quiz', - name='delete_delete', - ), - migrations.AddField( - model_name='quiz', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - migrations.AddField( - model_name='quizevent', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - pgtrigger.migrations.AddTrigger( - model_name='quiz', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "quiz_quizevent" ("audience", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."audience", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."published", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='2b8c28133d8d66edbcfea1937024e91f237d610a', operation='INSERT', pgid='pgtrigger_insert_insert_433ea', table='quiz_quiz', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='quiz', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."published" IS DISTINCT FROM (NEW."published") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "quiz_quizevent" ("audience", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (NEW."audience", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."published", NEW."question_pool_id", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='ec946ee67a9903dff656301a748f8a0d542ef595', operation='UPDATE', pgid='pgtrigger_update_update_44448', table='quiz_quiz', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='quiz', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "quiz_quizevent" ("audience", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "thumbnail", "title", "verification_required") VALUES (OLD."audience", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."published", OLD."question_pool_id", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='169f80cc2078c6339c393b401074367fc4ec4cb3', operation='DELETE', pgid='pgtrigger_delete_delete_56331', table='quiz_quiz', when='AFTER')), - ), - ] diff --git a/core/apps/sso/migrations/0001_initial.py b/core/apps/sso/migrations/0001_initial.py index 701da85..80132a4 100644 --- a/core/apps/sso/migrations/0001_initial.py +++ b/core/apps/sso/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import apps.common.util import django.db.models.deletion diff --git a/core/apps/store/migrations/0001_initial.py b/core/apps/store/migrations/0001_initial.py index 193a965..a9f3cfe 100644 --- a/core/apps/store/migrations/0001_initial.py +++ b/core/apps/store/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import django.db.models.deletion import django.utils.timezone diff --git a/core/apps/studio/admin.py b/core/apps/studio/admin.py index c3f2727..66e280b 100644 --- a/core/apps/studio/admin.py +++ b/core/apps/studio/admin.py @@ -2,23 +2,23 @@ from django.utils.translation import gettext_lazy as _ from apps.common.admin import ModelAdmin, ReadOnlyHiddenModelAdmin, ReadOnlyTabularInline -from apps.studio.models import Draft +from apps.studio.models import Editing -@admin.register(Draft) -class DraftAdmin(ModelAdmin): - class DraftEventInline(ReadOnlyTabularInline[Draft.pgh_event_model]): - model = Draft.pgh_event_model - verbose_name = _("Draft History") - verbose_name_plural = _("Draft Histories") +@admin.register(Editing) +class EditingAdmin(ModelAdmin): + class EditingEventInline(ReadOnlyTabularInline[Editing.pgh_event_model]): + model = Editing.pgh_event_model + verbose_name = _("Editing History") + verbose_name_plural = _("Editing Histories") ordering = ("-edited",) def get_queryset(self, request): return super().get_queryset(request).select_related("pgh_context", "author", "content_type") - inlines = (DraftEventInline,) + inlines = (EditingEventInline,) -@admin.register(Draft.pgh_event_model) -class DraftEventAdmin(ReadOnlyHiddenModelAdmin[Draft.pgh_event_model]): +@admin.register(Editing.pgh_event_model) +class EditingEventAdmin(ReadOnlyHiddenModelAdmin[Editing.pgh_event_model]): pass diff --git a/core/apps/studio/api/v1/__init__.py b/core/apps/studio/api/v1/__init__.py index 1ff67d8..1b4e431 100644 --- a/core/apps/studio/api/v1/__init__.py +++ b/core/apps/studio/api/v1/__init__.py @@ -29,7 +29,7 @@ from apps.studio.api.v1.quiz import router as quiz_router from apps.studio.api.v1.survey import router as survey_router from apps.studio.decorator import editor_required -from apps.studio.models import Draft +from apps.studio.models import Editing from apps.survey.models import Survey router = Router(by_alias=True) @@ -74,7 +74,7 @@ async def content( if kind: table_names = [(t, a, m) for t, a, m in table_names if m == kind] - draft_table = Draft._meta.db_table + editing_table = Editing._meta.db_table union_sql = " UNION ALL ".join( f"SELECT id, title, thumbnail, created, modified, published, '{app_label}' AS app_label, '{model_name}' AS model" + f" FROM {table} WHERE owner_id = %s" @@ -85,13 +85,13 @@ async def content( count_sql = f""" SELECT COUNT(*) FROM ({union_sql}) u - LEFT OUTER JOIN {draft_table} d ON d.content_id = u.id AND d.author_id = %s + LEFT OUTER JOIN {editing_table} d ON d.content_id = u.id AND d.author_id = %s """ sql = f""" SELECT u.id, u.title, u.thumbnail, u.created, u.modified, u.published, u.app_label, u.model, d.edited FROM ({union_sql}) u - LEFT OUTER JOIN {draft_table} d ON d.content_id = u.id AND d.author_id = %s + LEFT OUTER JOIN {editing_table} d ON d.content_id = u.id AND d.author_id = %s ORDER BY COALESCE(d.edited, u.modified) DESC, u.modified DESC LIMIT %s OFFSET %s """ diff --git a/core/apps/studio/api/v1/assignment.py b/core/apps/studio/api/v1/assignment.py index 62a06cc..8d900fd 100644 --- a/core/apps/studio/api/v1/assignment.py +++ b/core/apps/studio/api/v1/assignment.py @@ -27,7 +27,7 @@ Schema, ) from apps.common.util import HttpRequest, ModeChoices -from apps.studio.decorator import editor_required, track_draft +from apps.studio.decorator import editor_required, track_editing class AssignmentQuestionSaveSpec(Schema): @@ -126,7 +126,7 @@ async def get_assignment(request: HttpRequest, id: str): @router.post("/assignment", response=str) @editor_required() -@track_draft(Assignment) +@track_editing(Assignment) async def save_assignment( request: HttpRequest, data: AssignmentSaveSpec, @@ -177,7 +177,7 @@ def create_new(): @router.delete("/assignment/{id}") @editor_required() -@track_draft(Assignment, id_field="id") +@track_editing(Assignment, id_field="id") async def delete_assignment(request: HttpRequest, id: str): if await Attempt.objects.filter(assignment_id=id).exclude(mode=ModeChoices.PREVIEW).aexists(): raise ValueError(ErrorCode.ATTEMPT_EXISTS) @@ -197,7 +197,7 @@ async def get_assignment_questions(request: HttpRequest, id: str): @router.post("/assignment/{id}/question", response=list[int]) @editor_required() -@track_draft(Assignment, id_field="id") +@track_editing(Assignment, id_field="id") async def save_assignment_questions( request: HttpRequest, id: str, @@ -243,7 +243,7 @@ async def save_assignment_questions( @router.delete("/assignment/{id}/question/{question_id}") @editor_required() -@track_draft(Assignment, id_field="id") +@track_editing(Assignment, id_field="id") async def delete_assignment_quesion(request: HttpRequest, id: str, question_id: int): if await Attempt.objects.filter( assignment_id=id, question=question_id, assignment__owner_id=request.auth @@ -266,7 +266,7 @@ async def get_assignment_rubric(request: HttpRequest, id: str): @router.post("/assignment/{id}/rubric") @editor_required() -@track_draft(Assignment, id_field="id") +@track_editing(Assignment, id_field="id") async def save_assignment_rubric(request: HttpRequest, id: str, data: RootModel[list[RubricCriterionSpec]]): assignment = await aget_object_or_404(Assignment, id=id, owner_id=request.auth) diff --git a/core/apps/studio/api/v1/course.py b/core/apps/studio/api/v1/course.py index 1dc80cc..9b11343 100644 --- a/core/apps/studio/api/v1/course.py +++ b/core/apps/studio/api/v1/course.py @@ -36,7 +36,7 @@ Lesson, LessonMedia, ) -from apps.studio.decorator import editor_required, track_draft +from apps.studio.decorator import editor_required, track_editing log = logging.getLogger(__name__) @@ -198,7 +198,7 @@ async def get_course(request: HttpRequest, id: str): @router.post("/course", response=str) @editor_required() -@track_draft(Course) +@track_editing(Course) async def save_course( request: HttpRequest, data: CourseSaveSpec, @@ -241,7 +241,7 @@ def create_new(): @router.delete("/course/{id}") @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def delete_course(request: HttpRequest, id: str): if await Engagement.objects.filter(course_id=id).exclude(mode=ModeChoices.PREVIEW).aexists(): raise ValueError(ErrorCode.ATTEMPT_EXISTS) @@ -254,7 +254,7 @@ class CourseSurveySaveSpec(CourseSurveySpec): @router.post("/course/{id}/survey", response=list[int]) @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def save_course_surveys(request: HttpRequest, id: str, data: RootModel[list[CourseSurveySaveSpec]]): course = await aget_object_or_404(Course, id=id, owner_id=request.auth) course_surveys = await course.course_surveys.abulk_create( @@ -271,7 +271,7 @@ async def save_course_surveys(request: HttpRequest, id: str, data: RootModel[lis @router.delete("/course/{id}/survey/{course_survey_id}") @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def remove_course_survey(request: HttpRequest, id: str, course_survey_id: int): count, _ = await CourseSurvey.objects.filter( course_id=id, id=course_survey_id, course__owner_id=request.auth @@ -286,7 +286,7 @@ class AssessmentSaveSpec(AssessmentSpec): @router.post("/course/{id}/assessment", response=list[int]) @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def save_course_assessments(request: HttpRequest, id: str, data: RootModel[list[AssessmentSaveSpec]]): course = await aget_object_or_404(Course, id=id, owner_id=request.auth) @@ -319,7 +319,7 @@ async def save_course_assessments(request: HttpRequest, id: str, data: RootModel @router.delete("/course/{id}/assessment/{assessment_id}") @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def remove_course_assessment(request: HttpRequest, id: str, assessment_id: int): count, _ = await Assessment.objects.filter(course_id=id, id=assessment_id, course__owner_id=request.auth).adelete() if count == 0: @@ -332,7 +332,7 @@ class LessonSaveSpec(LessonSpec): @router.post("/course/{id}/lesson", response=list[int]) @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def save_course_lessons(request: HttpRequest, id: str, data: RootModel[list[LessonSaveSpec]]): course = await aget_object_or_404(Course, id=id, owner_id=request.auth) lessons, lesson_medias = [], [] @@ -376,7 +376,7 @@ def save_lessons(): @router.delete("/course/{id}/lesson/{lesson_id}") @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def remove_course_lesson(request: HttpRequest, id: str, lesson_id: int): count, _ = await Lesson.objects.filter(course_id=id, id=lesson_id, course__owner_id=request.auth).adelete() if count == 0: @@ -389,7 +389,7 @@ class CourseCertificateSaveSpec(CourseCertificateSpec): @router.post("/course/{id}/certificate", response=list[int]) @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def save_course_certificates(request: HttpRequest, id: str, data: RootModel[list[CourseCertificateSaveSpec]]): course = await aget_object_or_404(Course, id=id, owner_id=request.auth) course_certificates = await course.course_certificates.abulk_create( @@ -406,7 +406,7 @@ async def save_course_certificates(request: HttpRequest, id: str, data: RootMode @router.delete("/course/{id}/certificate/{course_certificate_id}") @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def remove_course_certificate(request: HttpRequest, id: str, course_certificate_id: int): count, _ = await CourseCertificate.objects.filter( course_id=id, id=course_certificate_id, course__owner_id=request.auth @@ -421,7 +421,7 @@ class CourseRelationSaveSpec(CourseRelationSpec): @router.post("/course/{id}/relation", response=list[int]) @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def save_course_relations(request: HttpRequest, id: str, data: RootModel[list[CourseRelationSaveSpec]]): course = await aget_object_or_404(Course, id=id, owner_id=request.auth) course_relations = await course.course_relations.abulk_create( @@ -438,7 +438,7 @@ async def save_course_relations(request: HttpRequest, id: str, data: RootModel[l @router.delete("/course/{id}/relation/{course_relation_id}") @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def remove_course_relation(request: HttpRequest, id: str, course_relation_id: int): count, _ = await CourseRelation.objects.filter( course_id=id, id=course_relation_id, course__owner_id=request.auth @@ -453,7 +453,7 @@ class CourseCategorySaveSpec(CourseCategorySpec): @router.post("/course/{id}/category", response=list[int]) @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def save_course_categories(request: HttpRequest, id: str, data: RootModel[list[CourseCategorySaveSpec]]): course = await aget_object_or_404(Course, id=id, owner_id=request.auth) course_categories = await course.course_categories.abulk_create( @@ -470,7 +470,7 @@ async def save_course_categories(request: HttpRequest, id: str, data: RootModel[ @router.delete("/course/{id}/category/{course_category_id}") @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def remove_course_category(request: HttpRequest, id: str, course_category_id: int): count, _ = await CourseCategory.objects.filter( course_id=id, id=course_category_id, course__owner_id=request.auth @@ -485,7 +485,7 @@ class CourseInstructorSaveSpec(CourseInstructorSpec): @router.post("/course/{id}/instructor", response=list[int]) @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def save_course_instructors(request: HttpRequest, id: str, data: RootModel[list[CourseInstructorSaveSpec]]): course = await aget_object_or_404(Course, id=id, owner_id=request.auth) instructors = await course.course_instructors.abulk_create( @@ -502,7 +502,7 @@ async def save_course_instructors(request: HttpRequest, id: str, data: RootModel @router.delete("/course/{id}/instructor/{course_instructor_id}") @editor_required() -@track_draft(Course, id_field="id") +@track_editing(Course, id_field="id") async def remove_course_instructor(request: HttpRequest, id: str, course_instructor_id: int): count, _ = await CourseInstructor.objects.filter( course_id=id, id=course_instructor_id, course__owner_id=request.auth diff --git a/core/apps/studio/api/v1/discussion.py b/core/apps/studio/api/v1/discussion.py index 8ce5486..6845a2b 100644 --- a/core/apps/studio/api/v1/discussion.py +++ b/core/apps/studio/api/v1/discussion.py @@ -19,7 +19,7 @@ ) from apps.common.util import HttpRequest, ModeChoices from apps.discussion.models import Attempt, Discussion, Question, QuestionPool -from apps.studio.decorator import editor_required, track_draft +from apps.studio.decorator import editor_required, track_editing class DiscussionQuestionSaveSpec(Schema): @@ -89,7 +89,7 @@ async def get_discussion(request: HttpRequest, id: str): @router.post("/discussion", response=str) @editor_required() -@track_draft(Discussion) +@track_editing(Discussion) async def save_discussion( request: HttpRequest, data: DiscussionSaveSpec, @@ -130,7 +130,7 @@ def create_new(): @router.delete("/discussion/{id}") @editor_required() -@track_draft(Discussion, id_field="id") +@track_editing(Discussion, id_field="id") async def delete_discussion(request: HttpRequest, id: str): if await Attempt.objects.filter(discussion_id=id).exclude(mode=ModeChoices.PREVIEW).aexists(): raise ValueError(ErrorCode.ATTEMPT_EXISTS) @@ -150,7 +150,7 @@ async def get_discussion_questions(request: HttpRequest, id: str): @router.post("/discussion/{id}/question", response=list[int]) @editor_required() -@track_draft(Discussion, id_field="id") +@track_editing(Discussion, id_field="id") async def save_discussion_questions( request: HttpRequest, id: str, @@ -198,7 +198,7 @@ async def save_discussion_questions( @router.delete("/discussion/{id}/question/{question_id}") @editor_required() -@track_draft(Discussion, id_field="id") +@track_editing(Discussion, id_field="id") async def delete_discussion_quesion(request: HttpRequest, id: str, question_id: int): if await Attempt.objects.filter( discussion_id=id, question_id=question_id, discussion__owner_id=request.auth diff --git a/core/apps/studio/api/v1/exam.py b/core/apps/studio/api/v1/exam.py index 6ca1bed..ac9451e 100644 --- a/core/apps/studio/api/v1/exam.py +++ b/core/apps/studio/api/v1/exam.py @@ -19,7 +19,7 @@ ) from apps.common.util import HttpRequest, ModeChoices from apps.exam.models import Attempt, Exam, Question, QuestionPool, Solution -from apps.studio.decorator import editor_required, track_draft +from apps.studio.decorator import editor_required, track_editing class ExamQuestionSaveSpec(Schema): @@ -107,7 +107,7 @@ async def get_exam(request: HttpRequest, id: str): @router.post("/exam", response=str) @editor_required() -@track_draft(Exam) +@track_editing(Exam) async def save_exam( request: HttpRequest, data: ExamSaveSpec, @@ -150,7 +150,7 @@ def create_new(): @router.delete("/exam/{id}") @editor_required() -@track_draft(Exam, id_field="id") +@track_editing(Exam, id_field="id") async def delete_exam(request: HttpRequest, id: str): if await Attempt.objects.filter(exam_id=id).exclude(mode=ModeChoices.PREVIEW).aexists(): raise ValueError(ErrorCode.ATTEMPT_EXISTS) @@ -171,7 +171,7 @@ async def get_exam_questions(request: HttpRequest, id: str): @router.post("/exam/{id}/question", response=list[int]) @editor_required() -@track_draft(Exam, id_field="id") +@track_editing(Exam, id_field="id") async def save_exam_questions( request: HttpRequest, id: str, @@ -222,7 +222,7 @@ async def save_exam_questions( @router.delete("/exam/{id}/question/{question_id}") @editor_required() -@track_draft(Exam, id_field="id") +@track_editing(Exam, id_field="id") async def delete_exam_quesion(request: HttpRequest, id: str, question_id: int): if await Attempt.objects.filter(exam_id=id, questions=question_id, exam__owner_id=request.auth).aexists(): raise ValueError(ErrorCode.IN_USE) diff --git a/core/apps/studio/api/v1/media.py b/core/apps/studio/api/v1/media.py index 83f9e6c..8fe72b4 100644 --- a/core/apps/studio/api/v1/media.py +++ b/core/apps/studio/api/v1/media.py @@ -14,7 +14,7 @@ from apps.common.util import HttpRequest, ModeChoices from apps.content.models import Media, Subtitle, Watch from apps.quiz.models import Quiz -from apps.studio.decorator import editor_required, track_draft +from apps.studio.decorator import editor_required, track_editing class SubtitleSpec(Schema): @@ -71,7 +71,7 @@ async def get_media(request: HttpRequest, id: str): @router.post("/media", response=str) @editor_required() -@track_draft(Media) +@track_editing(Media) async def save_media( request: HttpRequest, data: MediaSaveSpec, @@ -117,7 +117,7 @@ async def save_media( @router.delete("/media/{id}") @editor_required() -@track_draft(Media, id_field="id") +@track_editing(Media, id_field="id") async def delete_media(request: HttpRequest, id: str): if await Watch.objects.filter(media_id=id).exclude(mode=ModeChoices.PREVIEW).aexists(): raise ValueError(ErrorCode.ATTEMPT_EXISTS) @@ -126,7 +126,7 @@ async def delete_media(request: HttpRequest, id: str): @router.post("/media/{id}/subtitle") @editor_required() -@track_draft(Media, id_field="id") +@track_editing(Media, id_field="id") async def save_media_subtitle(request, id: str, data: SubtitleSpec): media = await aget_object_or_404(Media, id=id, owner_id=request.auth) await Subtitle.objects.aupdate_or_create(media=media, lang=data.lang, defaults={"body": data.body}) @@ -134,7 +134,7 @@ async def save_media_subtitle(request, id: str, data: SubtitleSpec): @router.delete("/media/{id}/subtitle/{lang}") @editor_required() -@track_draft(Media, id_field="id") +@track_editing(Media, id_field="id") async def delete_media_subtitle(request: HttpRequest, id: str, lang: str): count, _ = await Subtitle.objects.filter(lang=lang, media_id=id, media__owner_id=request.auth).adelete() if count < 1: @@ -143,7 +143,7 @@ async def delete_media_subtitle(request: HttpRequest, id: str, lang: str): @router.post("/media/{id}/subtitle/{lang}/quiz", response=str) @editor_required() -@track_draft(Media, id_field="id") +@track_editing(Media, id_field="id") async def create_media_quiz(request, id: str, lang: str): media = await aget_object_or_404(Media, id=id, owner_id=request.auth) quiz = await media.create_quiz(lang_code=lang) diff --git a/core/apps/studio/api/v1/quiz.py b/core/apps/studio/api/v1/quiz.py index 84a9573..92af5e5 100644 --- a/core/apps/studio/api/v1/quiz.py +++ b/core/apps/studio/api/v1/quiz.py @@ -13,7 +13,7 @@ from apps.common.schema import FileSizeValidator, FileTypeValidator, LearningObjectMixinSchema, Schema from apps.common.util import HttpRequest, ModeChoices from apps.quiz.models import Attempt, Question, QuestionPool, Quiz, Solution -from apps.studio.decorator import editor_required, track_draft +from apps.studio.decorator import editor_required, track_editing class QuizQuestionSaveSpec(Schema): @@ -92,7 +92,7 @@ async def get_quiz(request: HttpRequest, id: str): @router.post("/quiz", response=str) @editor_required() -@track_draft(Quiz) +@track_editing(Quiz) async def save_quiz( request: HttpRequest, data: QuizSaveSpec, @@ -133,7 +133,7 @@ def create_new(): @router.delete("/quiz/{id}") @editor_required() -@track_draft(Quiz, id_field="id") +@track_editing(Quiz, id_field="id") async def delete_quiz(request: HttpRequest, id: str): if await Attempt.objects.filter(quiz_id=id).exclude(mode=ModeChoices.PREVIEW).aexists(): raise ValueError(ErrorCode.ATTEMPT_EXISTS) @@ -154,7 +154,7 @@ async def get_quiz_questions(request: HttpRequest, id: str): @router.post("/quiz/{id}/question", response=list[int]) @editor_required() -@track_draft(Quiz, id_field="id") +@track_editing(Quiz, id_field="id") async def save_quiz_questions( request: HttpRequest, id: str, @@ -205,7 +205,7 @@ async def save_quiz_questions( @router.delete("/quiz/{id}/question/{question_id}") @editor_required() -@track_draft(Quiz, id_field="id") +@track_editing(Quiz, id_field="id") async def delete_quiz_quesion(request: HttpRequest, id: str, question_id: int): if await Attempt.objects.filter(quiz_id=id, questions=question_id, quiz__owner_id=request.auth).aexists(): raise ValueError(ErrorCode.IN_USE) diff --git a/core/apps/studio/api/v1/survey.py b/core/apps/studio/api/v1/survey.py index 42503ad..b3de3de 100644 --- a/core/apps/studio/api/v1/survey.py +++ b/core/apps/studio/api/v1/survey.py @@ -12,7 +12,7 @@ from apps.common.error import ErrorCode from apps.common.schema import FileSizeValidator, FileTypeValidator, LearningObjectMixinSchema, Schema from apps.common.util import HttpRequest, ModeChoices -from apps.studio.decorator import editor_required, track_draft +from apps.studio.decorator import editor_required, track_editing from apps.survey.models import Question, QuestionPool, Submission, Survey @@ -79,7 +79,7 @@ async def get_survey(request: HttpRequest, id: str): @router.post("/survey", response=str) @editor_required() -@track_draft(Survey) +@track_editing(Survey) async def save_survey( request: HttpRequest, data: SurveySaveSpec, # form doesn't work nested model @@ -120,7 +120,7 @@ def create_new(): @router.delete("/survey/{id}") @editor_required() -@track_draft(Survey, id_field="id") +@track_editing(Survey, id_field="id") async def delete_survey(request: HttpRequest, id: str): if await Submission.objects.filter(survey_id=id).exclude(mode=ModeChoices.PREVIEW).aexists(): raise ValueError(ErrorCode.ATTEMPT_EXISTS) @@ -140,7 +140,7 @@ async def get_survey_questions(request: HttpRequest, id: str): @router.post("/survey/{id}/question", response=list[int]) @editor_required() -@track_draft(Survey, id_field="id") +@track_editing(Survey, id_field="id") async def save_survey_questions( request: HttpRequest, id: str, @@ -180,7 +180,7 @@ async def save_survey_questions( @router.delete("/survey/{id}/question/{question_id}") @editor_required() -@track_draft(Survey, id_field="id") +@track_editing(Survey, id_field="id") async def delete_survey_quesion(request: HttpRequest, id: str, question_id: int): count, _ = await Question.objects.filter(id=question_id, pool__survey__id=id).adelete() if count < 1: diff --git a/core/apps/studio/decorator.py b/core/apps/studio/decorator.py index 0eb0f47..32f3a98 100644 --- a/core/apps/studio/decorator.py +++ b/core/apps/studio/decorator.py @@ -7,7 +7,7 @@ from apps.common.error import ErrorCode from apps.common.util import HttpRequest -from apps.studio.models import Draft +from apps.studio.models import Editing def editor_required(): @@ -24,7 +24,7 @@ async def wrapper(request: HttpRequest, *args, **kwargs): return decorator -def track_draft(model, *, id_field: str | None = None): +def track_editing(model, *, id_field: str | None = None): def decorator(func): @wraps(func) async def wrapper(request: HttpRequest, *args, **kwargs): @@ -34,7 +34,7 @@ async def wrapper(request: HttpRequest, *args, **kwargs): if not id_field and (type(result) not in (int, str)): raise ImproperlyConfigured - await Draft.objects.aupdate_or_create( + await Editing.objects.aupdate_or_create( author_id=request.auth, content_type=content_type, content_id=kwargs[id_field] if id_field else result, diff --git a/core/apps/studio/migrations/0001_initial.py b/core/apps/studio/migrations/0001_initial.py index ec17f0c..b84fb5a 100644 --- a/core/apps/studio/migrations/0001_initial.py +++ b/core/apps/studio/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import django.db.models.deletion import django.utils.timezone @@ -20,7 +20,7 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Draft', + name='Editing', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('edited', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Edited')), @@ -30,12 +30,12 @@ class Migration(migrations.Migration): ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype', verbose_name='Content type')), ], options={ - 'verbose_name': 'Content Draft', - 'verbose_name_plural': 'Content Drafts', + 'verbose_name': 'Content Editing', + 'verbose_name_plural': 'Content Editings', }, ), migrations.CreateModel( - name='DraftEvent', + name='EditingEvent', fields=[ ('pgh_id', models.AutoField(primary_key=True, serialize=False)), ('pgh_created_at', models.DateTimeField(auto_now_add=True)), @@ -47,30 +47,30 @@ class Migration(migrations.Migration): ('author', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Author')), ('content_type', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', related_query_name='+', to='contenttypes.contenttype', verbose_name='Content type')), ('pgh_context', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='pghistory.context')), - ('pgh_obj', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='studio.draft')), + ('pgh_obj', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='events', to='studio.editing')), ], options={ 'abstract': False, }, ), migrations.AddConstraint( - model_name='draft', - constraint=models.UniqueConstraint(fields=('author', 'content_type', 'content_id'), name='studio_draft_coty_coid_uniq'), + model_name='editing', + constraint=models.UniqueConstraint(fields=('author', 'content_type', 'content_id'), name='studio_editing_coty_coid_uniq'), ), pgtrigger.migrations.AddTrigger( - model_name='draft', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "studio_draftevent" ("author_id", "content_id", "content_type_id", "detail", "edited", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id") VALUES (NEW."author_id", NEW."content_id", NEW."content_type_id", NEW."detail", NEW."edited", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id"); RETURN NULL;', hash='74a02de2f26d4e59c7d4d0cac7fb25ac0a34ed7f', operation='INSERT', pgid='pgtrigger_insert_insert_f495e', table='studio_draft', when='AFTER')), + model_name='editing', + trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "studio_editingevent" ("author_id", "content_id", "content_type_id", "detail", "edited", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id") VALUES (NEW."author_id", NEW."content_id", NEW."content_type_id", NEW."detail", NEW."edited", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id"); RETURN NULL;', hash='6dc54e2c432d8859ea15f3a0edef97f5d2b2a429', operation='INSERT', pgid='pgtrigger_insert_insert_4cef1', table='studio_editing', when='AFTER')), ), pgtrigger.migrations.AddTrigger( - model_name='draft', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD.* IS DISTINCT FROM NEW.*)', func='INSERT INTO "studio_draftevent" ("author_id", "content_id", "content_type_id", "detail", "edited", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id") VALUES (NEW."author_id", NEW."content_id", NEW."content_type_id", NEW."detail", NEW."edited", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id"); RETURN NULL;', hash='c09f80b4afc40926883419725f88bc86fb1a5b07', operation='UPDATE', pgid='pgtrigger_update_update_477de', table='studio_draft', when='AFTER')), + model_name='editing', + trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD.* IS DISTINCT FROM NEW.*)', func='INSERT INTO "studio_editingevent" ("author_id", "content_id", "content_type_id", "detail", "edited", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id") VALUES (NEW."author_id", NEW."content_id", NEW."content_type_id", NEW."detail", NEW."edited", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id"); RETURN NULL;', hash='c1f6badaf8cb22ff861fe1265fc4f9f7a9eb57c9', operation='UPDATE', pgid='pgtrigger_update_update_9a5c4', table='studio_editing', when='AFTER')), ), pgtrigger.migrations.AddTrigger( - model_name='draft', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "studio_draftevent" ("author_id", "content_id", "content_type_id", "detail", "edited", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id") VALUES (OLD."author_id", OLD."content_id", OLD."content_type_id", OLD."detail", OLD."edited", OLD."id", _pgh_attach_context(), NOW(), \'delete\', OLD."id"); RETURN NULL;', hash='a50e7f9dec70e3a03923edf02de44e193110e844', operation='DELETE', pgid='pgtrigger_delete_delete_db766', table='studio_draft', when='AFTER')), + model_name='editing', + trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "studio_editingevent" ("author_id", "content_id", "content_type_id", "detail", "edited", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id") VALUES (OLD."author_id", OLD."content_id", OLD."content_type_id", OLD."detail", OLD."edited", OLD."id", _pgh_attach_context(), NOW(), \'delete\', OLD."id"); RETURN NULL;', hash='27dedb0d4dbca1c8c960cb9c39d32a60d1751b66', operation='DELETE', pgid='pgtrigger_delete_delete_c1e2e', table='studio_editing', when='AFTER')), ), pgtrigger.migrations.AddTrigger( - model_name='draftevent', - trigger=pgtrigger.compiler.Trigger(name='append_only', sql=pgtrigger.compiler.UpsertTriggerSql(func="RAISE EXCEPTION 'pgtrigger: Cannot update or delete rows from % table', TG_TABLE_NAME;", hash='011abe80fb87e61edab06b9858f92f4dd66ce930', operation='UPDATE OR DELETE', pgid='pgtrigger_append_only_dd2c4', table='studio_draftevent', when='BEFORE')), + model_name='editingevent', + trigger=pgtrigger.compiler.Trigger(name='append_only', sql=pgtrigger.compiler.UpsertTriggerSql(func="RAISE EXCEPTION 'pgtrigger: Cannot update or delete rows from % table', TG_TABLE_NAME;", hash='9ffd877bdfc5aebf92060cf13876d66eb1508081', operation='UPDATE OR DELETE', pgid='pgtrigger_append_only_c43e5', table='studio_editingevent', when='BEFORE')), ), ] diff --git a/core/apps/studio/models.py b/core/apps/studio/models.py index b261021..d475c48 100644 --- a/core/apps/studio/models.py +++ b/core/apps/studio/models.py @@ -11,7 +11,7 @@ @pghistory.track() -class Draft(Model): +class Editing(Model): author = ForeignKey(User, CASCADE, verbose_name=_("Author")) edited = DateTimeField(_("Edited"), default=timezone.now) detail = TextField(_("Action"), blank=True, default="") @@ -21,10 +21,10 @@ class Draft(Model): content = GenericForeignKey("content_type", "content_id") class Meta: - verbose_name = _("Content Draft") - verbose_name_plural = _("Content Drafts") + verbose_name = _("Content Editing") + verbose_name_plural = _("Content Editings") constraints = [ - UniqueConstraint(fields=["author", "content_type", "content_id"], name="studio_draft_coty_coid_uniq") + UniqueConstraint(fields=["author", "content_type", "content_id"], name="studio_editing_coty_coid_uniq") ] if TYPE_CHECKING: diff --git a/core/apps/survey/migrations/0001_initial.py b/core/apps/survey/migrations/0001_initial.py index e4125ca..c26dcc6 100644 --- a/core/apps/survey/migrations/0001_initial.py +++ b/core/apps/survey/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import apps.common.util import django.contrib.postgres.fields @@ -109,7 +109,6 @@ class Migration(migrations.Migration): options={ 'verbose_name': 'Submission', 'verbose_name_plural': 'Submissions', - 'abstract': False, }, ), migrations.CreateModel( @@ -127,7 +126,7 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('thumbnail', models.ImageField(upload_to='', verbose_name='Thumbnail')), ('complete_message', models.TextField(blank=True, default='', verbose_name='Complete Message')), ('anonymous', models.BooleanField(default=True, verbose_name='Anonymous')), @@ -185,7 +184,7 @@ class Migration(migrations.Migration): ('passing_point', models.PositiveSmallIntegerField(default=60, verbose_name='Passing Point')), ('max_attempts', models.PositiveSmallIntegerField(default=0, verbose_name='Max Attempts')), ('verification_required', models.BooleanField(default=False, verbose_name='Verification Required')), - ('published', models.BooleanField(default=False, verbose_name='Published')), + ('published', models.DateTimeField(blank=True, null=True, verbose_name='Published')), ('thumbnail', models.ImageField(upload_to='', verbose_name='Thumbnail')), ('complete_message', models.TextField(blank=True, default='', verbose_name='Complete Message')), ('anonymous', models.BooleanField(default=True, verbose_name='Anonymous')), diff --git a/core/apps/survey/migrations/0002_remove_survey_insert_insert_and_more.py b/core/apps/survey/migrations/0002_remove_survey_insert_insert_and_more.py deleted file mode 100644 index 15aeecf..0000000 --- a/core/apps/survey/migrations/0002_remove_survey_insert_insert_and_more.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:51 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('survey', '0001_initial'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='survey', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='survey', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='survey', - name='delete_delete', - ), - migrations.RemoveField( - model_name='survey', - name='published', - ), - migrations.RemoveField( - model_name='surveyevent', - name='published', - ), - pgtrigger.migrations.AddTrigger( - model_name='survey', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "survey_surveyevent" ("anonymous", "audience", "complete_message", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "show_results", "thumbnail", "title", "verification_required") VALUES (NEW."anonymous", NEW."audience", NEW."complete_message", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."question_pool_id", NEW."show_results", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='cf049c97c09d55b435d07a2b8fcb97aa5c07abdf', operation='INSERT', pgid='pgtrigger_insert_insert_69de8', table='survey_survey', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='survey', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."anonymous" IS DISTINCT FROM (NEW."anonymous") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."complete_message" IS DISTINCT FROM (NEW."complete_message") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."show_results" IS DISTINCT FROM (NEW."show_results") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "survey_surveyevent" ("anonymous", "audience", "complete_message", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "show_results", "thumbnail", "title", "verification_required") VALUES (NEW."anonymous", NEW."audience", NEW."complete_message", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."question_pool_id", NEW."show_results", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='d26a6b2ad7958e8c48103888e80f5caa7745b7a0', operation='UPDATE', pgid='pgtrigger_update_update_76063', table='survey_survey', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='survey', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "survey_surveyevent" ("anonymous", "audience", "complete_message", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "question_pool_id", "show_results", "thumbnail", "title", "verification_required") VALUES (OLD."anonymous", OLD."audience", OLD."complete_message", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."question_pool_id", OLD."show_results", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='2a13eddd2f5050ffada4d3156be445e992e67ed2', operation='DELETE', pgid='pgtrigger_delete_delete_3408b', table='survey_survey', when='AFTER')), - ), - ] diff --git a/core/apps/survey/migrations/0003_remove_survey_insert_insert_and_more.py b/core/apps/survey/migrations/0003_remove_survey_insert_insert_and_more.py deleted file mode 100644 index 170f7f1..0000000 --- a/core/apps/survey/migrations/0003_remove_survey_insert_insert_and_more.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 6.0.2 on 2026-03-03 12:55 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('survey', '0002_remove_survey_insert_insert_and_more'), - ] - - operations = [ - pgtrigger.migrations.RemoveTrigger( - model_name='survey', - name='insert_insert', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='survey', - name='update_update', - ), - pgtrigger.migrations.RemoveTrigger( - model_name='survey', - name='delete_delete', - ), - migrations.AddField( - model_name='survey', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - migrations.AddField( - model_name='surveyevent', - name='published', - field=models.DateTimeField(blank=True, null=True, verbose_name='Published'), - ), - pgtrigger.migrations.AddTrigger( - model_name='survey', - trigger=pgtrigger.compiler.Trigger(name='insert_insert', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "survey_surveyevent" ("anonymous", "audience", "complete_message", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "show_results", "thumbnail", "title", "verification_required") VALUES (NEW."anonymous", NEW."audience", NEW."complete_message", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."published", NEW."question_pool_id", NEW."show_results", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='66b93365d3e4dfad976fc854256990d120a29ee6', operation='INSERT', pgid='pgtrigger_insert_insert_69de8', table='survey_survey', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='survey', - trigger=pgtrigger.compiler.Trigger(name='update_update', sql=pgtrigger.compiler.UpsertTriggerSql(condition='WHEN (OLD."anonymous" IS DISTINCT FROM (NEW."anonymous") OR OLD."audience" IS DISTINCT FROM (NEW."audience") OR OLD."complete_message" IS DISTINCT FROM (NEW."complete_message") OR OLD."description" IS DISTINCT FROM (NEW."description") OR OLD."duration" IS DISTINCT FROM (NEW."duration") OR OLD."featured" IS DISTINCT FROM (NEW."featured") OR OLD."format" IS DISTINCT FROM (NEW."format") OR OLD."id" IS DISTINCT FROM (NEW."id") OR OLD."max_attempts" IS DISTINCT FROM (NEW."max_attempts") OR OLD."owner_id" IS DISTINCT FROM (NEW."owner_id") OR OLD."passing_point" IS DISTINCT FROM (NEW."passing_point") OR OLD."published" IS DISTINCT FROM (NEW."published") OR OLD."question_pool_id" IS DISTINCT FROM (NEW."question_pool_id") OR OLD."show_results" IS DISTINCT FROM (NEW."show_results") OR OLD."thumbnail" IS DISTINCT FROM (NEW."thumbnail") OR OLD."title" IS DISTINCT FROM (NEW."title") OR OLD."verification_required" IS DISTINCT FROM (NEW."verification_required"))', func='INSERT INTO "survey_surveyevent" ("anonymous", "audience", "complete_message", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "show_results", "thumbnail", "title", "verification_required") VALUES (NEW."anonymous", NEW."audience", NEW."complete_message", NEW."created", NEW."description", NEW."duration", NEW."featured", NEW."format", NEW."id", NEW."max_attempts", NEW."modified", NEW."owner_id", NEW."passing_point", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."published", NEW."question_pool_id", NEW."show_results", NEW."thumbnail", NEW."title", NEW."verification_required"); RETURN NULL;', hash='9765e2d59ac04d380cf132094cb23d549cea4490', operation='UPDATE', pgid='pgtrigger_update_update_76063', table='survey_survey', when='AFTER')), - ), - pgtrigger.migrations.AddTrigger( - model_name='survey', - trigger=pgtrigger.compiler.Trigger(name='delete_delete', sql=pgtrigger.compiler.UpsertTriggerSql(func='INSERT INTO "survey_surveyevent" ("anonymous", "audience", "complete_message", "created", "description", "duration", "featured", "format", "id", "max_attempts", "modified", "owner_id", "passing_point", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "published", "question_pool_id", "show_results", "thumbnail", "title", "verification_required") VALUES (OLD."anonymous", OLD."audience", OLD."complete_message", OLD."created", OLD."description", OLD."duration", OLD."featured", OLD."format", OLD."id", OLD."max_attempts", OLD."modified", OLD."owner_id", OLD."passing_point", _pgh_attach_context(), NOW(), \'delete\', OLD."id", OLD."published", OLD."question_pool_id", OLD."show_results", OLD."thumbnail", OLD."title", OLD."verification_required"); RETURN NULL;', hash='95898b40ee3f5ff1d9fc223ba9ccb3c1f4c34b26', operation='DELETE', pgid='pgtrigger_delete_delete_3408b', table='survey_survey', when='AFTER')), - ), - ] diff --git a/core/apps/tracking/migrations/0001_initial.py b/core/apps/tracking/migrations/0001_initial.py index b0357cc..ad55837 100644 --- a/core/apps/tracking/migrations/0001_initial.py +++ b/core/apps/tracking/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 import pghistory.utils from django.db import migrations, models diff --git a/core/apps/warehouse/migrations/0001_initial.py b/core/apps/warehouse/migrations/0001_initial.py index 5922bb8..ecb7871 100644 --- a/core/apps/warehouse/migrations/0001_initial.py +++ b/core/apps/warehouse/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 6.0.2 on 2026-03-02 17:13 +# Generated by Django 6.0.3 on 2026-03-07 04:59 from django.db import migrations, models diff --git a/core/locale/en/LC_MESSAGES/django.po b/core/locale/en/LC_MESSAGES/django.po index e3de82d..e4185f4 100644 --- a/core/locale/en/LC_MESSAGES/django.po +++ b/core/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-03-04 00:39+0900\n" +"POT-Creation-Date: 2026-03-07 13:30+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -206,8 +206,8 @@ msgstr "" msgid "Email" msgstr "" -#: apps/account/models.py:114 apps/assignment/models.py:531 -#: apps/assignment/models.py:549 apps/assignment/models.py:568 +#: apps/account/models.py:114 apps/assignment/models.py:524 +#: apps/assignment/models.py:542 apps/assignment/models.py:561 #: apps/competency/certificate.py:274 apps/competency/models.py:61 #: apps/competency/models.py:102 apps/competency/models.py:122 #: apps/competency/models.py:140 apps/competency/models.py:170 @@ -251,8 +251,8 @@ msgstr "" #: apps/common/models.py:153 apps/competency/models.py:173 #: apps/competency/models.py:241 apps/learning/models.py:80 #: apps/learning/models.py:335 apps/operation/models.py:176 -#: apps/operation/models.py:224 apps/operation/models.py:606 -#: apps/operation/models.py:625 apps/store/models.py:107 +#: apps/operation/models.py:224 apps/operation/models.py:619 +#: apps/operation/models.py:638 apps/store/models.py:107 #: apps/store/models.py:148 msgid "Active" msgstr "" @@ -271,8 +271,8 @@ msgstr "" #: apps/competency/models.py:139 apps/content/models.py:308 #: apps/content/models.py:476 apps/learning/models.py:79 #: apps/learning/models.py:497 apps/operation/models.py:159 -#: apps/operation/models.py:531 apps/operation/models.py:602 -#: apps/operation/models.py:718 apps/partner/admin.py:56 +#: apps/operation/models.py:544 apps/operation/models.py:615 +#: apps/operation/models.py:731 apps/partner/admin.py:56 #: apps/partner/models.py:96 apps/partner/models.py:211 apps/sso/models.py:29 #: apps/sso/models.py:49 apps/store/models.py:147 msgid "User" @@ -313,7 +313,7 @@ msgid "Temporary Passwords" msgstr "" #: apps/account/models.py:561 apps/account/models.py:567 -#: apps/account/models.py:573 apps/operation/models.py:603 +#: apps/account/models.py:573 apps/operation/models.py:616 msgid "Token" msgstr "" @@ -403,7 +403,7 @@ msgstr "" #: apps/course/templates/course/mail/grade_completed_email.html:50 #: apps/course/templates/course/mail/start_today_email.html:52 #: apps/course/templates/course/mail/weekly_progress_email.html:51 -#: apps/operation/models.py:617 +#: apps/operation/models.py:630 msgid "Privacy Policy" msgstr "" @@ -417,11 +417,11 @@ msgstr "" #: apps/course/templates/course/mail/grade_completed_email.html:51 #: apps/course/templates/course/mail/start_today_email.html:53 #: apps/course/templates/course/mail/weekly_progress_email.html:52 -#: apps/operation/models.py:616 +#: apps/operation/models.py:629 msgid "Terms of Service" msgstr "" -#: apps/assignment/admin.py:118 apps/assignment/admin.py:119 +#: apps/assignment/admin.py:107 apps/assignment/admin.py:108 #: apps/discussion/admin.py:69 apps/discussion/admin.py:70 #: apps/operation/admin.py:103 apps/operation/admin.py:104 #: apps/operation/admin.py:150 apps/operation/admin.py:151 @@ -429,43 +429,43 @@ msgstr "" msgid "Attachments" msgstr "" -#: apps/assignment/admin.py:123 apps/exam/admin.py:103 +#: apps/assignment/admin.py:112 apps/exam/admin.py:103 msgid "Submission History" msgstr "" -#: apps/assignment/admin.py:124 apps/exam/admin.py:104 +#: apps/assignment/admin.py:113 apps/exam/admin.py:104 msgid "Submission Histories" msgstr "" -#: apps/assignment/admin.py:152 apps/discussion/admin.py:97 +#: apps/assignment/admin.py:141 apps/discussion/admin.py:97 #: apps/exam/admin.py:123 msgid "Grading History" msgstr "" -#: apps/assignment/admin.py:153 apps/discussion/admin.py:98 +#: apps/assignment/admin.py:142 apps/discussion/admin.py:98 #: apps/exam/admin.py:124 msgid "Grading Histories" msgstr "" -#: apps/assignment/admin.py:159 apps/assignment/models.py:442 +#: apps/assignment/admin.py:148 apps/assignment/models.py:435 #: apps/course/admin.py:132 apps/course/admin.py:149 #: apps/discussion/admin.py:103 apps/discussion/models.py:434 #: apps/exam/admin.py:133 apps/exam/models.py:395 apps/quiz/models.py:360 msgid "Grade" msgstr "" -#: apps/assignment/admin.py:180 apps/common/models.py:163 +#: apps/assignment/admin.py:166 apps/common/models.py:163 #: apps/discussion/admin.py:122 apps/exam/admin.py:152 msgid "Earned Details" msgstr "" -#: apps/assignment/admin.py:185 apps/common/models.py:168 +#: apps/assignment/admin.py:171 apps/common/models.py:168 #: apps/discussion/admin.py:132 apps/exam/admin.py:157 msgid "Feedback" msgstr "" -#: apps/assignment/apps.py:8 apps/assignment/models.py:192 -#: apps/assignment/models.py:276 apps/warehouse/models.py:144 +#: apps/assignment/apps.py:8 apps/assignment/models.py:131 +#: apps/assignment/models.py:268 apps/warehouse/models.py:144 #: apps/warehouse/views.py:53 msgid "Assignment" msgstr "" @@ -474,26 +474,26 @@ msgstr "" #: apps/common/models.py:121 apps/course/models.py:90 #: apps/discussion/models.py:79 apps/discussion/models.py:325 #: apps/exam/models.py:76 apps/operation/models.py:128 -#: apps/operation/models.py:192 apps/operation/models.py:363 -#: apps/operation/models.py:532 apps/operation/models.py:623 -#: apps/operation/models.py:745 apps/quiz/models.py:72 apps/survey/models.py:37 +#: apps/operation/models.py:192 apps/operation/models.py:376 +#: apps/operation/models.py:545 apps/operation/models.py:636 +#: apps/operation/models.py:758 apps/quiz/models.py:72 apps/survey/models.py:37 msgid "Title" msgstr "" -#: apps/assignment/models.py:82 apps/assignment/models.py:532 -#: apps/assignment/models.py:550 apps/assignment/models.py:569 +#: apps/assignment/models.py:82 apps/assignment/models.py:525 +#: apps/assignment/models.py:543 apps/assignment/models.py:562 #: apps/common/models.py:122 apps/competency/models.py:141 #: apps/competency/models.py:171 apps/competency/models.py:237 #: apps/course/models.py:91 apps/discussion/models.py:80 apps/exam/models.py:77 #: apps/learning/models.py:333 apps/operation/models.py:206 -#: apps/operation/models.py:624 apps/operation/models.py:746 +#: apps/operation/models.py:637 apps/operation/models.py:759 #: apps/partner/models.py:44 apps/partner/models.py:67 #: apps/partner/models.py:177 apps/quiz/models.py:73 apps/store/models.py:56 #: apps/store/models.py:103 apps/survey/models.py:38 msgid "Description" msgstr "" -#: apps/assignment/models.py:83 apps/assignment/models.py:187 +#: apps/assignment/models.py:83 apps/assignment/models.py:124 #: apps/content/models.py:83 apps/course/models.py:115 #: apps/discussion/models.py:81 apps/discussion/models.py:128 #: apps/exam/models.py:78 apps/exam/models.py:147 apps/operation/models.py:239 @@ -503,7 +503,7 @@ msgid "Owner" msgstr "" #: apps/assignment/models.py:86 apps/assignment/models.py:106 -#: apps/assignment/models.py:189 apps/discussion/models.py:84 +#: apps/assignment/models.py:126 apps/discussion/models.py:84 #: apps/discussion/models.py:104 apps/discussion/models.py:130 #: apps/exam/models.py:82 apps/exam/models.py:110 apps/exam/models.py:149 #: apps/quiz/models.py:78 apps/quiz/models.py:94 apps/quiz/models.py:127 @@ -516,12 +516,11 @@ msgstr "" msgid "Question Pools" msgstr "" -#: apps/assignment/models.py:107 apps/assignment/models.py:115 -#: apps/assignment/models.py:133 apps/assignment/models.py:278 -#: apps/discussion/models.py:114 apps/discussion/models.py:204 -#: apps/exam/admin.py:76 apps/exam/models.py:112 apps/exam/models.py:118 -#: apps/exam/models.py:135 apps/operation/admin.py:131 -#: apps/operation/models.py:222 apps/operation/models.py:364 +#: apps/assignment/models.py:107 apps/assignment/models.py:114 +#: apps/assignment/models.py:270 apps/discussion/models.py:114 +#: apps/discussion/models.py:204 apps/exam/admin.py:76 apps/exam/models.py:112 +#: apps/exam/models.py:118 apps/exam/models.py:135 apps/operation/admin.py:131 +#: apps/operation/models.py:222 apps/operation/models.py:377 #: apps/quiz/admin.py:50 apps/quiz/models.py:95 apps/quiz/models.py:101 #: apps/quiz/models.py:115 apps/survey/models.py:59 apps/survey/models.py:67 msgid "Question" @@ -541,64 +540,48 @@ msgid "Attachment File Types" msgstr "" #: apps/assignment/models.py:111 -msgid "Sample Attachment" -msgstr "" - -#: apps/assignment/models.py:112 msgid "Plagiarism Threshold Percentage" msgstr "" -#: apps/assignment/models.py:116 apps/discussion/models.py:115 +#: apps/assignment/models.py:115 apps/discussion/models.py:115 #: apps/exam/admin.py:77 apps/exam/models.py:119 apps/exam/models.py:242 #: apps/quiz/admin.py:51 apps/quiz/models.py:102 apps/quiz/models.py:243 #: apps/survey/models.py:68 msgid "Questions" msgstr "" -#: apps/assignment/models.py:134 apps/assignment/models.py:535 -#: apps/assignment/models.py:548 -msgid "Rubric" -msgstr "" - -#: apps/assignment/models.py:135 apps/exam/models.py:138 -#: apps/operation/admin.py:158 apps/operation/models.py:466 -#: apps/quiz/models.py:117 -msgid "Explanation" -msgstr "" - -#: apps/assignment/models.py:138 apps/exam/models.py:141 -#: apps/quiz/models.py:120 -msgid "Solution" +#: apps/assignment/models.py:125 apps/course/models.py:122 +#: apps/discussion/models.py:129 apps/exam/models.py:148 +#: apps/operation/models.py:196 +msgid "Honor Code" msgstr "" -#: apps/assignment/models.py:139 apps/exam/models.py:142 -#: apps/quiz/models.py:121 -msgid "Solutions" +#: apps/assignment/models.py:127 apps/assignment/models.py:528 +#: apps/assignment/models.py:541 +msgid "Rubric" msgstr "" -#: apps/assignment/models.py:188 apps/course/models.py:122 -#: apps/discussion/models.py:129 apps/exam/models.py:148 -#: apps/operation/models.py:196 apps/operation/tests/factories.py:61 -msgid "Honor Code" +#: apps/assignment/models.py:128 +msgid "Sample Attachment" msgstr "" -#: apps/assignment/models.py:193 +#: apps/assignment/models.py:132 msgid "Assignments" msgstr "" -#: apps/assignment/models.py:277 apps/course/models.py:571 +#: apps/assignment/models.py:269 apps/course/models.py:571 #: apps/discussion/models.py:203 apps/exam/models.py:241 -#: apps/operation/models.py:465 apps/quiz/models.py:242 +#: apps/operation/models.py:478 apps/quiz/models.py:242 msgid "Learner" msgstr "" -#: apps/assignment/models.py:279 apps/discussion/models.py:205 +#: apps/assignment/models.py:271 apps/discussion/models.py:205 #: apps/exam/models.py:243 apps/quiz/models.py:244 msgid "Retry" msgstr "" -#: apps/assignment/models.py:282 apps/assignment/models.py:405 -#: apps/assignment/models.py:438 apps/assignment/models.py:494 +#: apps/assignment/models.py:274 apps/assignment/models.py:398 +#: apps/assignment/models.py:431 apps/assignment/models.py:487 #: apps/discussion/models.py:208 apps/discussion/models.py:323 #: apps/discussion/models.py:430 apps/exam/models.py:246 #: apps/exam/models.py:365 apps/exam/models.py:375 apps/exam/models.py:391 @@ -606,114 +589,114 @@ msgstr "" msgid "Attempt" msgstr "" -#: apps/assignment/models.py:283 apps/discussion/models.py:209 +#: apps/assignment/models.py:275 apps/discussion/models.py:209 #: apps/exam/models.py:247 apps/quiz/models.py:248 msgid "Attempts" msgstr "" -#: apps/assignment/models.py:406 apps/operation/models.py:223 -#: apps/operation/models.py:437 +#: apps/assignment/models.py:399 apps/operation/models.py:223 +#: apps/operation/models.py:450 msgid "Answer" msgstr "" -#: apps/assignment/models.py:407 +#: apps/assignment/models.py:400 msgid "Extracted Text" msgstr "" -#: apps/assignment/models.py:410 apps/exam/models.py:379 +#: apps/assignment/models.py:403 apps/exam/models.py:379 #: apps/quiz/models.py:351 apps/survey/models.py:124 msgid "Submission" msgstr "" -#: apps/assignment/models.py:411 apps/exam/models.py:380 +#: apps/assignment/models.py:404 apps/exam/models.py:380 #: apps/quiz/models.py:352 apps/survey/models.py:125 msgid "Submissions" msgstr "" -#: apps/assignment/models.py:439 apps/course/models.py:778 +#: apps/assignment/models.py:432 apps/course/models.py:778 #: apps/discussion/models.py:431 apps/exam/models.py:392 msgid "Grader" msgstr "" -#: apps/assignment/models.py:443 apps/discussion/models.py:435 +#: apps/assignment/models.py:436 apps/discussion/models.py:435 #: apps/exam/models.py:396 apps/quiz/models.py:361 msgid "Grades" msgstr "" -#: apps/assignment/models.py:489 +#: apps/assignment/models.py:482 msgid "Not Detected" msgstr "" -#: apps/assignment/models.py:490 +#: apps/assignment/models.py:483 msgid "Detected" msgstr "" -#: apps/assignment/models.py:491 +#: apps/assignment/models.py:484 msgid "Excused" msgstr "" -#: apps/assignment/models.py:492 +#: apps/assignment/models.py:485 msgid "Not Resolved" msgstr "" -#: apps/assignment/models.py:495 apps/store/models.py:54 +#: apps/assignment/models.py:488 apps/store/models.py:54 #: apps/store/models.py:184 apps/store/models.py:361 apps/store/models.py:381 msgid "Status" msgstr "" -#: apps/assignment/models.py:496 +#: apps/assignment/models.py:489 msgid "Similarity Percentage" msgstr "" -#: apps/assignment/models.py:497 +#: apps/assignment/models.py:490 msgid "Flagged Text" msgstr "" -#: apps/assignment/models.py:498 +#: apps/assignment/models.py:491 msgid "Source Text" msgstr "" -#: apps/assignment/models.py:499 +#: apps/assignment/models.py:492 msgid "Source User ID" msgstr "" -#: apps/assignment/models.py:500 apps/store/models.py:385 +#: apps/assignment/models.py:493 apps/store/models.py:385 msgid "Reason" msgstr "" -#: apps/assignment/models.py:503 +#: apps/assignment/models.py:496 msgid "Plagiarism Check" msgstr "" -#: apps/assignment/models.py:504 +#: apps/assignment/models.py:497 msgid "Plagiarism Checks" msgstr "" -#: apps/assignment/models.py:536 +#: apps/assignment/models.py:529 msgid "Rubrics" msgstr "" -#: apps/assignment/models.py:553 +#: apps/assignment/models.py:546 msgid "Rubric Criterion" msgstr "" -#: apps/assignment/models.py:554 +#: apps/assignment/models.py:547 msgid "Rubric Criteria" msgstr "" -#: apps/assignment/models.py:567 +#: apps/assignment/models.py:560 msgid "Criterion" msgstr "" -#: apps/assignment/models.py:570 apps/exam/models.py:115 apps/quiz/models.py:98 +#: apps/assignment/models.py:563 apps/exam/models.py:115 apps/quiz/models.py:98 msgid "Point" msgstr "" -#: apps/assignment/models.py:573 +#: apps/assignment/models.py:566 msgid "Performance Level" msgstr "" -#: apps/assignment/models.py:574 +#: apps/assignment/models.py:567 msgid "Performance Levels" msgstr "" @@ -756,8 +739,8 @@ msgstr "" msgid "Chats" msgstr "" -#: apps/assistant/models.py:64 apps/operation/models.py:538 -#: apps/operation/models.py:554 apps/warehouse/models.py:111 +#: apps/assistant/models.py:64 apps/operation/models.py:551 +#: apps/operation/models.py:567 apps/warehouse/models.py:111 msgid "Message" msgstr "" @@ -765,8 +748,8 @@ msgstr "" msgid "Response" msgstr "" -#: apps/assistant/models.py:66 apps/operation/models.py:369 -#: apps/operation/models.py:469 apps/operation/models.py:755 +#: apps/assistant/models.py:66 apps/operation/models.py:382 +#: apps/operation/models.py:482 apps/operation/models.py:768 msgid "Path" msgstr "" @@ -779,7 +762,7 @@ msgstr "" msgid "Bookmarked" msgstr "" -#: apps/assistant/models.py:69 apps/operation/models.py:776 +#: apps/assistant/models.py:69 apps/operation/models.py:789 msgid "Rating" msgstr "" @@ -836,7 +819,7 @@ msgid "ordering_group must be defined in subclass" msgstr "" #: apps/common/models.py:112 apps/operation/models.py:240 -#: apps/operation/models.py:775 +#: apps/operation/models.py:788 msgid "Deleted" msgstr "" @@ -1522,18 +1505,18 @@ msgstr "" msgid "PDF" msgstr "" -#: apps/competency/models.py:299 apps/operation/models.py:535 +#: apps/competency/models.py:299 apps/operation/models.py:548 #: apps/tracking/models.py:29 msgid "Data" msgstr "" #: apps/competency/models.py:304 apps/learning/models.py:88 -#: apps/learning/models.py:471 apps/operation/models.py:366 +#: apps/learning/models.py:471 apps/operation/models.py:379 msgid "Content Type" msgstr "" #: apps/competency/models.py:305 apps/learning/models.py:91 -#: apps/learning/models.py:474 apps/operation/models.py:367 +#: apps/learning/models.py:474 apps/operation/models.py:380 #: apps/studio/models.py:20 msgid "Content ID" msgstr "" @@ -1700,8 +1683,8 @@ msgid "Public Access Medias" msgstr "" #: apps/content/models.py:257 apps/discussion/models.py:326 -#: apps/operation/models.py:129 apps/operation/models.py:533 -#: apps/operation/models.py:685 +#: apps/operation/models.py:129 apps/operation/models.py:546 +#: apps/operation/models.py:698 msgid "Body" msgstr "" @@ -2355,7 +2338,7 @@ msgstr "" msgid "Discussions" msgstr "" -#: apps/discussion/models.py:324 apps/operation/models.py:772 +#: apps/discussion/models.py:324 apps/operation/models.py:785 msgid "Parent" msgstr "" @@ -2400,6 +2383,19 @@ msgstr "" msgid "Correct Criteria" msgstr "" +#: apps/exam/models.py:138 apps/operation/admin.py:158 +#: apps/operation/models.py:479 apps/quiz/models.py:117 +msgid "Explanation" +msgstr "" + +#: apps/exam/models.py:141 apps/quiz/models.py:120 +msgid "Solution" +msgstr "" + +#: apps/exam/models.py:142 apps/quiz/models.py:121 +msgid "Solutions" +msgstr "" + #: apps/exam/models.py:154 msgid "Exams" msgstr "" @@ -2528,7 +2524,7 @@ msgstr "" msgid "Cohort Catalogs" msgstr "" -#: apps/operation/admin.py:121 apps/operation/models.py:439 +#: apps/operation/admin.py:121 apps/operation/models.py:452 msgid "Solved" msgstr "" @@ -2544,6 +2540,28 @@ msgstr "" msgid "Operation" msgstr "" +#: apps/operation/management/commands/setup_base_operation_data.py:41 +msgid "Student Honor Code" +msgstr "" + +#: apps/operation/management/commands/setup_base_operation_data.py:46 +msgid "Frequently Asked Questions" +msgstr "" + +#: apps/operation/management/commands/setup_base_operation_data.py:46 +msgid "default faq" +msgstr "" + +#: apps/operation/management/commands/setup_base_operation_data.py:52 +#, python-format +msgid "Write FAQ question %d here" +msgstr "" + +#: apps/operation/management/commands/setup_base_operation_data.py:52 +#, python-format +msgid "Write FAQ answer %d here" +msgstr "" + #: apps/operation/models.py:95 apps/operation/models.py:102 msgid "Tag" msgstr "" @@ -2564,7 +2582,7 @@ msgstr "" msgid "Tagged Items" msgstr "" -#: apps/operation/models.py:131 apps/operation/models.py:774 +#: apps/operation/models.py:131 apps/operation/models.py:787 msgid "Pinned" msgstr "" @@ -2576,7 +2594,7 @@ msgstr "" msgid "Announcements" msgstr "" -#: apps/operation/models.py:161 apps/operation/models.py:555 +#: apps/operation/models.py:161 apps/operation/models.py:568 msgid "Read at" msgstr "" @@ -2624,180 +2642,180 @@ msgstr "" msgid "Attachment" msgstr "" -#: apps/operation/models.py:365 apps/operation/models.py:438 -#: apps/operation/models.py:777 +#: apps/operation/models.py:378 apps/operation/models.py:451 +#: apps/operation/models.py:790 msgid "Writer" msgstr "" -#: apps/operation/models.py:372 apps/operation/models.py:436 +#: apps/operation/models.py:385 apps/operation/models.py:449 #: apps/warehouse/models.py:109 msgid "Inquiry" msgstr "" -#: apps/operation/models.py:373 +#: apps/operation/models.py:386 msgid "Inquiries" msgstr "" -#: apps/operation/models.py:442 +#: apps/operation/models.py:455 msgid "Inquiry Response" msgstr "" -#: apps/operation/models.py:443 +#: apps/operation/models.py:456 msgid "Inquiry Responses" msgstr "" -#: apps/operation/models.py:467 +#: apps/operation/models.py:480 msgid "Review" msgstr "" -#: apps/operation/models.py:468 apps/operation/models.py:754 +#: apps/operation/models.py:481 apps/operation/models.py:767 msgid "Closed" msgstr "" -#: apps/operation/models.py:472 +#: apps/operation/models.py:485 msgid "Question Type" msgstr "" -#: apps/operation/models.py:473 +#: apps/operation/models.py:486 msgid "Question ID" msgstr "" -#: apps/operation/models.py:477 +#: apps/operation/models.py:490 msgid "Grade Appeal" msgstr "" -#: apps/operation/models.py:478 +#: apps/operation/models.py:491 msgid "Grade Appeals" msgstr "" -#: apps/operation/models.py:534 apps/partner/models.py:71 +#: apps/operation/models.py:547 apps/partner/models.py:71 #: apps/partner/models.py:85 msgid "Group" msgstr "" -#: apps/operation/models.py:539 +#: apps/operation/models.py:552 msgid "Messages" msgstr "" -#: apps/operation/models.py:558 +#: apps/operation/models.py:571 msgid "Message Read" msgstr "" -#: apps/operation/models.py:559 +#: apps/operation/models.py:572 msgid "Message Reads" msgstr "" -#: apps/operation/models.py:604 +#: apps/operation/models.py:617 msgid "Platform" msgstr "" -#: apps/operation/models.py:605 +#: apps/operation/models.py:618 msgid "Device Name" msgstr "" -#: apps/operation/models.py:618 +#: apps/operation/models.py:631 msgid "Cookie Policy" msgstr "" -#: apps/operation/models.py:619 +#: apps/operation/models.py:632 msgid "Marketing Policy" msgstr "" -#: apps/operation/models.py:620 +#: apps/operation/models.py:633 msgid "Data Retention Policy" msgstr "" -#: apps/operation/models.py:622 apps/store/models.py:100 +#: apps/operation/models.py:635 apps/store/models.py:100 #: apps/tracking/admin.py:35 msgid "Kind" msgstr "" -#: apps/operation/models.py:626 apps/survey/models.py:62 +#: apps/operation/models.py:639 apps/survey/models.py:62 msgid "Mandatory" msgstr "" -#: apps/operation/models.py:627 +#: apps/operation/models.py:640 msgid "Priority" msgstr "" -#: apps/operation/models.py:630 apps/operation/models.py:684 +#: apps/operation/models.py:643 apps/operation/models.py:697 msgid "Policy" msgstr "" -#: apps/operation/models.py:631 +#: apps/operation/models.py:644 msgid "Policies" msgstr "" -#: apps/operation/models.py:686 +#: apps/operation/models.py:699 msgid "Data Category" msgstr "" -#: apps/operation/models.py:687 +#: apps/operation/models.py:700 msgid "Version" msgstr "" -#: apps/operation/models.py:688 +#: apps/operation/models.py:701 msgid "Effective Date" msgstr "" -#: apps/operation/models.py:691 apps/operation/models.py:719 +#: apps/operation/models.py:704 apps/operation/models.py:732 msgid "Policy Version" msgstr "" -#: apps/operation/models.py:692 +#: apps/operation/models.py:705 msgid "Policy Versions" msgstr "" -#: apps/operation/models.py:720 +#: apps/operation/models.py:733 msgid "Accepted" msgstr "" -#: apps/operation/models.py:723 +#: apps/operation/models.py:736 msgid "Policy Agreement" msgstr "" -#: apps/operation/models.py:724 +#: apps/operation/models.py:737 msgid "Policy Agreements" msgstr "" -#: apps/operation/models.py:747 +#: apps/operation/models.py:760 msgid "Subject Type" msgstr "" -#: apps/operation/models.py:748 +#: apps/operation/models.py:761 msgid "Subject ID" msgstr "" -#: apps/operation/models.py:750 +#: apps/operation/models.py:763 msgid "Comment Count" msgstr "" -#: apps/operation/models.py:751 +#: apps/operation/models.py:764 msgid "Rating Count" msgstr "" -#: apps/operation/models.py:752 +#: apps/operation/models.py:765 msgid "Rating Sum" msgstr "" -#: apps/operation/models.py:753 +#: apps/operation/models.py:766 msgid "Rating Average" msgstr "" -#: apps/operation/models.py:758 apps/operation/models.py:771 +#: apps/operation/models.py:771 apps/operation/models.py:784 msgid "Thread" msgstr "" -#: apps/operation/models.py:759 +#: apps/operation/models.py:772 msgid "Threads" msgstr "" -#: apps/operation/models.py:773 apps/operation/models.py:780 +#: apps/operation/models.py:786 apps/operation/models.py:793 #: apps/warehouse/models.py:112 msgid "Comment" msgstr "" -#: apps/operation/models.py:781 +#: apps/operation/models.py:794 msgid "Comments" msgstr "" @@ -3207,11 +3225,11 @@ msgid "Refunds" msgstr "" #: apps/studio/admin.py:12 -msgid "Draft History" +msgid "Editing History" msgstr "" #: apps/studio/admin.py:13 -msgid "Draft Histories" +msgid "Editing Histories" msgstr "" #: apps/studio/models.py:15 @@ -3231,11 +3249,11 @@ msgid "Content type" msgstr "" #: apps/studio/models.py:24 -msgid "Content Draft" +msgid "Content Editing" msgstr "" #: apps/studio/models.py:25 -msgid "Content Drafts" +msgid "Content Editings" msgstr "" #: apps/survey/models.py:80 diff --git a/core/locale/ko/LC_MESSAGES/django.mo b/core/locale/ko/LC_MESSAGES/django.mo index 227c1a8..bf32a3e 100644 Binary files a/core/locale/ko/LC_MESSAGES/django.mo and b/core/locale/ko/LC_MESSAGES/django.mo differ diff --git a/core/locale/ko/LC_MESSAGES/django.po b/core/locale/ko/LC_MESSAGES/django.po index 74533d8..a368cee 100644 --- a/core/locale/ko/LC_MESSAGES/django.po +++ b/core/locale/ko/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-03-04 00:39+0900\n" +"POT-Creation-Date: 2026-03-07 13:30+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -227,8 +227,8 @@ msgstr "" msgid "Email" msgstr "이메일" -#: apps/account/models.py:114 apps/assignment/models.py:531 -#: apps/assignment/models.py:549 apps/assignment/models.py:568 +#: apps/account/models.py:114 apps/assignment/models.py:524 +#: apps/assignment/models.py:542 apps/assignment/models.py:561 #: apps/competency/certificate.py:274 apps/competency/models.py:61 #: apps/competency/models.py:102 apps/competency/models.py:122 #: apps/competency/models.py:140 apps/competency/models.py:170 @@ -272,8 +272,8 @@ msgstr "환경설정" #: apps/common/models.py:153 apps/competency/models.py:173 #: apps/competency/models.py:241 apps/learning/models.py:80 #: apps/learning/models.py:335 apps/operation/models.py:176 -#: apps/operation/models.py:224 apps/operation/models.py:606 -#: apps/operation/models.py:625 apps/store/models.py:107 +#: apps/operation/models.py:224 apps/operation/models.py:619 +#: apps/operation/models.py:638 apps/store/models.py:107 #: apps/store/models.py:148 msgid "Active" msgstr "활성" @@ -292,8 +292,8 @@ msgstr "최고 관리자" #: apps/competency/models.py:139 apps/content/models.py:308 #: apps/content/models.py:476 apps/learning/models.py:79 #: apps/learning/models.py:497 apps/operation/models.py:159 -#: apps/operation/models.py:531 apps/operation/models.py:602 -#: apps/operation/models.py:718 apps/partner/admin.py:56 +#: apps/operation/models.py:544 apps/operation/models.py:615 +#: apps/operation/models.py:731 apps/partner/admin.py:56 #: apps/partner/models.py:96 apps/partner/models.py:211 apps/sso/models.py:29 #: apps/sso/models.py:49 apps/store/models.py:147 msgid "User" @@ -334,7 +334,7 @@ msgid "Temporary Passwords" msgstr "임시 비밀번호" #: apps/account/models.py:561 apps/account/models.py:567 -#: apps/account/models.py:573 apps/operation/models.py:603 +#: apps/account/models.py:573 apps/operation/models.py:616 msgid "Token" msgstr "토큰" @@ -426,7 +426,7 @@ msgstr "© %(year)s %(platform_name)s. 모든 권리 보유." #: apps/course/templates/course/mail/grade_completed_email.html:50 #: apps/course/templates/course/mail/start_today_email.html:52 #: apps/course/templates/course/mail/weekly_progress_email.html:51 -#: apps/operation/models.py:617 +#: apps/operation/models.py:630 msgid "Privacy Policy" msgstr "개인정보처리방침" @@ -440,11 +440,11 @@ msgstr "개인정보처리방침" #: apps/course/templates/course/mail/grade_completed_email.html:51 #: apps/course/templates/course/mail/start_today_email.html:53 #: apps/course/templates/course/mail/weekly_progress_email.html:52 -#: apps/operation/models.py:616 +#: apps/operation/models.py:629 msgid "Terms of Service" msgstr "이용약관" -#: apps/assignment/admin.py:118 apps/assignment/admin.py:119 +#: apps/assignment/admin.py:107 apps/assignment/admin.py:108 #: apps/discussion/admin.py:69 apps/discussion/admin.py:70 #: apps/operation/admin.py:103 apps/operation/admin.py:104 #: apps/operation/admin.py:150 apps/operation/admin.py:151 @@ -452,43 +452,43 @@ msgstr "이용약관" msgid "Attachments" msgstr "첨부파일" -#: apps/assignment/admin.py:123 apps/exam/admin.py:103 +#: apps/assignment/admin.py:112 apps/exam/admin.py:103 msgid "Submission History" msgstr "제출 기록" -#: apps/assignment/admin.py:124 apps/exam/admin.py:104 +#: apps/assignment/admin.py:113 apps/exam/admin.py:104 msgid "Submission Histories" msgstr "제출 기록" -#: apps/assignment/admin.py:152 apps/discussion/admin.py:97 +#: apps/assignment/admin.py:141 apps/discussion/admin.py:97 #: apps/exam/admin.py:123 msgid "Grading History" msgstr "채점 기록" -#: apps/assignment/admin.py:153 apps/discussion/admin.py:98 +#: apps/assignment/admin.py:142 apps/discussion/admin.py:98 #: apps/exam/admin.py:124 msgid "Grading Histories" msgstr "채점 기록" -#: apps/assignment/admin.py:159 apps/assignment/models.py:442 +#: apps/assignment/admin.py:148 apps/assignment/models.py:435 #: apps/course/admin.py:132 apps/course/admin.py:149 #: apps/discussion/admin.py:103 apps/discussion/models.py:434 #: apps/exam/admin.py:133 apps/exam/models.py:395 apps/quiz/models.py:360 msgid "Grade" msgstr "채점" -#: apps/assignment/admin.py:180 apps/common/models.py:163 +#: apps/assignment/admin.py:166 apps/common/models.py:163 #: apps/discussion/admin.py:122 apps/exam/admin.py:152 msgid "Earned Details" msgstr "득점 세부사항" -#: apps/assignment/admin.py:185 apps/common/models.py:168 +#: apps/assignment/admin.py:171 apps/common/models.py:168 #: apps/discussion/admin.py:132 apps/exam/admin.py:157 msgid "Feedback" msgstr "피드백" -#: apps/assignment/apps.py:8 apps/assignment/models.py:192 -#: apps/assignment/models.py:276 apps/warehouse/models.py:144 +#: apps/assignment/apps.py:8 apps/assignment/models.py:131 +#: apps/assignment/models.py:268 apps/warehouse/models.py:144 #: apps/warehouse/views.py:53 msgid "Assignment" msgstr "과제" @@ -497,26 +497,26 @@ msgstr "과제" #: apps/common/models.py:121 apps/course/models.py:90 #: apps/discussion/models.py:79 apps/discussion/models.py:325 #: apps/exam/models.py:76 apps/operation/models.py:128 -#: apps/operation/models.py:192 apps/operation/models.py:363 -#: apps/operation/models.py:532 apps/operation/models.py:623 -#: apps/operation/models.py:745 apps/quiz/models.py:72 apps/survey/models.py:37 +#: apps/operation/models.py:192 apps/operation/models.py:376 +#: apps/operation/models.py:545 apps/operation/models.py:636 +#: apps/operation/models.py:758 apps/quiz/models.py:72 apps/survey/models.py:37 msgid "Title" msgstr "제목" -#: apps/assignment/models.py:82 apps/assignment/models.py:532 -#: apps/assignment/models.py:550 apps/assignment/models.py:569 +#: apps/assignment/models.py:82 apps/assignment/models.py:525 +#: apps/assignment/models.py:543 apps/assignment/models.py:562 #: apps/common/models.py:122 apps/competency/models.py:141 #: apps/competency/models.py:171 apps/competency/models.py:237 #: apps/course/models.py:91 apps/discussion/models.py:80 apps/exam/models.py:77 #: apps/learning/models.py:333 apps/operation/models.py:206 -#: apps/operation/models.py:624 apps/operation/models.py:746 +#: apps/operation/models.py:637 apps/operation/models.py:759 #: apps/partner/models.py:44 apps/partner/models.py:67 #: apps/partner/models.py:177 apps/quiz/models.py:73 apps/store/models.py:56 #: apps/store/models.py:103 apps/survey/models.py:38 msgid "Description" msgstr "설명" -#: apps/assignment/models.py:83 apps/assignment/models.py:187 +#: apps/assignment/models.py:83 apps/assignment/models.py:124 #: apps/content/models.py:83 apps/course/models.py:115 #: apps/discussion/models.py:81 apps/discussion/models.py:128 #: apps/exam/models.py:78 apps/exam/models.py:147 apps/operation/models.py:239 @@ -526,7 +526,7 @@ msgid "Owner" msgstr "소유자" #: apps/assignment/models.py:86 apps/assignment/models.py:106 -#: apps/assignment/models.py:189 apps/discussion/models.py:84 +#: apps/assignment/models.py:126 apps/discussion/models.py:84 #: apps/discussion/models.py:104 apps/discussion/models.py:130 #: apps/exam/models.py:82 apps/exam/models.py:110 apps/exam/models.py:149 #: apps/quiz/models.py:78 apps/quiz/models.py:94 apps/quiz/models.py:127 @@ -539,12 +539,11 @@ msgstr "문제 은행" msgid "Question Pools" msgstr "문제 은행" -#: apps/assignment/models.py:107 apps/assignment/models.py:115 -#: apps/assignment/models.py:133 apps/assignment/models.py:278 -#: apps/discussion/models.py:114 apps/discussion/models.py:204 -#: apps/exam/admin.py:76 apps/exam/models.py:112 apps/exam/models.py:118 -#: apps/exam/models.py:135 apps/operation/admin.py:131 -#: apps/operation/models.py:222 apps/operation/models.py:364 +#: apps/assignment/models.py:107 apps/assignment/models.py:114 +#: apps/assignment/models.py:270 apps/discussion/models.py:114 +#: apps/discussion/models.py:204 apps/exam/admin.py:76 apps/exam/models.py:112 +#: apps/exam/models.py:118 apps/exam/models.py:135 apps/operation/admin.py:131 +#: apps/operation/models.py:222 apps/operation/models.py:377 #: apps/quiz/admin.py:50 apps/quiz/models.py:95 apps/quiz/models.py:101 #: apps/quiz/models.py:115 apps/survey/models.py:59 apps/survey/models.py:67 msgid "Question" @@ -564,64 +563,48 @@ msgid "Attachment File Types" msgstr "첨부파일 형식" #: apps/assignment/models.py:111 -msgid "Sample Attachment" -msgstr "샘플 첨부파일" - -#: apps/assignment/models.py:112 msgid "Plagiarism Threshold Percentage" msgstr "표절 유사도 기준" -#: apps/assignment/models.py:116 apps/discussion/models.py:115 +#: apps/assignment/models.py:115 apps/discussion/models.py:115 #: apps/exam/admin.py:77 apps/exam/models.py:119 apps/exam/models.py:242 #: apps/quiz/admin.py:51 apps/quiz/models.py:102 apps/quiz/models.py:243 #: apps/survey/models.py:68 msgid "Questions" msgstr "문제" -#: apps/assignment/models.py:134 apps/assignment/models.py:535 -#: apps/assignment/models.py:548 -msgid "Rubric" -msgstr "평가 척도" - -#: apps/assignment/models.py:135 apps/exam/models.py:138 -#: apps/operation/admin.py:158 apps/operation/models.py:466 -#: apps/quiz/models.py:117 -msgid "Explanation" -msgstr "해설" - -#: apps/assignment/models.py:138 apps/exam/models.py:141 -#: apps/quiz/models.py:120 -msgid "Solution" -msgstr "해답" - -#: apps/assignment/models.py:139 apps/exam/models.py:142 -#: apps/quiz/models.py:121 -msgid "Solutions" -msgstr "해답" - -#: apps/assignment/models.py:188 apps/course/models.py:122 +#: apps/assignment/models.py:125 apps/course/models.py:122 #: apps/discussion/models.py:129 apps/exam/models.py:148 -#: apps/operation/models.py:196 apps/operation/tests/factories.py:61 +#: apps/operation/models.py:196 msgid "Honor Code" msgstr "윤리 서약" -#: apps/assignment/models.py:193 +#: apps/assignment/models.py:127 apps/assignment/models.py:528 +#: apps/assignment/models.py:541 +msgid "Rubric" +msgstr "평가 척도" + +#: apps/assignment/models.py:128 +msgid "Sample Attachment" +msgstr "샘플 첨부파일" + +#: apps/assignment/models.py:132 msgid "Assignments" msgstr "과제" -#: apps/assignment/models.py:277 apps/course/models.py:571 +#: apps/assignment/models.py:269 apps/course/models.py:571 #: apps/discussion/models.py:203 apps/exam/models.py:241 -#: apps/operation/models.py:465 apps/quiz/models.py:242 +#: apps/operation/models.py:478 apps/quiz/models.py:242 msgid "Learner" msgstr "학습자" -#: apps/assignment/models.py:279 apps/discussion/models.py:205 +#: apps/assignment/models.py:271 apps/discussion/models.py:205 #: apps/exam/models.py:243 apps/quiz/models.py:244 msgid "Retry" msgstr "재응시" -#: apps/assignment/models.py:282 apps/assignment/models.py:405 -#: apps/assignment/models.py:438 apps/assignment/models.py:494 +#: apps/assignment/models.py:274 apps/assignment/models.py:398 +#: apps/assignment/models.py:431 apps/assignment/models.py:487 #: apps/discussion/models.py:208 apps/discussion/models.py:323 #: apps/discussion/models.py:430 apps/exam/models.py:246 #: apps/exam/models.py:365 apps/exam/models.py:375 apps/exam/models.py:391 @@ -629,114 +612,114 @@ msgstr "재응시" msgid "Attempt" msgstr "응시" -#: apps/assignment/models.py:283 apps/discussion/models.py:209 +#: apps/assignment/models.py:275 apps/discussion/models.py:209 #: apps/exam/models.py:247 apps/quiz/models.py:248 msgid "Attempts" msgstr "응시" -#: apps/assignment/models.py:406 apps/operation/models.py:223 -#: apps/operation/models.py:437 +#: apps/assignment/models.py:399 apps/operation/models.py:223 +#: apps/operation/models.py:450 msgid "Answer" msgstr "답변" -#: apps/assignment/models.py:407 +#: apps/assignment/models.py:400 msgid "Extracted Text" msgstr "추출된 텍스트" -#: apps/assignment/models.py:410 apps/exam/models.py:379 +#: apps/assignment/models.py:403 apps/exam/models.py:379 #: apps/quiz/models.py:351 apps/survey/models.py:124 msgid "Submission" msgstr "제출" -#: apps/assignment/models.py:411 apps/exam/models.py:380 +#: apps/assignment/models.py:404 apps/exam/models.py:380 #: apps/quiz/models.py:352 apps/survey/models.py:125 msgid "Submissions" msgstr "제출" -#: apps/assignment/models.py:439 apps/course/models.py:778 +#: apps/assignment/models.py:432 apps/course/models.py:778 #: apps/discussion/models.py:431 apps/exam/models.py:392 msgid "Grader" msgstr "채점자" -#: apps/assignment/models.py:443 apps/discussion/models.py:435 +#: apps/assignment/models.py:436 apps/discussion/models.py:435 #: apps/exam/models.py:396 apps/quiz/models.py:361 msgid "Grades" msgstr "채점" -#: apps/assignment/models.py:489 +#: apps/assignment/models.py:482 msgid "Not Detected" msgstr "없음" -#: apps/assignment/models.py:490 +#: apps/assignment/models.py:483 msgid "Detected" msgstr "탐지됨" -#: apps/assignment/models.py:491 +#: apps/assignment/models.py:484 msgid "Excused" msgstr "면제됨" -#: apps/assignment/models.py:492 +#: apps/assignment/models.py:485 msgid "Not Resolved" msgstr "알 수 없음" -#: apps/assignment/models.py:495 apps/store/models.py:54 +#: apps/assignment/models.py:488 apps/store/models.py:54 #: apps/store/models.py:184 apps/store/models.py:361 apps/store/models.py:381 msgid "Status" msgstr "상태" -#: apps/assignment/models.py:496 +#: apps/assignment/models.py:489 msgid "Similarity Percentage" msgstr "유사도" -#: apps/assignment/models.py:497 +#: apps/assignment/models.py:490 msgid "Flagged Text" msgstr "검토 텍스트" -#: apps/assignment/models.py:498 +#: apps/assignment/models.py:491 msgid "Source Text" msgstr "소스 텍스트" -#: apps/assignment/models.py:499 +#: apps/assignment/models.py:492 msgid "Source User ID" msgstr "소스 사용자 ID" -#: apps/assignment/models.py:500 apps/store/models.py:385 +#: apps/assignment/models.py:493 apps/store/models.py:385 msgid "Reason" msgstr "사유" -#: apps/assignment/models.py:503 +#: apps/assignment/models.py:496 msgid "Plagiarism Check" msgstr "표절 검사" -#: apps/assignment/models.py:504 +#: apps/assignment/models.py:497 msgid "Plagiarism Checks" msgstr "표절 검사" -#: apps/assignment/models.py:536 +#: apps/assignment/models.py:529 msgid "Rubrics" msgstr "평가 척도" -#: apps/assignment/models.py:553 +#: apps/assignment/models.py:546 msgid "Rubric Criterion" msgstr "평가 항목" -#: apps/assignment/models.py:554 +#: apps/assignment/models.py:547 msgid "Rubric Criteria" msgstr "평가 항목" -#: apps/assignment/models.py:567 +#: apps/assignment/models.py:560 msgid "Criterion" msgstr "평가 기준" -#: apps/assignment/models.py:570 apps/exam/models.py:115 apps/quiz/models.py:98 +#: apps/assignment/models.py:563 apps/exam/models.py:115 apps/quiz/models.py:98 msgid "Point" msgstr "배점" -#: apps/assignment/models.py:573 +#: apps/assignment/models.py:566 msgid "Performance Level" msgstr "성취 수준" -#: apps/assignment/models.py:574 +#: apps/assignment/models.py:567 msgid "Performance Levels" msgstr "성취 수준" @@ -779,8 +762,8 @@ msgstr "대화" msgid "Chats" msgstr "대화" -#: apps/assistant/models.py:64 apps/operation/models.py:538 -#: apps/operation/models.py:554 apps/warehouse/models.py:111 +#: apps/assistant/models.py:64 apps/operation/models.py:551 +#: apps/operation/models.py:567 apps/warehouse/models.py:111 msgid "Message" msgstr "메시지" @@ -788,8 +771,8 @@ msgstr "메시지" msgid "Response" msgstr "응답" -#: apps/assistant/models.py:66 apps/operation/models.py:369 -#: apps/operation/models.py:469 apps/operation/models.py:755 +#: apps/assistant/models.py:66 apps/operation/models.py:382 +#: apps/operation/models.py:482 apps/operation/models.py:768 msgid "Path" msgstr "경로" @@ -802,7 +785,7 @@ msgstr "완료됨" msgid "Bookmarked" msgstr "북마크" -#: apps/assistant/models.py:69 apps/operation/models.py:776 +#: apps/assistant/models.py:69 apps/operation/models.py:789 msgid "Rating" msgstr "평점" @@ -859,7 +842,7 @@ msgid "ordering_group must be defined in subclass" msgstr "서브클래스에서 ordering_group를 정의해야 합니다." #: apps/common/models.py:112 apps/operation/models.py:240 -#: apps/operation/models.py:775 +#: apps/operation/models.py:788 msgid "Deleted" msgstr "삭제됨" @@ -1556,18 +1539,18 @@ msgstr "수신 주소" msgid "PDF" msgstr "PDF" -#: apps/competency/models.py:299 apps/operation/models.py:535 +#: apps/competency/models.py:299 apps/operation/models.py:548 #: apps/tracking/models.py:29 msgid "Data" msgstr "데이터" #: apps/competency/models.py:304 apps/learning/models.py:88 -#: apps/learning/models.py:471 apps/operation/models.py:366 +#: apps/learning/models.py:471 apps/operation/models.py:379 msgid "Content Type" msgstr "콘텐츠 유형" #: apps/competency/models.py:305 apps/learning/models.py:91 -#: apps/learning/models.py:474 apps/operation/models.py:367 +#: apps/learning/models.py:474 apps/operation/models.py:380 #: apps/studio/models.py:20 msgid "Content ID" msgstr "콘텐츠 ID" @@ -1736,8 +1719,8 @@ msgid "Public Access Medias" msgstr "공개 접근" #: apps/content/models.py:257 apps/discussion/models.py:326 -#: apps/operation/models.py:129 apps/operation/models.py:533 -#: apps/operation/models.py:685 +#: apps/operation/models.py:129 apps/operation/models.py:546 +#: apps/operation/models.py:698 msgid "Body" msgstr "본문" @@ -2406,7 +2389,7 @@ msgstr "댓글 최소 글자수" msgid "Discussions" msgstr "토론" -#: apps/discussion/models.py:324 apps/operation/models.py:772 +#: apps/discussion/models.py:324 apps/operation/models.py:785 msgid "Parent" msgstr "부모" @@ -2451,6 +2434,19 @@ msgstr "정답" msgid "Correct Criteria" msgstr "정답 기준" +#: apps/exam/models.py:138 apps/operation/admin.py:158 +#: apps/operation/models.py:479 apps/quiz/models.py:117 +msgid "Explanation" +msgstr "해설" + +#: apps/exam/models.py:141 apps/quiz/models.py:120 +msgid "Solution" +msgstr "해답" + +#: apps/exam/models.py:142 apps/quiz/models.py:121 +msgid "Solutions" +msgstr "해답" + #: apps/exam/models.py:154 msgid "Exams" msgstr "시험" @@ -2585,7 +2581,7 @@ msgstr "그룹 카탈로그" msgid "Cohort Catalogs" msgstr "그룹 카탈로그" -#: apps/operation/admin.py:121 apps/operation/models.py:439 +#: apps/operation/admin.py:121 apps/operation/models.py:452 msgid "Solved" msgstr "해결됨" @@ -2601,6 +2597,28 @@ msgstr "동의 기록" msgid "Operation" msgstr "운영" +#: apps/operation/management/commands/setup_base_operation_data.py:41 +msgid "Student Honor Code" +msgstr "준수 사항" + +#: apps/operation/management/commands/setup_base_operation_data.py:46 +msgid "Frequently Asked Questions" +msgstr "자주 묻는 질문" + +#: apps/operation/management/commands/setup_base_operation_data.py:46 +msgid "default faq" +msgstr "자주 묻는 질문" + +#: apps/operation/management/commands/setup_base_operation_data.py:52 +#, python-format +msgid "Write FAQ question %d here" +msgstr "여기에 FAQ 질문 %d 작성" + +#: apps/operation/management/commands/setup_base_operation_data.py:52 +#, python-format +msgid "Write FAQ answer %d here" +msgstr "여기에 FAQ 답변 %d 작성" + #: apps/operation/models.py:95 apps/operation/models.py:102 msgid "Tag" msgstr "태그" @@ -2621,7 +2639,7 @@ msgstr "태그된 항목" msgid "Tagged Items" msgstr "태그된 항목" -#: apps/operation/models.py:131 apps/operation/models.py:774 +#: apps/operation/models.py:131 apps/operation/models.py:787 msgid "Pinned" msgstr "고정됨" @@ -2633,7 +2651,7 @@ msgstr "공지사항" msgid "Announcements" msgstr "공지사항" -#: apps/operation/models.py:161 apps/operation/models.py:555 +#: apps/operation/models.py:161 apps/operation/models.py:568 msgid "Read at" msgstr "읽음" @@ -2681,180 +2699,180 @@ msgstr "MIME 타입" msgid "Attachment" msgstr "첨부파일" -#: apps/operation/models.py:365 apps/operation/models.py:438 -#: apps/operation/models.py:777 +#: apps/operation/models.py:378 apps/operation/models.py:451 +#: apps/operation/models.py:790 msgid "Writer" msgstr "작성자" -#: apps/operation/models.py:372 apps/operation/models.py:436 +#: apps/operation/models.py:385 apps/operation/models.py:449 #: apps/warehouse/models.py:109 msgid "Inquiry" msgstr "1:1 문의" -#: apps/operation/models.py:373 +#: apps/operation/models.py:386 msgid "Inquiries" msgstr "1:1 문의" -#: apps/operation/models.py:442 +#: apps/operation/models.py:455 msgid "Inquiry Response" msgstr "문의 응답" -#: apps/operation/models.py:443 +#: apps/operation/models.py:456 msgid "Inquiry Responses" msgstr "문의 응답" -#: apps/operation/models.py:467 +#: apps/operation/models.py:480 msgid "Review" msgstr "검토" -#: apps/operation/models.py:468 apps/operation/models.py:754 +#: apps/operation/models.py:481 apps/operation/models.py:767 msgid "Closed" msgstr "종료" -#: apps/operation/models.py:472 +#: apps/operation/models.py:485 msgid "Question Type" msgstr "문제 유형" -#: apps/operation/models.py:473 +#: apps/operation/models.py:486 msgid "Question ID" msgstr "문제 ID" -#: apps/operation/models.py:477 +#: apps/operation/models.py:490 msgid "Grade Appeal" msgstr "채점 이의" -#: apps/operation/models.py:478 +#: apps/operation/models.py:491 msgid "Grade Appeals" msgstr "채점 이의" -#: apps/operation/models.py:534 apps/partner/models.py:71 +#: apps/operation/models.py:547 apps/partner/models.py:71 #: apps/partner/models.py:85 msgid "Group" msgstr "그룹" -#: apps/operation/models.py:539 +#: apps/operation/models.py:552 msgid "Messages" msgstr "메시지" -#: apps/operation/models.py:558 +#: apps/operation/models.py:571 msgid "Message Read" msgstr "메시지 읽음" -#: apps/operation/models.py:559 +#: apps/operation/models.py:572 msgid "Message Reads" msgstr "메시지 읽음" -#: apps/operation/models.py:604 +#: apps/operation/models.py:617 msgid "Platform" msgstr "플랫폼" -#: apps/operation/models.py:605 +#: apps/operation/models.py:618 msgid "Device Name" msgstr "기기 이름" -#: apps/operation/models.py:618 +#: apps/operation/models.py:631 msgid "Cookie Policy" msgstr "쿠키 정책" -#: apps/operation/models.py:619 +#: apps/operation/models.py:632 msgid "Marketing Policy" msgstr "마케팅 정책" -#: apps/operation/models.py:620 +#: apps/operation/models.py:633 msgid "Data Retention Policy" msgstr "데이터 보존 정책" -#: apps/operation/models.py:622 apps/store/models.py:100 +#: apps/operation/models.py:635 apps/store/models.py:100 #: apps/tracking/admin.py:35 msgid "Kind" msgstr "유형" -#: apps/operation/models.py:626 apps/survey/models.py:62 +#: apps/operation/models.py:639 apps/survey/models.py:62 msgid "Mandatory" msgstr "필수" -#: apps/operation/models.py:627 +#: apps/operation/models.py:640 msgid "Priority" msgstr "우선순위" -#: apps/operation/models.py:630 apps/operation/models.py:684 +#: apps/operation/models.py:643 apps/operation/models.py:697 msgid "Policy" msgstr "정책" -#: apps/operation/models.py:631 +#: apps/operation/models.py:644 msgid "Policies" msgstr "정책" -#: apps/operation/models.py:686 +#: apps/operation/models.py:699 msgid "Data Category" msgstr "데이터 카테고리" -#: apps/operation/models.py:687 +#: apps/operation/models.py:700 msgid "Version" msgstr "버전" -#: apps/operation/models.py:688 +#: apps/operation/models.py:701 msgid "Effective Date" msgstr "시행일" -#: apps/operation/models.py:691 apps/operation/models.py:719 +#: apps/operation/models.py:704 apps/operation/models.py:732 msgid "Policy Version" msgstr "정책 버전" -#: apps/operation/models.py:692 +#: apps/operation/models.py:705 msgid "Policy Versions" msgstr "정책 버전" -#: apps/operation/models.py:720 +#: apps/operation/models.py:733 msgid "Accepted" msgstr "동의함" -#: apps/operation/models.py:723 +#: apps/operation/models.py:736 msgid "Policy Agreement" msgstr "정책 동의" -#: apps/operation/models.py:724 +#: apps/operation/models.py:737 msgid "Policy Agreements" msgstr "정책 동의" -#: apps/operation/models.py:747 +#: apps/operation/models.py:760 msgid "Subject Type" msgstr "주제 유형" -#: apps/operation/models.py:748 +#: apps/operation/models.py:761 msgid "Subject ID" msgstr "주제 ID" -#: apps/operation/models.py:750 +#: apps/operation/models.py:763 msgid "Comment Count" msgstr "댓글 수" -#: apps/operation/models.py:751 +#: apps/operation/models.py:764 msgid "Rating Count" msgstr "평점 수" -#: apps/operation/models.py:752 +#: apps/operation/models.py:765 msgid "Rating Sum" msgstr "평점 합계" -#: apps/operation/models.py:753 +#: apps/operation/models.py:766 msgid "Rating Average" msgstr "평점 평균" -#: apps/operation/models.py:758 apps/operation/models.py:771 +#: apps/operation/models.py:771 apps/operation/models.py:784 msgid "Thread" msgstr "스레드" -#: apps/operation/models.py:759 +#: apps/operation/models.py:772 msgid "Threads" msgstr "스레드" -#: apps/operation/models.py:773 apps/operation/models.py:780 +#: apps/operation/models.py:786 apps/operation/models.py:793 #: apps/warehouse/models.py:112 msgid "Comment" msgstr "댓글" -#: apps/operation/models.py:781 +#: apps/operation/models.py:794 msgid "Comments" msgstr "댓글" @@ -3267,11 +3285,11 @@ msgid "Refunds" msgstr "환불" #: apps/studio/admin.py:12 -msgid "Draft History" +msgid "Editing History" msgstr "수정 기록" #: apps/studio/admin.py:13 -msgid "Draft Histories" +msgid "Editing Histories" msgstr "수정 기록" #: apps/studio/models.py:15 @@ -3291,12 +3309,12 @@ msgid "Content type" msgstr "콘텐츠 유형" #: apps/studio/models.py:24 -msgid "Content Draft" -msgstr "콘텐츠 초안" +msgid "Content Editing" +msgstr "콘텐츠 수정" #: apps/studio/models.py:25 -msgid "Content Drafts" -msgstr "콘텐츠 초안" +msgid "Content Editings" +msgstr "콘텐츠 수정" #: apps/survey/models.py:80 msgid "Complete Message" @@ -3534,6 +3552,15 @@ msgstr "과정 수료" msgid "Storage" msgstr "저장소" +#~ msgid "Draft History" +#~ msgstr "수정 기록" + +#~ msgid "Draft Histories" +#~ msgstr "수정 기록" + +#~ msgid "Content Draft" +#~ msgstr "콘텐츠 초안" + #~ msgid "Attempt Start" #~ msgstr "응시 시작" diff --git a/web/package-lock.json b/web/package-lock.json index 78a7947..a31a75a 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -74,7 +74,7 @@ "version": "4.4.4", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@asamuzakjp/css-color": { @@ -160,7 +160,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -429,7 +428,6 @@ "integrity": "sha512-QnHe81PMslpy3mnpL8DnO2M4S4ZnYPkjlGCLWBZT/3R9M6b5daArWMMtEfP52/n174RKnwRIf3oT8+wc9ihSfQ==", "dev": true, "license": "MIT OR Apache-2.0", - "peer": true, "bin": { "biome": "bin/biome" }, @@ -825,7 +823,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -866,7 +863,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } @@ -1368,7 +1364,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.14.9.tgz", "integrity": "sha512-3gtUX0e584MYkKBQMgSECMvE1Dwzg+eONefDQ0wxVSe5YMBsZwdN5pL7UapwWBlV8+i8QCztF9TP947tEjZAGA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/component": "0.7.1", "@firebase/logger": "0.5.0", @@ -1435,7 +1430,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.9.tgz", "integrity": "sha512-e5LzqjO69/N2z7XcJeuMzIp4wWnW696dQeaHAUpQvGk89gIWHAIvG6W+mA3UotGW6jBoqdppEJ9DnuwbcBByug==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/app": "0.14.9", "@firebase/component": "0.7.1", @@ -1451,8 +1445,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@firebase/auth": { "version": "1.12.1", @@ -1903,7 +1896,6 @@ "integrity": "sha512-/gnejm7MKkVIXnSJGpc9L2CvvvzJvtDPeAEq5jAwgVlf/PeNxot+THx/bpD20wQ8uL5sz0xqgXy1nisOYMU+mw==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" }, @@ -4156,7 +4148,7 @@ "version": "6.9.1", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.4.0", @@ -4176,7 +4168,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/@tiptap/core": { @@ -4184,7 +4176,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.20.0.tgz", "integrity": "sha512-aC9aROgia/SpJqhsXFiX9TsligL8d+oeoI8W3u00WI45s0VfsqjgeKQLDLF7Tu7hC+7F02teC84SAHuup003VQ==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4415,7 +4406,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.20.0.tgz", "integrity": "sha512-+V0/gsVWAv+7vcY0MAe6D52LYTIicMSHw00wz3ISZgprSb2yQhJ4+4gurOnUrQ4Du3AnRQvxPROaofwxIQ66WQ==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4587,7 +4577,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.20.0.tgz", "integrity": "sha512-HIsXX942w3nbxEQBlMAAR/aa6qiMBEP7CsSMxaxmTIVAmW35p6yUASw6GdV1u0o3lCZjXq2OSRMTskzIqi5uLg==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4602,7 +4591,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.20.0.tgz", "integrity": "sha512-jn+2KnQZn+b+VXr8EFOJKsnjVNaA4diAEr6FOazupMt8W8ro1hfpYtZ25JL87Kao/WbMze55sd8M8BDXLUKu1A==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", @@ -4998,7 +4986,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "dequal": "^2.0.3" @@ -5189,7 +5177,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5603,7 +5590,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/cssesc": { @@ -5778,7 +5765,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=6" @@ -6536,7 +6523,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.28.4" }, @@ -6692,7 +6678,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -7492,7 +7478,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=4" @@ -8105,7 +8091,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", "license": "MIT", - "peer": true, "dependencies": { "orderedmap": "^2.0.0" } @@ -8135,7 +8120,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0", @@ -8184,7 +8168,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.4.tgz", "integrity": "sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", @@ -8263,7 +8246,6 @@ "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "devOptional": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -8344,7 +8326,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "indent-string": "^4.0.0", @@ -8540,7 +8522,6 @@ "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.0.tgz", "integrity": "sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -8605,7 +8586,6 @@ "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.11.tgz", "integrity": "sha512-WEJtcc5mkh/BnHA6Yrg4whlF8g6QwpmXXRg4P2ztPmcKeHHlH4+djYecBLhSpecZY2RRECXYUwIc/C2r3yzQ4Q==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.0", @@ -8722,7 +8702,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "min-indent": "^1.0.0" @@ -8742,8 +8722,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.1.tgz", "integrity": "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/tapable": { "version": "2.3.0", @@ -8825,7 +8804,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8943,7 +8921,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9056,7 +9033,6 @@ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -9087,7 +9063,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -9176,7 +9151,6 @@ "integrity": "sha512-Yr1dQybmtDtDAHkii6hXuc1oVH9CPcS/Zb2jN/P36qqcrkNnVPsMTzQ06jyzFPFjj3U1IYKMVt/9ZqcwGCEbjw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", @@ -9218,7 +9192,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/web/src/locale/en/translation.json b/web/src/locale/en/translation.json index fcef32b..d838623 100644 --- a/web/src/locale/en/translation.json +++ b/web/src/locale/en/translation.json @@ -1,5 +1,6 @@ { "(current device)": "(current device)", + "(Optional)": "(Optional)", "[DELETED]": "[DELETED]", "{{count}} comment_one": "{{count}} comment", "{{count}} comment_other": "{{count}} comments", @@ -28,9 +29,15 @@ "Achievement": "Achievement", "Activate account": "Activate account", "Activation email has been sent to your email address. Please check your email and click on the link to activate your account.": "Activation email has been sent to your email address. Please check your email and click on the link to activate your account.", - "active": "active", "Active": "Active", "Actual size": "Actual size", + "Add assessment": "Add assessment", + "Add category": "Add category", + "Add certificate": "Add certificate", + "Add instructor": "Add instructor", + "Add media lesson": "Add media lesson", + "Add related course": "Add related course", + "Add survey": "Add survey", "Additional information about this goal.": "Additional information about this goal.", "After the end of the period, the course will not be accessible.": "After the end of the period, the course will not be accessible.", "After this period ends, enrolled content will no longer be accessible.": "After this period ends, enrolled content will no longer be accessible.", @@ -41,7 +48,6 @@ "Align left": "Align left", "Align right": "Align right", "All": "All", - "All faq items already exist": "All faq items already exist", "All time": "All time", "Already have an account?": "Already have an account?", "and more...": "and more...", @@ -79,6 +85,7 @@ "Assistant Error": "Assistant Error", "Assistant Note": "Assistant Note", "at least 0": "at least 0", + "at least 1": "at least 1", "Attachment {{date}}": "Attachment {{date}}", "Attachment file count": "Attachment file count", "Attachment file count must be {{num}}, current count: {{num2}}": "Attachment file count must be {{num}}, current count: {{num2}}", @@ -117,7 +124,6 @@ "Cohort member {{count}}_other": "Cohort members {{count}}", "Cohort: {{cohort}}": "Cohort: {{cohort}}", "comma separated characters": "comma separated characters", - "Comma separated quiz ids": "Comma separated quiz ids", "Communication": "Communication", "Competency goals": "Competency goals", "Complete message": "Complete message", @@ -134,7 +140,8 @@ "Content Count": "Content Count", "Content must be at least {{num1}} characters. Current length: {{num2}}": "Content must be at least {{num1}} characters. Current length: {{num2}}", "Content will be read only after the period": "Content will be read only after the period", - "Copy of {{title}}": "Copy of {{title}}", + "Copy question pool": "Copy question pool", + "Copy rubric criteria": "Copy rubric criteria", "Copyright © {{yearText}} - All right reserved by {{PLATFORM_NAME}}.": "Copyright © {{yearText}} - All right reserved by {{PLATFORM_NAME}}.", "Correct answer": "Correct answer", "Correct Answer": "Correct Answer", @@ -150,8 +157,11 @@ "Created": "Created", "Created at {{date}}": "Created at {{date}}", "Criterion": "Criterion", + "Criterion {{num}} description": "Criterion {{num}} description", + "Criterion {{num}} name": "Criterion {{num}} name", "Crop image": "Crop image", "D-{{days}}": "D-{{days}}", + "Delete": "Delete", "Description": "Description", "Detailed level descriptions are available after the grading completion.": "Detailed level descriptions are available after the grading completion.", "Directive": "Directive", @@ -201,17 +211,12 @@ "Exam questions": "Exam questions", "Exam time is still running. Submit before {{duration}} elapses from the start {{start}}.": "Exam time is still running. Submit before {{duration}} elapses from the start {{start}}.", "Exam time over": "Exam time over", - "exam-question": "exam-question", "Explanation": "Explanation", "Explanation must be at least {{num1}} characters. Current length: {{num2}}": "Explanation must be at least {{num1}} characters. Current length: {{num2}}", - "Export": "Export", "Export all questions": "Export all questions", "Export data to CSV": "Export data to CSV", "Failed": "Failed", "FAQ": "FAQ", - "FAQ description": "FAQ description", - "Faq Items": "Faq Items", - "FAQ name": "FAQ name", "Featured": "Featured", "Feedback": "Feedback", "File Attachment": "File Attachment", @@ -255,15 +260,13 @@ "Heading 3": "Heading 3", "Highest Score": "Highest Score", "Highlight": "Highlight", - "Honor code content": "Honor code content", - "Honor code title": "Honor code title", + "Honor code": "Honor code", "I have read and agree to the this code.": "I have read and agree to the this code.", "I have read and agree to the this honor code.": "I have read and agree to the this honor code.", "If you deactivate it, you will no longer receive notifications.": "If you deactivate it, you will no longer receive notifications.", "If you delete it, the device information will be permanently removed.": "If you delete it, the device information will be permanently removed.", "If you refresh or close the browser, all data will be cleared.": "If you refresh or close the browser, all data will be cleared.", "Immediately after response": "Immediately after response", - "Import": "Import", "Import data from CSV": "Import data from CSV", "Import failed": "Import failed", "Import questions": "Import questions", @@ -322,8 +325,8 @@ "My Profile": "My Profile", "My score": "My score", "Name": "Name", + "New": "New", "New Chat": "New Chat", - "New content": "New content", "New email": "New email", "New password": "New password", "Next": "Next", @@ -373,6 +376,8 @@ "Passwords do not match": "Passwords do not match", "Pdf": "PDF", "Pending": "Pending", + "Performance level {{num}} description": "Performance level {{num}} description", + "Performance level {{num}} name": "Performance level {{num}} name", "Period": "Period", "Personal": "Personal", "Phone": "Phone", @@ -410,11 +415,11 @@ "Question Breakdown": "Question Breakdown", "Question Count": "Question Count", "Question must be at least {{num1}} characters. Current length: {{num2}}": "Question must be at least {{num1}} characters. Current length: {{num2}}", - "Question pool description": "Question pool description", - "Questions & Answers": "Questions & Answers", + "Question select count": "Question select count", "Quiz": "Quiz", "Quiz {{num}}": "Quiz {{num}}", "Quiz questions": "Quiz questions", + "Quizzes": "Quizzes", "Read at {{date}}": "Read at {{date}}", "Real Name is required to issue certificates.": "Real Name is required to issue certificates.", "Recent Chats": "Recent Chats", @@ -440,6 +445,7 @@ "Retry": "Retry", "Review": "Review", "Reviewed": "Reviewed", + "Rubric criteria": "Rubric criteria", "Sample attachment": "Sample attachment", "Save": "Save", "Save all questions": "Save all questions", @@ -461,7 +467,6 @@ "Select All": "Select All", "Select competency category": "Select competency category", "Select content": "Select content", - "Select count": "Select count", "Select Language": "Select Language", "Select the skills you want to learn. Skill's highest level is 8.": "Select the skills you want to learn. Skill's highest level is 8.", "Send Message": "Send Message", @@ -496,7 +501,6 @@ "Subtitle Enabled": "Subtitle Enabled", "Supplement": "Supplement", "Survey": "Survey", - "Survey question pool description": "Survey question pool description", "Survey questions": "Survey questions", "Survey Target": "Survey Target", "Surveys": "Surveys", @@ -538,7 +542,6 @@ "Unpublished": "Unpublished", "Update": "Update", "URL": "URL", - "Validation error": "Validation error", "Verification email has been sent to your email address. Please check your email and click on the link to activate your account.": "Verification email has been sent to your email address. Please check your email and click on the link to activate your account.", "Verification email has been sent to your email address. Please check your email and click on the link to verify your account.": "Verification email has been sent to your email address. Please check your email and click on the link to verify your account.", "Verification required": "Verification required", diff --git a/web/src/locale/ko/translation.json b/web/src/locale/ko/translation.json index 9cc41b6..415bca6 100644 --- a/web/src/locale/ko/translation.json +++ b/web/src/locale/ko/translation.json @@ -1,5 +1,6 @@ { "(current device)": "(현재 기기)", + "(Optional)": "(Optional)", "[DELETED]": "[삭제됨]", "{{count}} comment_other": "{{count}} 댓글", "{{count}} learning object_other": "{{count}} 학습 콘텐츠", @@ -19,9 +20,15 @@ "Achievement": "성취", "Activate account": "계정 활성화", "Activation email has been sent to your email address. Please check your email and click on the link to activate your account.": "활성화 이메일이 발송되었습니다. 이메일을 확인하고 링크를 클릭하여 계정을 활성화하세요.", - "active": "활성", "Active": "활성", "Actual size": "실제 크기", + "Add assessment": "과제 추가", + "Add category": "카테고리 추가", + "Add certificate": "수료증 추가", + "Add instructor": "강사 추가", + "Add media lesson": "미디어 강의 추가", + "Add related course": "관련 과정 추가", + "Add survey": "설문조사 추가", "Additional information about this goal.": "이 목표에 대한 추가 정보", "After the end of the period, the course will not be accessible.": "수강 기간 종료 후에는 코스에 접근할 수 없습니다.", "After this period ends, enrolled content will no longer be accessible.": "이 기간이 종료되면 등록된 콘텐츠에 더 이상 접근할 수 없습니다.", @@ -32,7 +39,6 @@ "Align left": "왼쪽 정렬", "Align right": "오른쪽 정렬", "All": "모두", - "All faq items already exist": "모든 FAQ 항목이 이미 존재합니다", "All time": "모두", "Already have an account?": "이미 계정이 있으신가요?", "and more...": "더 보기...", @@ -70,6 +76,7 @@ "Assistant Error": "도우미 오류", "Assistant Note": "도우미 메모", "at least 0": "0 이상", + "at least 1": "최소 1", "Attachment {{date}}": "첨부파일 {{date}}", "Attachment file count": "첨부파일 수", "Attachment file count must be {{num}}, current count: {{num2}}": "첨부 파일 수는 {{num}}개여야 합니다. 현재: {{num2}}개", @@ -107,7 +114,6 @@ "Cohort member {{count}}_other": "학습 그룹 멤버 {{count}}", "Cohort: {{cohort}}": "학습 그룹: {{cohort}}", "comma separated characters": "쉼표로 구분된 문자", - "Comma separated quiz ids": "쉼표로 구분된 퀴즈 ID", "Communication": "소통", "Competency goals": "역량 목표", "Complete message": "완료 메시지", @@ -123,7 +129,8 @@ "Content Count": "콘텐츠 수", "Content must be at least {{num1}} characters. Current length: {{num2}}": "내용은 최소 {{num1}}자 이상이어야 합니다. 현재 길이: {{num2}}자", "Content will be read only after the period": "내용은 기간 종료 후에만 확인 가능합니다.", - "Copy of {{title}}": "{{title}} 사본", + "Copy question pool": "문제 은행 복사", + "Copy rubric criteria": "평가 표 복사", "Copyright © {{yearText}} - All right reserved by {{PLATFORM_NAME}}.": "저작권 © {{yearText}} - {{PLATFORM_NAME}}. 모든 권리 보유.", "Correct answer": "정답", "Correct Answer": "정답", @@ -139,8 +146,11 @@ "Created": "생성됨", "Created at {{date}}": "{{date}} 생성됨", "Criterion": "기준", + "Criterion {{num}} description": "평가 항목 {{num}} 설명", + "Criterion {{num}} name": "평가 항목 {{num}} 이름", "Crop image": "이미지 자르기", "D-{{days}}": "D-{{days}}", + "Delete": "삭제", "Description": "설명", "Detailed level descriptions are available after the grading completion.": "상세 수준 설명은 채점 완료 후 확인 가능합니다.", "Directive": "지침", @@ -183,24 +193,19 @@ "Enter your account email address": "계정 이메일 주소 입력", "Enter your new password": "새 비밀번호 입력", "Error": "오류", - "essay": "essay", + "essay": "서술형", "Exam": "시험", "Exam Information": "시험 정보", "Exam Period": "시험 기간", "Exam questions": "시험 문제", "Exam time is still running. Submit before {{duration}} elapses from the start {{start}}.": "시험 시간이 진행 중입니다. {{start}} 시작 후 {{duration}} 이내에 제출하세요.", "Exam time over": "시험 종료", - "exam-question": "exam-question", "Explanation": "설명", "Explanation must be at least {{num1}} characters. Current length: {{num2}}": "설명은 최소 {{num1}}자 이상이어야 합니다. 현재 길이: {{num2}}자", - "Export": "내보내기", "Export all questions": "모든 문제 내보내기", "Export data to CSV": "CSV로 내보내기", "Failed": "실패", "FAQ": "FAQ", - "FAQ description": "FAQ 설명", - "Faq Items": "FAQ 항목", - "FAQ name": "FAQ 이름", "Featured": "추천", "Feedback": "피드백", "File Attachment": "파일 첨부", @@ -243,15 +248,13 @@ "Heading 3": "제목 3", "Highest Score": "최고 점수", "Highlight": "강조", - "Honor code content": "준수 사항 내용", - "Honor code title": "준수 사항 제목", + "Honor code": "준수 사항", "I have read and agree to the this code.": "이 규정을 읽고 동의합니다.", "I have read and agree to the this honor code.": "준수 사항을 읽고 동의합니다.", "If you deactivate it, you will no longer receive notifications.": "비활성화하면 알림을 더 이상 받지 않습니다.", "If you delete it, the device information will be permanently removed.": "삭제하면 기기 정보가 영구적으로 제거됩니다.", "If you refresh or close the browser, all data will be cleared.": "새로고침 또는 브라우저를 닫으면 모든 데이터가 사라집니다.", "Immediately after response": "응답 후 즉시", - "Import": "가져오기", "Import data from CSV": "CSV에서 가져오기", "Import failed": "가져오기 실패", "Import questions": "문제 가져오기", @@ -309,8 +312,8 @@ "My Profile": "내 프로필", "My score": "내 점수", "Name": "이름", + "New": "새로 만들기", "New Chat": "새 채팅", - "New content": "새 콘텐츠", "New email": "새 이메일", "New password": "새 비밀번호", "Next": "다음", @@ -342,7 +345,7 @@ "Only the number of posts that meet the evaluation criteria is included in the total count.": "평가기준에 맞는 게시글 수만 총 수에 포함됩니다.", "Open": "열기", "Opening soon": "곧 시작됩니다", - "Option {{num}}": "Option {{num}}", + "Option {{num}}": "옵션 {{num}}", "optional": "선택 사항", "Optional": "선택 사항", "Or Search competency categories directly": "또는 역량 카테고리를 직접 검색", @@ -360,6 +363,8 @@ "Passwords do not match": "비밀번호가 일치하지 않습니다", "Pdf": "PDF", "Pending": "대기중", + "Performance level {{num}} description": "성취 수준 {{num}} 설명", + "Performance level {{num}} name": "성취 수준 {{num}} 이름", "Period": "기간", "Personal": "개인", "Phone": "전화", @@ -397,11 +402,11 @@ "Question Breakdown": "문제 분석", "Question Count": "문제 수", "Question must be at least {{num1}} characters. Current length: {{num2}}": "문제 내용은 최소 {{num1}}자 이상이어야 합니다. 현재 길이: {{num2}}자", - "Question pool description": "문제 풀 설명", - "Questions & Answers": "질문 & 답변", + "Question select count": "문제 선택 수", "Quiz": "퀴즈", "Quiz {{num}}": "퀴즈 {{num}}", "Quiz questions": "퀴즈 문제", + "Quizzes": "퀴즈", "Read at {{date}}": "{{date}} 읽음", "Real Name is required to issue certificates.": "수료증 발급을 위해 실명이 필요합니다.", "Recent Chats": "최근 채팅", @@ -427,6 +432,7 @@ "Retry": "다시 시도", "Review": "검토", "Reviewed": "검토 완료", + "Rubric criteria": "평가 표", "Sample attachment": "샘플 첨부파일", "Save": "저장", "Save all questions": "모든 문제 저장", @@ -448,7 +454,6 @@ "Select All": "전체 선택", "Select competency category": "역량 카테고리 선택", "Select content": "콘텐츠 선택", - "Select count": "선택 수", "Select Language": "언어 선택", "Select the skills you want to learn. Skill's highest level is 8.": "학습하고 싶은 스킬을 선택하세요. 스킬의 최고 레벨은 8입니다.", "Send Message": "메시지 전송", @@ -456,7 +461,7 @@ "Set up OTP": "OTP 설정", "Setup OTP Authentication": "OTP 인증 설정", "Show results": "결과 보기", - "single_choice": "single_choice", + "single_choice": "단일 선택", "Site Policies": "사이트 정책", "Skill factors: {{num}}": "스킬 요소: {{num}}개", "Skills": "스킬", @@ -483,7 +488,6 @@ "Subtitle Enabled": "자막 켜기", "Supplement": "보충 자료", "Survey": "설문조사", - "Survey question pool description": "설문조사 문제 풀 설명", "Survey questions": "설문조사 문제", "Survey Target": "설문조사 대상", "Surveys": "설문조사 목록", @@ -491,7 +495,7 @@ "Task list": "과제 목록", "Team": "팀", "Text align": "텍스트 정렬", - "text_input": "text_input", + "text_input": "답단형", "Thank you for joining!": "참여해 주셔서 감사합니다!", "Thank you for your joining!": "참여해 주셔서 감사합니다!", "Thank you for your response!": "응답해 주셔서 감사합니다!", @@ -525,7 +529,6 @@ "Unpublished": "미게시", "Update": "변경하기", "URL": "URL", - "Validation error": "유효성 검사 오류", "Verification email has been sent to your email address. Please check your email and click on the link to activate your account.": "인증 이메일이 발송되었습니다. 이메일을 확인하고 링크를 클릭하여 계정을 활성화하세요.", "Verification email has been sent to your email address. Please check your email and click on the link to verify your account.": "인증 이메일이 발송되었습니다. 이메일을 확인하고 링크를 클릭하여 계정을 인증하세요.", "Verification required": "인증 필요", diff --git a/web/src/routes/studio/-exam/Question.tsx b/web/src/routes/studio/-exam/Question.tsx index 5e1f9d1..b8738ee 100644 --- a/web/src/routes/studio/-exam/Question.tsx +++ b/web/src/routes/studio/-exam/Question.tsx @@ -53,7 +53,7 @@ export const Question = (props: Props) => { } return ( - + {(status, actions) => (