diff --git a/core/migrations/0012_auto_20190306_1611.py b/core/migrations/0012_auto_20190306_1611.py new file mode 100644 index 0000000000000000000000000000000000000000..54f93ca92be48e5c48a814198dec8e77668a3eb8 --- /dev/null +++ b/core/migrations/0012_auto_20190306_1611.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.5 on 2019-03-06 16:11 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0011_auto_20181001_1259'), + ] + + operations = [ + migrations.AlterField( + model_name='studentinfo', + name='exam', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='students', to='core.ExamType'), + ), + ] diff --git a/core/models.py b/core/models.py index 1f942494dc85ea659f5396058a7104589aff2e07..224eca210c4242137dbbd5f905a7a336733bcfe2 100644 --- a/core/models.py +++ b/core/models.py @@ -280,9 +280,9 @@ class StudentInfo(models.Model): max_length=30, default=random_matrikel_no) exam = models.ForeignKey('ExamType', - on_delete=models.SET_NULL, + on_delete=models.CASCADE, related_name='students', - null=True) + null=False) user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, related_name='student') diff --git a/core/tests/test_access_rights.py b/core/tests/test_access_rights.py index 2d57498db8b6cbeb3772c99de2aec4606951a3e9..064687c088d5c678e6506de8a780bf3fcafcdf2e 100644 --- a/core/tests/test_access_rights.py +++ b/core/tests/test_access_rights.py @@ -5,7 +5,7 @@ from rest_framework.test import (APIRequestFactory, APITestCase, from core.views import (ExamApiViewSet, StudentReviewerApiViewSet, StudentSelfApiView, TutorApiViewSet) -from util.factories import GradyUserFactory +from util.factories import GradyUserFactory, make_exams class AccessRightsOfStudentAPIViewTests(APITestCase): @@ -18,7 +18,12 @@ class AccessRightsOfStudentAPIViewTests(APITestCase): cls.user_factory = GradyUserFactory() def setUp(self): - self.student = self.user_factory.make_student() + self.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + self.student = self.user_factory.make_student(exam=self.exam) self.tutor = self.user_factory.make_tutor() self.reviewer = self.user_factory.make_reviewer() self.request = self.factory.get(reverse('student-page')) @@ -53,7 +58,12 @@ class AccessRightsOfTutorAPIViewTests(APITestCase): cls.user_factory = GradyUserFactory() def setUp(self): - self.student = self.user_factory.make_student() + self.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + self.student = self.user_factory.make_student(exam=self.exam) self.tutor = self.user_factory.make_tutor() self.reviewer = self.user_factory.make_reviewer() self.request = self.factory.get(reverse('tutor-list')) @@ -89,7 +99,12 @@ class AccessRightsOfStudentReviewerAPIViewTest(APITestCase): cls.user_factory = GradyUserFactory() def setUp(self): - self.student = self.user_factory.make_student() + self.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + self.student = self.user_factory.make_student(exam=self.exam) self.tutor = self.user_factory.make_tutor() self.reviewer = self.user_factory.make_reviewer() self.request = self.factory.get(reverse('student-list')) @@ -127,7 +142,12 @@ class AccessRightsOfExamTypeAPIViewTest(APITestCase): cls.user_factory = GradyUserFactory() def setUp(self): - self.student = self.user_factory.make_student() + self.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + self.student = self.user_factory.make_student(exam=self.exam) self.tutor = self.user_factory.make_tutor() self.reviewer = self.user_factory.make_reviewer() self.request = self.factory.get(reverse('examtype-list')) diff --git a/core/tests/test_commands.py b/core/tests/test_commands.py index 97b3ea81afff6876610a63e1f9d3eb2e54903c72..e313c5a3c96d84f3d66a3b8feb2137cda48e9bb0 100644 --- a/core/tests/test_commands.py +++ b/core/tests/test_commands.py @@ -5,7 +5,7 @@ from django.contrib.auth import get_user_model from django.core.management import call_command from django.test import TestCase -from util.factories import GradyUserFactory +from util.factories import GradyUserFactory, make_exams class CommandsTestCase(TestCase): @@ -22,7 +22,12 @@ class CommandsTestCase(TestCase): self.assertFalse(someone.is_active) def test_replaceusernames(self): - self.factory.make_student(identifier=88884444, username='before') + self.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + self.factory.make_student(identifier=88884444, username='before', exam=self.exam) with tempfile.NamedTemporaryFile() as matno2username: matno2username.write(json.dumps({'88884444': 'after'}).encode()) diff --git a/core/tests/test_factory.py b/core/tests/test_factory.py index 5b5828901c33fcf9f2393e48c76bef4f1a884686..c5bfc6971051be98615e7cf74a92c16ee7b67354 100644 --- a/core/tests/test_factory.py +++ b/core/tests/test_factory.py @@ -2,7 +2,7 @@ from django.test import TestCase from core import models from core.models import StudentInfo -from util.factories import GradyUserFactory +from util.factories import GradyUserFactory, make_exams class FactoryTestCase(TestCase): @@ -10,11 +10,15 @@ class FactoryTestCase(TestCase): factory = GradyUserFactory() def test_make_student(self): - - user = self.factory.make_student() + self.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + user = self.factory.make_student(exam=self.exam) self.assertEqual(StudentInfo.objects.count(), 1) - self.assertEqual(user.student.exam, None) + self.assertEqual(user.student.exam.module_reference, "Test Exam 01") self.assertEqual(len(str(user.student.matrikel_no)), 8) def test_can_create_reviewer(self): @@ -30,9 +34,19 @@ class FactoryTestCase(TestCase): models.UserAccount.objects.all()) def test_can_create_student_user(self): - self.assertIn(self.factory.make_student(), + self.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + self.assertIn(self.factory.make_student(exam=self.exam), models.UserAccount.objects.all()) def test_can_create_student_info(self): - self.assertIn(self.factory.make_student().student, + self.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + self.assertIn(self.factory.make_student(exam=self.exam).student, StudentInfo.objects.all()) diff --git a/core/tests/test_feedback.py b/core/tests/test_feedback.py index 6677eb066caa509bdab25fac4c937d055fe0ad52..d82dcca27e1c6121d4f7fd28ceb5f47fc611d119 100644 --- a/core/tests/test_feedback.py +++ b/core/tests/test_feedback.py @@ -5,7 +5,7 @@ from rest_framework.test import APIRequestFactory, APITestCase from core import models from core.models import Feedback, FeedbackComment, Submission, SubmissionType -from util.factories import GradyUserFactory, make_test_data +from util.factories import GradyUserFactory, make_test_data, make_exams class FeedbackRetrieveTestCase(APITestCase): @@ -17,7 +17,12 @@ class FeedbackRetrieveTestCase(APITestCase): def setUpTestData(cls): cls.score = 23 cls.tutor = cls.factory.make_tutor() - cls.student = cls.factory.make_student() + cls.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + cls.student = cls.factory.make_student(exam=cls.exam) cls.reviewer = cls.factory.make_reviewer() cls.tutors = [cls.tutor, cls.reviewer] cls.request_factory = APIRequestFactory() @@ -91,7 +96,12 @@ class FeedbackCreateTestCase(APITestCase): cls.url = '/api/feedback/' cls.user_factory = GradyUserFactory() cls.tutor = cls.user_factory.make_tutor(password='p') - cls.student = cls.user_factory.make_student() + cls.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + cls.student = cls.user_factory.make_student(exam=cls.exam) cls.submission_type = SubmissionType.objects.create( name='Cooking some crystal with Jesse', full_score=100 @@ -282,6 +292,11 @@ class FeedbackPatchTestCase(APITestCase): def setUpTestData(cls): cls.burl = '/api/feedback/' cls.data = make_test_data({ + 'exams': [{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }], 'submission_types': [ { 'name': '01. Sort this or that', @@ -290,7 +305,10 @@ class FeedbackPatchTestCase(APITestCase): 'solution': 'Trivial!' }], 'students': [ - {'username': 'student01'} + { + 'username': 'student01', + 'exam': 'Test Exam 01' + } ], 'tutors': [ {'username': 'tutor01'}, @@ -395,6 +413,11 @@ class FeedbackCommentApiEndpointTest(APITestCase): def setUpTestData(cls): cls.burl = '/api/feedback/' cls.data = make_test_data({ + 'exams': [{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }], 'submission_types': [ { 'name': '01. Sort this or that', @@ -403,7 +426,10 @@ class FeedbackCommentApiEndpointTest(APITestCase): 'solution': 'Trivial!' }], 'students': [ - {'username': 'student01'} + { + 'username': 'student01', + 'exam': 'Test Exam 01' + } ], 'tutors': [ {'username': 'tutor01'}, diff --git a/core/tests/test_functional_views.py b/core/tests/test_functional_views.py index 7763b5c63f990a6e727c34f512f6014c6ce400e6..1cee60f1159fed175516cf705680f98a88d99228 100644 --- a/core/tests/test_functional_views.py +++ b/core/tests/test_functional_views.py @@ -3,7 +3,7 @@ from rest_framework.test import (APIRequestFactory, APITestCase, force_authenticate) from core.views import get_user_role -from util.factories import GradyUserFactory +from util.factories import GradyUserFactory, make_exams class GetUserRoleTest(APITestCase): @@ -11,7 +11,12 @@ class GetUserRoleTest(APITestCase): def setUpTestData(cls): cls.factory = APIRequestFactory() cls.user_factory = GradyUserFactory() - cls.student = cls.user_factory.make_student() + cls.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + cls.student = cls.user_factory.make_student(exam=cls.exam) cls.tutor = cls.user_factory.make_tutor() cls.reviewer = cls.user_factory.make_reviewer() diff --git a/core/tests/test_student_page.py b/core/tests/test_student_page.py index 41800def5a6680ab1e317a92f0351d433eef38b9..d6c6f97ffac95664337df1b07638b2480048406e 100644 --- a/core/tests/test_student_page.py +++ b/core/tests/test_student_page.py @@ -130,6 +130,11 @@ class StudentSelfSubmissionsTests(APITestCase): def setUp(self): self.test_data = make_test_data(data_dict={ + 'exams': [{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }], 'submission_types': [{ 'name': 'problem01', 'full_score': 10, @@ -138,6 +143,7 @@ class StudentSelfSubmissionsTests(APITestCase): }], 'students': [{ 'username': 'user01', + 'exam': 'Test Exam 01' }], 'tutors': [ { diff --git a/core/tests/test_subscription_assignment_service.py b/core/tests/test_subscription_assignment_service.py index ca576ae9ad6bf9b1f87aabc9d53bd82de5a96802..410dfca75c81b364ef3b4e813251a774daa33a65 100644 --- a/core/tests/test_subscription_assignment_service.py +++ b/core/tests/test_subscription_assignment_service.py @@ -4,7 +4,7 @@ from rest_framework.test import APIClient, APITestCase from core import models from core.models import (Submission, SubmissionSubscription, SubmissionType, SubscriptionEnded, SubscriptionTemporarilyEnded, TutorSubmissionAssignment) -from util.factories import GradyUserFactory, make_test_data +from util.factories import GradyUserFactory, make_test_data, make_exams class SubmissionSubscriptionRandomTest(APITestCase): @@ -15,8 +15,13 @@ class SubmissionSubscriptionRandomTest(APITestCase): def setUp(self): self.t = self.user_factory.make_tutor() - self.s1 = self.user_factory.make_student() - self.s2 = self.user_factory.make_student() + self.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + self.s1 = self.user_factory.make_student(exam=self.exam) + self.s2 = self.user_factory.make_student(exam=self.exam) self.submission_type = SubmissionType.objects.create( name='submission_01', full_score=14) @@ -97,6 +102,11 @@ class TestApiEndpoints(APITestCase): @classmethod def setUpTestData(cls): cls.data = make_test_data(data_dict={ + 'exams': [{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }], 'submission_types': [ { 'name': '01. Sort this or that', @@ -112,8 +122,14 @@ class TestApiEndpoints(APITestCase): } ], 'students': [ - {'username': 'student01'}, - {'username': 'student02'} + { + 'username': 'student01', + 'exam': 'Test Exam 01' + }, + { + 'username': 'student02', + 'exam': 'Test Exam 01' + } ], 'tutors': [ {'username': 'tutor01'}, diff --git a/core/tests/test_tutor_api_endpoints.py b/core/tests/test_tutor_api_endpoints.py index 210012369bab6fbb0f005005e7a62571dcbde861..689edad516d892208699c667d7bf9521fada4817 100644 --- a/core/tests/test_tutor_api_endpoints.py +++ b/core/tests/test_tutor_api_endpoints.py @@ -56,14 +56,25 @@ class TutorListTests(APITestCase): view = TutorApiViewSet.as_view({'get': 'list'}) data = make_test_data(data_dict={ + 'exams': [{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }], 'submission_types': [{ 'name': '01. Sort this or that', 'full_score': 35, 'description': 'Very complicated', 'solution': 'Trivial!'}], 'students': [ - {'username': 'student01'}, - {'username': 'student02'} + { + 'username': 'student01', + 'exam': 'Test Exam 01' + }, + { + 'username': 'student02', + 'exam': 'Test Exam 01' + } ], 'tutors': [ {'username': 'tutor01'}, diff --git a/core/tests/test_user_account_views.py b/core/tests/test_user_account_views.py index bc9e50c0ff7850a187b5d50c6ce2eaf001858755..dbcfec719ba2de8b3d5e74ecda6455bec7955be3 100644 --- a/core/tests/test_user_account_views.py +++ b/core/tests/test_user_account_views.py @@ -1,7 +1,7 @@ from rest_framework import status from rest_framework.test import (APIClient, APITestCase) -from util.factories import GradyUserFactory +from util.factories import GradyUserFactory, make_exams class TutorReviewerCanChangePasswordTests(APITestCase): @@ -75,7 +75,12 @@ class TutorReviewerCanChangePasswordTests(APITestCase): self.assertTrue(ret) def test_student_cant_change_password(self): - student = self.user_factory.make_student(password='l') + self.exam = make_exams(exams=[{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }])[0] + student = self.user_factory.make_student(password='l', exam=self.exam) res = self._change_password(student) self.assertEqual(status.HTTP_403_FORBIDDEN, res.status_code) ret = self.client.login(username=student.username, diff --git a/functional_tests/test_login_page.py b/functional_tests/test_login_page.py index ede4aee70b2326cabbbd47fa5788ddc75bd01339..11917939ad80bacb8331ff97c5a3c91905cdfcb2 100644 --- a/functional_tests/test_login_page.py +++ b/functional_tests/test_login_page.py @@ -24,6 +24,11 @@ class LoginPageTest(LiveServerTestCase): def setUp(self): self.test_data = make_test_data(data_dict={ + 'exams': [{ + 'module_reference': 'Test Exam 01', + 'total_score': 100, + 'pass_score': 60, + }], 'submission_types': [ { 'name': '01. Sort this or that', @@ -39,8 +44,16 @@ class LoginPageTest(LiveServerTestCase): } ], 'students': [ - {'username': 'student01', 'password': 'p'}, - {'username': 'student02', 'password': 'p'} + { + 'username': 'student01', + 'password': 'p', + 'exam': 'Test Exam 01' + }, + { + 'username': 'student02', + 'password': 'p', + 'exam': 'Test Exam 01' + } ], 'tutors': [ {'username': 'tutor01', 'password': 'p'}, diff --git a/util/factories.py b/util/factories.py index 6fedf552984211459306f71ba6fcf8293c59429f..eb1a0411c806c5c2e4f5e52b28990e4d493b6ccc 100644 --- a/util/factories.py +++ b/util/factories.py @@ -94,11 +94,9 @@ class GradyUserFactory: """ Creates a student. Defaults can be passed via kwargs like in relation managers objects.update method. """ user = self._make_base_user(username, 'Student', **kwargs) - student_info = StudentInfo.objects.get_or_create(user=user)[0] + student_info = StudentInfo.objects.get_or_create(user=user, exam=exam)[0] if identifier: student_info.matrikel_no = identifier - if exam: - student_info.exam = exam student_info.save() return user diff --git a/util/importer.py b/util/importer.py index 6a99b6db38a02abbf34e5f725de953130b09f4ee..eb5c170336aa2b98bc3d60b5f22f4dcd95a7e19c 100644 --- a/util/importer.py +++ b/util/importer.py @@ -253,10 +253,9 @@ def do_load_submission_types(): def do_load_module_descriptions(): print(''' - This loader imports descriptions of modules in an exam. This step is purely - optional -- Grady works just fine without these information. If you want to - distinguish students within one instance or give information about the - grading type you should provide this info. + This loader imports descriptions of modules in an exam. This information + is used to distinguish students within one instance or give information + about the grading type. CSV file format: module_reference, total_score, pass_score, pass_only @@ -377,10 +376,11 @@ def do_load_submissions(): file = i('Get me the file with all the submissions', 'submissions.json', is_file=True) - exam_obj = {} - if ExamType.objects.all() and \ - i('Do you want to add module/exam information?', NO): + if not ExamType.objects.all(): + raise Exception('Modules need to be loaded before submissions.') + else: exam_query_set = ExamType.objects.all() + print('Please select the corresponding module') print('You have the following choices:\n') for j, exam_type in enumerate(exam_query_set): print(f'\t[{j}] {exam_type.module_reference}')