<commit_before>from django.db.models import Q
from django.db.models import Sum
from rest_framework.serializers import JSONField
from rest_framework.serializers import ModelSerializer
from rest_framework.serializers import SerializerMethodField

from kolibri.auth.models import Classroom
from kolibri.core.exams.models import Exam
from kolibri.core.lessons.models import Lesson
from kolibri.logger.models import ContentSummaryLog
from kolibri.logger.models import ExamLog


class ExamProgressSerializer(ModelSerializer):
    """
    Annotates an Exam with progress information based on logs generated
    by the requesting User
    """
    class Meta:
        model = Exam
        fields = (
            'active',
            'id',
            'progress',
            'question_count',
            'title',
        )

    progress = SerializerMethodField()

    # Mostly copied from UserExamSerializer.to_representation, but working directly
    # from Exam Model instead of ExamAssignment
    def get_progress(self, instance):
        try:
            examlogs = instance.examlogs.get(user=self.context['user'])
            return {
                'score': examlogs.attemptlogs.aggregate(Sum('correct')).get('correct__sum'),
                'answer_count': examlogs.attemptlogs.count(),
                'closed': examlogs.closed,
            }
        except ExamLog.DoesNotExist:
            return {
                'score': None,
                'answer_count': None,
                'closed': None,
            }


class LessonProgressSerializer(ModelSerializer):
    """
    Annotates a Lesson with progress information based on logs generated
    by the requesting User
    """
    progress = SerializerMethodField()
    resources = JSONField(default='[]')

    class Meta:
        model = Lesson
        fields = (
            'description',
            'id',
            'is_active',
            'title',
            'progress',
            'resources',
        )

    def get_progress(self, instance):
        content_ids = [resource['content_id'] for resource in instance.resources]
        num_completed_logs = ContentSummaryLog.objects \
            .exclude(completion_timestamp__isnull=True) \
            .filter(
                user=self.context['user'],
                content_id__in=content_ids
            ) \
            .count()
        return {
            'resources_completed': num_completed_logs,
            'total_resources': len(instance.resources),
        }

class LearnerClassroomSerializer(ModelSerializer):
    assignments = SerializerMethodField()

    class Meta:
        model = Classroom
        fields = (
            'id',
            'name',
            'assignments',
        )

    def get_assignments(self, instance):
        """
        Returns all Exams and Lessons (and progress) assigned to the requesting User
        """
        current_user = self.context['request'].user
        memberships = current_user.memberships.all()
        learner_groups = [m.collection for m in memberships]

        # Return only active Lessons that are assigned to the requesting user's groups
        # TODO move this to a permission_class on Lesson
        filtered_lessons = Lesson.objects.filter(
            lesson_assignments__collection__in=learner_groups,
            collection=instance,
            is_active=True,
        )

        filtered_exams = Exam.objects.filter(
            assignments__collection__in=learner_groups,
            collection=instance,
        ).filter(Q(active=True) | Q(examlogs__user=current_user))

        return {
            'lessons': LessonProgressSerializer(
                filtered_lessons,
                many=True,
                context={'user': current_user},
            ).data,
            'exams': ExamProgressSerializer(
                filtered_exams,
                many=True,<commit_msg>Do not return duplicate exams<commit_after>from django.db.models import Q
from django.db.models import Sum
from rest_framework.serializers import JSONField
from rest_framework.serializers import ModelSerializer
from rest_framework.serializers import SerializerMethodField

from kolibri.auth.models import Classroom
from kolibri.core.exams.models import Exam
from kolibri.core.lessons.models import Lesson
from kolibri.logger.models import ContentSummaryLog
from kolibri.logger.models import ExamLog


class ExamProgressSerializer(ModelSerializer):
    """
    Annotates an Exam with progress information based on logs generated
    by the requesting User
    """
    class Meta:
        model = Exam
        fields = (
            'active',
            'id',
            'progress',
            'question_count',
            'title',
        )

    progress = SerializerMethodField()

    # Mostly copied from UserExamSerializer.to_representation, but working directly
    # from Exam Model instead of ExamAssignment
    def get_progress(self, instance):
        try:
            examlogs = instance.examlogs.get(user=self.context['user'])
            return {
                'score': examlogs.attemptlogs.aggregate(Sum('correct')).get('correct__sum'),
                'answer_count': examlogs.attemptlogs.count(),
                'closed': examlogs.closed,
            }
        except ExamLog.DoesNotExist:
            return {
                'score': None,
                'answer_count': None,
                'closed': None,
            }


class LessonProgressSerializer(ModelSerializer):
    """
    Annotates a Lesson with progress information based on logs generated
    by the requesting User
    """
    progress = SerializerMethodField()
    resources = JSONField(default='[]')

    class Meta:
        model = Lesson
        fields = (
            'description',
            'id',
            'is_active',
            'title',
            'progress',
            'resources',
        )

    def get_progress(self, instance):
        content_ids = [resource['content_id'] for resource in instance.resources]
        num_completed_logs = ContentSummaryLog.objects \
            .exclude(completion_timestamp__isnull=True) \
            .filter(
                user=self.context['user'],
                content_id__in=content_ids
            ) \
            .count()
        return {
            'resources_completed': num_completed_logs,
            'total_resources': len(instance.resources),
        }

class LearnerClassroomSerializer(ModelSerializer):
    assignments = SerializerMethodField()

    class Meta:
        model = Classroom
        fields = (
            'id',
            'name',
            'assignments',
        )

    def get_assignments(self, instance):
        """
        Returns all Exams and Lessons (and progress) assigned to the requesting User
        """
        current_user = self.context['request'].user
        memberships = current_user.memberships.all()
        learner_groups = [m.collection for m in memberships]

        # Return only active Lessons that are assigned to the requesting user's groups
        # TODO move this to a permission_class on Lesson
        filtered_lessons = Lesson.objects.filter(
            lesson_assignments__collection__in=learner_groups,
            collection=instance,
            is_active=True,
        ).distinct()

        filtered_exams = Exam.objects.filter(
            assignments__collection__in=learner_groups,
            collection=instance,
        ).filter(Q(active=True) | Q(examlogs__user=current_user)).distinct()

        return {
            'lessons': LessonProgressSerializer(
                filtered_lessons,
                many=True,
                context={'user': current_user},
            ).data,
            'exams': ExamProgressSerializer(
                filtered_exams,
                many=True,
{'fraction_non_alphanumeric': 0.08256410256410257, 'fraction_numerical': 0.0, 'mean_word_length': 2.0993245927691695, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7374120', 'n_tokens_mistral': 2014, 'n_tokens_neox': 1950, 'n_words': 474}