import logging from django.core.exceptions import ObjectDoesNotExist from drf_dynamic_fields import DynamicFieldsMixin from rest_framework import serializers from core import models from core.models import (ExamType, Feedback, GeneralTaskSubscription, StudentInfo, Submission, SubmissionType, TutorSubmissionAssignment) from util.factories import GradyUserFactory log = logging.getLogger(__name__) user_factory = GradyUserFactory() class DynamicFieldsModelSerializer(DynamicFieldsMixin, serializers.ModelSerializer): pass class ExamSerializer(DynamicFieldsModelSerializer): class Meta: model = ExamType fields = ('module_reference', 'total_score', 'pass_score', 'pass_only',) class FeedbackSerializer(DynamicFieldsModelSerializer): assignment_id = serializers.UUIDField(write_only=True) def validate(self, data): log.debug(data) assignment_id = data.pop('assignment_id') score = data.get('score') try: assignment = TutorSubmissionAssignment.objects.get( assignment_id=assignment_id) except ObjectDoesNotExist as err: raise serializers.ValidationError('No assignment for given id.') submission = assignment.submission if not 0 <= score <= submission.type.full_score: raise serializers.ValidationError( f'Score has to be in range [0..{submission.type.full_score}].') if hasattr(submission, 'feedback'): raise serializers.ValidationError( 'Feedback for this submission already exists') return { **data, 'assignment': assignment, 'of_submission': submission } def create(self, validated_data) -> Feedback: assignment = validated_data.pop('assignment') assignment.set_done() return Feedback.objects.create(**validated_data) class Meta: model = Feedback fields = ('assignment_id', 'text', 'score') class SubmissionTypeSerializer(DynamicFieldsModelSerializer): class Meta: model = SubmissionType fields = ('name', 'full_score', 'description', 'solution') class SubmissionSerializer(DynamicFieldsModelSerializer): feedback = serializers.ReadOnlyField(source='feedback.text') score = serializers.ReadOnlyField(source='feedback.score') type_id = serializers.ReadOnlyField(source='type.id') type_name = serializers.ReadOnlyField(source='type.name') full_score = serializers.ReadOnlyField(source='type.full_score') class Meta: model = Submission fields = ('type_id', 'type_name', 'text', 'feedback', 'score', 'full_score') class StudentInfoSerializer(DynamicFieldsModelSerializer): name = serializers.ReadOnlyField(source='user.fullname') user = serializers.ReadOnlyField(source='user.username') exam = ExamSerializer() submissions = SubmissionSerializer(many=True) class Meta: model = StudentInfo fields = ('name', 'user', 'exam', 'submissions') class SubmissionNoTextFieldsSerializer(DynamicFieldsModelSerializer): score = serializers.ReadOnlyField(source='feedback.score') type = serializers.ReadOnlyField(source='type.name') full_score = serializers.ReadOnlyField(source='type.full_score') class Meta: model = Submission fields = ('type', 'score', 'full_score') class StudentInfoSerializerForListView(DynamicFieldsModelSerializer): name = serializers.ReadOnlyField(source='user.fullname') user = serializers.ReadOnlyField(source='user.username') exam = serializers.ReadOnlyField(source='exam.module_reference') submissions = SubmissionNoTextFieldsSerializer(many=True) class Meta: model = StudentInfo fields = ('name', 'user', 'exam', 'submissions') class TutorSerializer(DynamicFieldsModelSerializer): feedback_count = serializers.IntegerField(source='get_feedback_count', read_only=True) def create(self, validated_data) -> models.UserAccount: log.info("Crating tutor from data %s", validated_data) return user_factory.make_tutor( username=validated_data['username']) class Meta: model = models.UserAccount fields = ('username', 'feedback_count') class AssignmentSerializer(DynamicFieldsModelSerializer): submission_id = serializers.ReadOnlyField( source='submission.submission_id') class Meta: model = TutorSubmissionAssignment fields = ('assignment_id', 'submission_id', 'is_done',) class SubmissionAssignmentSerializer(DynamicFieldsModelSerializer): text = serializers.ReadOnlyField() type_id = serializers.ReadOnlyField(source='type.id') full_score = serializers.ReadOnlyField(source='type.full_score') class Meta: model = Submission fields = ('submission_id', 'type_id', 'text', 'full_score') class AssignmentDetailSerializer(DynamicFieldsModelSerializer): submission = SubmissionAssignmentSerializer() feedback = FeedbackSerializer(source='submission.feedback') class Meta: model = TutorSubmissionAssignment fields = ('assignment_id', 'feedback', 'submission', 'is_done',) class SubscriptionSerializer(DynamicFieldsModelSerializer): owner = serializers.ReadOnlyField(source='owner.username') query_key = serializers.CharField(required=False) assignments = AssignmentSerializer(read_only=True, many=True) def validate(self, data): data['owner'] = self.context['request'].user if 'query_key' in data != \ data['query_type'] == GeneralTaskSubscription.RANDOM: raise serializers.ValidationError( f'The {data["query_type"]} query_type does not work with the' f'provided key') try: GeneralTaskSubscription.objects.get( owner=data['owner'], query_type=data['query_type'], query_key=data.get('query_key', None)) except ObjectDoesNotExist: pass else: raise serializers.ValidationError( 'The user already has the subscription') return data def create(self, validated_data) -> GeneralTaskSubscription: return GeneralTaskSubscription.objects.create(**validated_data) class Meta: model = GeneralTaskSubscription fields = ( 'subscription_id', 'owner', 'query_type', 'query_key', 'assignments')