From 79f9882abc34ba6f54f89a89aec88d84e65562af Mon Sep 17 00:00:00 2001 From: NiedielnitsevIvan <81557788+NiedielnitsevIvan@users.noreply.github.com> Date: Wed, 10 Apr 2024 17:55:02 +0300 Subject: [PATCH] feat: [AXM-236] Add progress for other courses (#2536) --- .../mobile_api/users/serializers.py | 57 ++++++++++++------- lms/djangoapps/mobile_api/users/views.py | 3 + 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/lms/djangoapps/mobile_api/users/serializers.py b/lms/djangoapps/mobile_api/users/serializers.py index 82eb3edee278..991205cc3091 100644 --- a/lms/djangoapps/mobile_api/users/serializers.py +++ b/lms/djangoapps/mobile_api/users/serializers.py @@ -106,6 +106,8 @@ class CourseEnrollmentSerializer(serializers.ModelSerializer): audit_access_expires = serializers.SerializerMethodField() course_modes = serializers.SerializerMethodField() + BLOCK_STRUCTURE_CACHE_TIMEOUT = 60 * 60 # 1 hour + def get_audit_access_expires(self, model): """ Returns expiration date for a course audit expiration, if any or null @@ -137,6 +139,40 @@ def get_course_modes(self, obj): for mode in course_modes ] + def to_representation(self, instance): + """ + Override the to_representation method to add the course_status field to the serialized data. + """ + data = super().to_representation(instance) + if 'progress' in self.context.get('requested_fields', []): + data['progress'] = self.calculate_progress(instance) + + return data + + def calculate_progress(self, model: CourseEnrollment) -> Dict[str, int]: + """ + Calculate the progress of the user in the course. + :param model: + :return: + """ + is_staff = bool(has_access(model.user, 'staff', model.course.id)) + + cache_key = f'course_block_structure_{str(model.course.id)}_{model.user.id}' + collected_block_structure = cache.get(cache_key) + if not collected_block_structure: + collected_block_structure = get_block_structure_manager(model.course.id).get_collected() + cache.set(cache_key, collected_block_structure, self.BLOCK_STRUCTURE_CACHE_TIMEOUT) + + course_grade = CourseGradeFactory().read(model.user, collected_block_structure=collected_block_structure) + + # recalculate course grade from visible grades (stored grade was calculated over all grades, visible or not) + course_grade.update(visible_grades_only=True, has_staff_access=is_staff) + subsection_grades = list(course_grade.subsection_grades.values()) + return { + 'num_points_earned': sum(map(lambda x: x.graded_total.earned if x.graded else 0, subsection_grades)), + 'num_points_possible': sum(map(lambda x: x.graded_total.possible if x.graded else 0, subsection_grades)), + } + class Meta: model = CourseEnrollment fields = ('audit_access_expires', 'created', 'mode', 'is_active', 'course', 'certificate', 'course_modes') @@ -165,8 +201,6 @@ class CourseEnrollmentSerializerModifiedForPrimary(CourseEnrollmentSerializer): progress = serializers.SerializerMethodField() course_assignments = serializers.SerializerMethodField() - BLOCK_STRUCTURE_CACHE_TIMEOUT = 60 * 60 # 1 hour - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.course = modulestore().get_course(self.instance.course.id) @@ -230,24 +264,7 @@ def get_progress(self, model: CourseEnrollment) -> Dict[str, int]: """ Returns the progress of the user in the course. """ - assert isinstance(model, CourseEnrollment), f'Expected CourseEnrollment, got {type(model)}' - is_staff = bool(has_access(model.user, 'staff', model.course.id)) - - cache_key = f'course_block_structure_{str(model.course.id)}_{model.user.id}' - collected_block_structure = cache.get(cache_key) - if not collected_block_structure: - collected_block_structure = get_block_structure_manager(model.course.id).get_collected() - cache.set(cache_key, collected_block_structure, self.BLOCK_STRUCTURE_CACHE_TIMEOUT) - - course_grade = CourseGradeFactory().read(model.user, collected_block_structure=collected_block_structure) - - # recalculate course grade from visible grades (stored grade was calculated over all grades, visible or not) - course_grade.update(visible_grades_only=True, has_staff_access=is_staff) - subsection_grades = list(course_grade.subsection_grades.values()) - return { - 'num_points_earned': sum(map(lambda x: x.graded_total.earned if x.graded else 0, subsection_grades)), - 'num_points_possible': sum(map(lambda x: x.graded_total.possible if x.graded else 0, subsection_grades)), - } + return self.calculate_progress(model) def get_course_assignments(self, model: CourseEnrollment) -> Optional[Dict[str, List[Dict[str, str]]]]: """ diff --git a/lms/djangoapps/mobile_api/users/views.py b/lms/djangoapps/mobile_api/users/views.py index 4d2ddf2aad7a..b8949d269632 100644 --- a/lms/djangoapps/mobile_api/users/views.py +++ b/lms/djangoapps/mobile_api/users/views.py @@ -346,7 +346,10 @@ def is_org(self, check_org, course_org): def get_serializer_context(self): context = super().get_serializer_context() + requested_fields = self.request.GET.get('requested_fields', '') + context['api_version'] = self.kwargs.get('api_version') + context['requested_fields'] = requested_fields.split(',') return context def get_serializer_class(self):