diff --git a/core/serializers.py b/core/serializers.py index 5f9d7391d33587dd41af1100994ac4b6425ddf38..c04238896d9fc0dc71f96f82ccd7b2f29ec5c1b9 100644 --- a/core/serializers.py +++ b/core/serializers.py @@ -7,8 +7,8 @@ from rest_framework.validators import UniqueValidator from core import models from core.models import (ExamType, Feedback, GeneralTaskSubscription, - StudentInfo, Submission, SubmissionType, - TutorSubmissionAssignment) + StudentInfo, Submission, SubmissionType, Test, + TutorSubmissionAssignment, UserAccount) from util.factories import GradyUserFactory log = logging.getLogger(__name__) @@ -25,7 +25,20 @@ class CannotSetFirstFeedbackFinal(Exception): class DynamicFieldsModelSerializer(DynamicFieldsMixin, serializers.ModelSerializer): - pass + + def __init__(self, *args, **kwargs): + # Don't pass the 'fields' arg up to the superclass + fields = kwargs.pop('fields', None) + + # Instantiate the superclass normally + super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs) + + if fields is not None: + # Drop any fields that are not specified in the `fields` argument. + allowed = set(fields) + existing = set(self.fields.keys()) + for field_name in existing - allowed: + self.fields.pop(field_name) class ExamSerializer(DynamicFieldsModelSerializer): @@ -138,47 +151,67 @@ class FeedbackCommentSerializer(serializers.ModelSerializer): read_only_fields = ('created',) -class SubmissionTypeSerializer(DynamicFieldsModelSerializer): - fullScore = serializers.ReadOnlyField(source='full_score') - typeId = serializers.ReadOnlyField(source='id') +class TestSerializer(DynamicFieldsModelSerializer): + + class Meta: + model = Test + fields = ('name', 'label', 'annotation') + + +class SubmissionTypeListSerializer(DynamicFieldsModelSerializer): + fullScore = serializers.IntegerField(source='full_score') + + class Meta: + model = SubmissionType + fields = ('id', 'name', 'fullScore') + + +class SubmissionTypeSerializer(SubmissionTypeListSerializer): class Meta: model = SubmissionType - fields = ('typeId', 'name', 'fullScore', 'description', 'solution') + fields = ('id', 'name', 'fullScore', 'description', 'solution') class SubmissionSerializer(DynamicFieldsModelSerializer): - feedback = serializers.ReadOnlyField(source='feedback.text') - score = serializers.ReadOnlyField(source='feedback.score') - typeId = serializers.ReadOnlyField(source='type.id') - typeName = serializers.ReadOnlyField(source='type.name') - fullScore = serializers.ReadOnlyField(source='type.full_score') + type = SubmissionTypeSerializer() + feedback = FeedbackSerializer() + tests = TestSerializer(many=True) + + class Meta: + model = Submission + fields = ('type', 'text', 'feedback', 'tests') + + +class SubmissionListSerializer(DynamicFieldsModelSerializer): + type = SubmissionTypeListSerializer(fields=('id', 'name', 'fullScore')) + # TODO change this according to new feedback model + feedback = FeedbackSerializer(fields=('score',)) class Meta: model = Submission - fields = ('typeId', 'typeName', 'text', - 'feedback', 'score', 'fullScore') + fields = ('type', 'feedback') class StudentInfoSerializer(DynamicFieldsModelSerializer): name = serializers.ReadOnlyField(source='user.fullname') - user = serializers.ReadOnlyField(source='user.username') + matrikel_no = serializers.ReadOnlyField(source='user.matrikel_no') exam = ExamSerializer() - submissions = SubmissionSerializer(many=True) + submissions = SubmissionListSerializer(many=True) class Meta: model = StudentInfo - fields = ('name', 'user', 'exam', 'submissions') + fields = ('name', 'user', 'matrikel_no', 'exam', 'submissions') class SubmissionNoTextFieldsSerializer(DynamicFieldsModelSerializer): score = serializers.ReadOnlyField(source='feedback.score') - typeId = serializers.ReadOnlyField(source='type.id') + type = serializers.ReadOnlyField(source='type.name') fullScore = serializers.ReadOnlyField(source='type.full_score') class Meta: model = Submission - fields = ('typeId', 'score', 'fullScore') + fields = ('type', 'score', 'fullScore') class StudentInfoSerializerForListView(DynamicFieldsModelSerializer): @@ -196,13 +229,13 @@ class TutorSerializer(DynamicFieldsModelSerializer): feedback_count = serializers.IntegerField(source='get_feedback_count', read_only=True) - def create(self, validated_data) -> models.UserAccount: + def create(self, validated_data) -> UserAccount: log.info("Crating tutor from data %s", validated_data) return user_factory.make_tutor( username=validated_data['username']) class Meta: - model = models.UserAccount + model = UserAccount fields = ('username', 'feedback_count') diff --git a/core/tests/test_student_page.py b/core/tests/test_student_page.py index 169e794120498b0b3365bd9e55224a5c75bbc2f0..455dfd58f09271c184f244924c6dce94076cd998 100644 --- a/core/tests/test_student_page.py +++ b/core/tests/test_student_page.py @@ -49,7 +49,8 @@ class StudentPageTests(APITestCase): }] }) - self.student = self.test_data['students'][0].student + self.student = self.test_data['students'][0] + self.student_info = self.student.student self.tutor = self.test_data['tutors'][0] self.reviewer = self.test_data['reviewers'][0] self.submission = self.test_data['submissions'][0] @@ -57,7 +58,7 @@ class StudentPageTests(APITestCase): self.request = self.factory.get(reverse('student-page')) self.view = StudentSelfApiView.as_view() - force_authenticate(self.request, user=self.student.user) + force_authenticate(self.request, user=self.student) self.response = self.view(self.request) self.exam_obj = self.response.data['exam'] @@ -66,7 +67,7 @@ class StudentPageTests(APITestCase): def test_student_information_contains_name(self): self.assertEqual( - self.response.data['name'], self.student.user.fullname) + self.response.data['name'], self.student.fullname) def test_all_student_submissions_are_loded(self): self.assertEqual(len(self.submission_list), @@ -76,40 +77,40 @@ class StudentPageTests(APITestCase): def test_exam_data_contains_module_reference(self): self.assertEqual( self.exam_obj["module_reference"], - self.student.exam.module_reference) + self.student_info.exam.module_reference) def test_exam_data_contains_total_score(self): self.assertEqual( - self.exam_obj["total_score"], self.student.exam.total_score) + self.exam_obj["total_score"], self.student_info.exam.total_score) def test_exam_data_contains_pass_score(self): self.assertEqual( - self.exam_obj["pass_score"], self.student.exam.pass_score) + self.exam_obj["pass_score"], self.student_info.exam.pass_score) def test_exam_data_contains_pass_only_field(self): self.assertEqual( - self.exam_obj["pass_only"], self.student.exam.pass_only) + self.exam_obj["pass_only"], self.student_info.exam.pass_only) # Tests concerning submission data def test_a_student_submissions_contains_type_name(self): self.assertEqual( - self.submission_list_first_entry['typeName'], - self.student.submissions.first().type.name) + self.submission_list_first_entry['type']['name'], + self.student_info.submissions.first().type.name) def test_a_student_submissions_contains_type_id(self): self.assertEqual( - self.submission_list_first_entry['typeId'], - self.student.submissions.first().type.id) + self.submission_list_first_entry['type']['id'], + self.student_info.submissions.first().type.id) def test_submission_data_contains_full_score(self): self.assertEqual( - self.submission_list_first_entry['fullScore'], - self.student.submissions.first().type.full_score) + self.submission_list_first_entry['type']['fullScore'], + self.student_info.submissions.first().type.full_score) def test_submission_data_contains_feedback_score(self): self.assertEqual( - self.submission_list_first_entry['score'], - self.student.submissions.first().feedback.score) + self.submission_list_first_entry['feedback']['score'], + self.student_info.submissions.first().feedback.score) # We don't want a matriculation number here def test_matriculation_number_is_not_send(self): @@ -148,7 +149,8 @@ class StudentSelfSubmissionsTests(APITestCase): }] }) - self.student = self.test_data['students'][0].student + self.student = self.test_data['students'][0] + self.student_info = self.student.student self.tutor = self.test_data['tutors'][0] self.submission = self.test_data['submissions'][0] self.feedback = self.submission.feedback @@ -156,7 +158,7 @@ class StudentSelfSubmissionsTests(APITestCase): self.request = self.factory.get(reverse('student-submissions')) self.view = StudentSelfSubmissionsApiView.as_view() - force_authenticate(self.request, user=self.student.user) + force_authenticate(self.request, user=self.student) self.response = self.view(self.request) self.submission_list = self.response.data @@ -165,33 +167,38 @@ class StudentSelfSubmissionsTests(APITestCase): # Tests concerning submission data def test_a_student_submissions_contains_type_name(self): self.assertEqual( - self.submission_list_first_entry['typeName'], - self.student.submissions.first().type.name) + self.submission_list_first_entry['type']['name'], + self.student_info.submissions.first().type.name) def test_a_student_submissions_contains_type_id(self): self.assertEqual( - self.submission_list_first_entry['typeId'], - self.student.submissions.first().type.id) + self.submission_list_first_entry['type']['id'], + self.student_info.submissions.first().type.id) def test_submission_data_contains_full_score(self): self.assertEqual( - self.submission_list_first_entry['fullScore'], - self.student.submissions.first().type.full_score) + self.submission_list_first_entry['type']['fullScore'], + self.student_info.submissions.first().type.full_score) - def test_submission_data_contains_text(self): + def test_submission_data_contains_description(self): self.assertEqual( - self.submission_list_first_entry['text'], - self.student.submissions.first().text) + self.submission_list_first_entry['type']['description'], + self.student_info.submissions.first().type.description) - def test_submission_data_contains_feedback_score(self): + def test_submission_data_contains_solution(self): + self.assertEqual( + self.submission_list_first_entry['type']['solution'], + self.student_info.submissions.first().type.solution) + + def test_submission_data_contains_final_status(self): self.assertEqual( - self.submission_list_first_entry['score'], - self.student.submissions.first().feedback.score) + self.submission_list_first_entry['feedback']['isFinal'], + self.student_info.submissions.first().feedback.is_final) - def test_submission_data_contains_feedback_text(self): + def test_submission_data_contains_feedback_score(self): self.assertEqual( - self.submission_list_first_entry['feedback'], - self.student.submissions.first().feedback.text) + self.submission_list_first_entry['feedback']['score'], + self.student_info.submissions.first().feedback.score) # We don't want a matriculation number here def test_matriculation_number_is_not_send(self): diff --git a/core/tests/test_student_reviewer_viewset.py b/core/tests/test_student_reviewer_viewset.py index ab39d9840366d06db063d36e6d486e72e622e203..cffccfa3e8f80777200d39e10a7b94a01e5a2b7f 100644 --- a/core/tests/test_student_reviewer_viewset.py +++ b/core/tests/test_student_reviewer_viewset.py @@ -69,5 +69,6 @@ class StudentPageTests(APITestCase): self.response.data[0]['submissions'][0]['score']) def test_submissions_full_score_is_included(self): + print(self.response.data[0]['submissions'][0]) self.assertEqual(self.student.submissions.first().type.full_score, self.response.data[0]['submissions'][0]['fullScore']) diff --git a/core/tests/test_submissiontypeview.py b/core/tests/test_submissiontypeview.py index 5f35ffd7c2d8ceff99dc4d8f23c8a64a8d4e3e8b..713cdb9072c47c370c6d1bc12a3ba3c2e239fc56 100644 --- a/core/tests/test_submissiontypeview.py +++ b/core/tests/test_submissiontypeview.py @@ -62,7 +62,7 @@ class SubmissionTypeViewTestRetrieve(APITestCase): self.assertEqual(self.response.status_code, status.HTTP_200_OK) def test_get_id(self): - self.assertEqual(self.pk, self.response.data['typeId']) + self.assertEqual(self.pk, self.response.data['id']) def test_get_sumbission_type_name(self): self.assertEqual('Hard question', self.response.data['name']) diff --git a/util/factories.py b/util/factories.py index 9ea00f3bd8b16329ca2680e24f7d3aa13cfdcbbf..7e4711de1a89e06ef469deda8678fee20053e9f3 100644 --- a/util/factories.py +++ b/util/factories.py @@ -247,17 +247,13 @@ def init_test_instance(): ' asasxasx\n' ' lorem ipsum und so\n', 'type': '02. Merge this or that or maybe even this', - 'user': 'student01' - }, - { - 'text': 'function blabl\n' - ' on multi lines\n' - ' for blabla in bla:\n' - ' arrrgh\n' - ' asasxasx\n' - ' lorem ipsum und so\n', - 'type': '03. This one exists for the sole purpose to test', - 'user': 'student01' + 'user': 'student01', + 'feedback': { + 'text': 'Not good!', + 'score': 5, + 'of_tutor': 'tutor01', + 'is_final': True + } }, { 'text': 'function blabl\n' @@ -267,7 +263,13 @@ def init_test_instance(): ' asasxasx\n' ' lorem ipsum und so\n', 'type': '03. This one exists for the sole purpose to test', - 'user': 'student02' + 'user': 'student01', + 'feedback': { + 'text': 'Not good!', + 'score': 5, + 'of_tutor': 'tutor01', + 'is_final': True + } }, ]} )