diff --git a/edx_exams/apps/core/admin.py b/edx_exams/apps/core/admin.py index 112a6011..3819ff3e 100644 --- a/edx_exams/apps/core/admin.py +++ b/edx_exams/apps/core/admin.py @@ -12,6 +12,7 @@ Exam, ExamAttempt, ProctoringProvider, + StudentAllowance, User ) @@ -103,3 +104,20 @@ class CourseStaffRoleAdmin(admin.ModelAdmin): list_filter = ('course_id',) search_fields = ('user__username', 'course_id') ordering = ('course_id',) + + +@admin.register(StudentAllowance) +class StudentAllowanceAdmin(admin.ModelAdmin): + """ Admin configuration for the Student Allowance model """ + list_display = ('username', 'course_id', 'exam_name', 'extra_time_mins') + search_fields = ('user__username', 'exam__course_id', 'exam__exam_name') + ordering = ('-modified',) + + def username(self, obj): + return obj.user.username + + def exam_name(self, obj): + return obj.exam.exam_name + + def course_id(self, obj): + return obj.exam.course_id diff --git a/edx_exams/apps/core/migrations/0026_studentallowance.py b/edx_exams/apps/core/migrations/0026_studentallowance.py new file mode 100644 index 00000000..ca19e834 --- /dev/null +++ b/edx_exams/apps/core/migrations/0026_studentallowance.py @@ -0,0 +1,33 @@ +# Generated by Django 4.2.13 on 2024-07-11 15:45 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import model_utils.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0025_proctoringprovider_org_key'), + ] + + operations = [ + migrations.CreateModel( + name='StudentAllowance', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')), + ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')), + ('extra_time_mins', models.PositiveIntegerField()), + ('exam', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.exam')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'student allowance', + 'db_table': 'exams_studentallowance', + 'unique_together': {('user', 'exam')}, + }, + ), + ] diff --git a/edx_exams/apps/core/models.py b/edx_exams/apps/core/models.py index 91d2db15..617e61c9 100644 --- a/edx_exams/apps/core/models.py +++ b/edx_exams/apps/core/models.py @@ -428,3 +428,22 @@ def _sync_exams_with_new_provider(cls, course_id, new_provider): exam.save() return len(exams) + + +class StudentAllowance(TimeStampedModel): + """ + Allowance for extra time in an exam + + .. no_pii: + """ + user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE) + + exam = models.ForeignKey(Exam, on_delete=models.CASCADE) + + extra_time_mins = models.PositiveIntegerField() + + class Meta: + """ Meta class for this Django model """ + db_table = 'exams_studentallowance' + verbose_name = 'student allowance' + unique_together = ('user', 'exam') diff --git a/edx_exams/apps/core/tests/test_models.py b/edx_exams/apps/core/tests/test_models.py index 9b7c0808..c708c0e0 100644 --- a/edx_exams/apps/core/tests/test_models.py +++ b/edx_exams/apps/core/tests/test_models.py @@ -4,11 +4,12 @@ from django_dynamic_fixture import G from social_django.models import UserSocialAuth -from edx_exams.apps.core.models import CourseExamConfiguration, Exam, User +from edx_exams.apps.core.models import CourseExamConfiguration, Exam, User, StudentAllowance from edx_exams.apps.core.test_utils.factories import ( CourseExamConfigurationFactory, ExamFactory, - ProctoringProviderFactory + ProctoringProviderFactory, + UserFactory, ) @@ -133,3 +134,20 @@ def test_create_or_update_new_config(self): new_config = CourseExamConfiguration.objects.get(course_id=other_course_id) self.assertEqual(new_config.provider, self.config.provider) self.assertEqual(new_config.escalation_email, self.escalation_email) + + +class StudentAllowanceTests(TestCase): + """ + StudentAllowance model tests. + """ + + def test_model(self): + """ + TODO: temporary test for the sake of coverage. + This can be removed once this model is actually used in app code. + """ + StudentAllowance.objects.create( + user=UserFactory(), + exam=ExamFactory(), + extra_time_mins=10, + )