Skip to content

Commit

Permalink
feat: [AXM-236] Add progress for other courses (#2536)
Browse files Browse the repository at this point in the history
  • Loading branch information
NiedielnitsevIvan authored and KyryloKireiev committed Jun 26, 2024
1 parent 4f23379 commit c6f6214
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 20 deletions.
57 changes: 37 additions & 20 deletions lms/djangoapps/mobile_api/users/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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]]]]:
"""
Expand Down
3 changes: 3 additions & 0 deletions lms/djangoapps/mobile_api/users/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down

0 comments on commit c6f6214

Please sign in to comment.