Commit 90e861d6 by Dennis Jen Committed by GitHub

Merge pull request #130 from edx/dsjen/attempt-ratio

Updated engagement ranges fields.
parents 7fe4fa66 541d4247
...@@ -424,23 +424,23 @@ class DateRangeSerializer(serializers.Serializer): ...@@ -424,23 +424,23 @@ class DateRangeSerializer(serializers.Serializer):
class EnagementRangeMetricSerializer(serializers.Serializer): class EnagementRangeMetricSerializer(serializers.Serializer):
""" """
Serializes ModuleEngagementMetricRanges ('low', 'normal', and 'high') into Serializes ModuleEngagementMetricRanges ('bottom', 'average', and 'top') into
the below_average, average, and above_average ranges represented as arrays. the class_rank_bottom, class_rank_average, and class_rank_top ranges
If any one of the ranges is not defined, it is not included in the represented as arrays. If any one of the ranges is not defined, it is not
serialized output. included in the serialized output.
""" """
below_average = serializers.SerializerMethodField('get_below_average_range') class_rank_bottom = serializers.SerializerMethodField('get_class_rank_bottom')
average = serializers.SerializerMethodField('get_average_range') class_rank_average = serializers.SerializerMethodField('get_class_rank_average')
above_average = serializers.SerializerMethodField('get_above_average_range') class_rank_top = serializers.SerializerMethodField('get_class_rank_top')
def get_average_range(self, obj): def get_class_rank_average(self, obj):
return self._transform_range(obj['normal_range']) return self._transform_range(obj['average'])
def get_below_average_range(self, obj): def get_class_rank_bottom(self, obj):
return self._transform_range(obj['low_range']) return self._transform_range(obj['bottom'])
def get_above_average_range(self, obj): def get_class_rank_top(self, obj):
return self._transform_range(obj['high_range']) return self._transform_range(obj['top'])
def _transform_range(self, metric_range): def _transform_range(self, metric_range):
return [metric_range.low_value, metric_range.high_value] if metric_range else None return [metric_range.low_value, metric_range.high_value] if metric_range else None
...@@ -459,15 +459,20 @@ class CourseLearnerMetadataSerializer(serializers.Serializer): ...@@ -459,15 +459,20 @@ class CourseLearnerMetadataSerializer(serializers.Serializer):
} }
for metric in engagement_events.EVENTS: for metric in engagement_events.EVENTS:
low_range_queryset = query_set.filter(metric=metric, range_type='low') # construct the range type to class rank pairs
normal_range_queryset = query_set.filter(metric=metric, range_type='normal') ranges_ranks = [('normal', 'average')]
high_range_queryset = query_set.filter(metric=metric, range_type='high') if metric == 'problem_attempts_per_completed':
ranges_ranks.extend([('low', 'top'), ('high', 'bottom')])
else:
ranges_ranks.extend([('high', 'top'), ('low', 'bottom')])
# put together data to be serialized
serializer_kwargs = {}
for range_type, class_rank_type in ranges_ranks:
range_queryset = query_set.filter(metric=metric, range_type=range_type)
serializer_kwargs[class_rank_type] = range_queryset[0] if len(range_queryset) else None
engagement_ranges.update({ engagement_ranges.update({
metric: EnagementRangeMetricSerializer({ metric: EnagementRangeMetricSerializer(serializer_kwargs).data
'low_range': low_range_queryset[0] if len(low_range_queryset) else None,
'normal_range': normal_range_queryset[0] if len(normal_range_queryset) else None,
'high_range': high_range_queryset[0] if len(high_range_queryset) else None,
}).data
}) })
return engagement_ranges return engagement_ranges
...@@ -569,7 +569,7 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin, ...@@ -569,7 +569,7 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin,
} }
} }
empty_range = { empty_range = {
range_type: None for range_type in ['below_average', 'average', 'above_average'] range_type: None for range_type in ['class_rank_bottom', 'class_rank_average', 'class_rank_top']
} }
for metric in engagement_events.EVENTS: for metric in engagement_events.EVENTS:
empty_engagement_ranges['engagement_ranges'][metric] = copy.deepcopy(empty_range) empty_engagement_ranges['engagement_ranges'][metric] = copy.deepcopy(empty_range)
...@@ -593,9 +593,9 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin, ...@@ -593,9 +593,9 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin,
'end': '2015-07-21' 'end': '2015-07-21'
}, },
metric_type: { metric_type: {
'below_average': None, 'class_rank_bottom': None,
'average': [90.0, 6120.0], 'class_rank_average': [90.0, 6120.0],
'above_average': None 'class_rank_top': None
} }
}) })
...@@ -625,11 +625,20 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin, ...@@ -625,11 +625,20 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin,
normal_floor = 800.8 normal_floor = 800.8
G(ModuleEngagementMetricRanges, course_id=self.course_id, start_date=start_date, end_date=end_date, G(ModuleEngagementMetricRanges, course_id=self.course_id, start_date=start_date, end_date=end_date,
metric=metric_type, range_type='normal', low_value=normal_floor, high_value=max_value) metric=metric_type, range_type='normal', low_value=normal_floor, high_value=max_value)
expected['engagement_ranges'][metric_type] = { expected['engagement_ranges'][metric_type] = {
'below_average': [0.0, low_ceil], 'class_rank_average': [normal_floor, max_value],
'average': [normal_floor, max_value],
'above_average': None
} }
if metric_type == 'problem_attempts_per_completed':
expected['engagement_ranges'][metric_type].update({
'class_rank_top': [0.0, low_ceil],
'class_rank_bottom': None
})
else:
expected['engagement_ranges'][metric_type].update({
'class_rank_bottom': [0.0, low_ceil],
'class_rank_top': None
})
return expected return expected
......
...@@ -347,8 +347,8 @@ class CourseLearnerMetadata(CourseViewMixin, generics.RetrieveAPIView): ...@@ -347,8 +347,8 @@ class CourseLearnerMetadata(CourseViewMixin, generics.RetrieveAPIView):
tracks in the course to the number of learners belonging to those tracks in the course to the number of learners belonging to those
tracks. Examples include "audit" and "verified". tracks. Examples include "audit" and "verified".
* engagement_ranges: An object containing ranges of learner * engagement_ranges: An object containing ranges of learner
engagement with the courseware. Each range has 'below_average', engagement with the courseware. Each range has 'class_rank_bottom',
'average', and 'above_average' keys. These keys map to 'class_rank_average', and 'class_rank_top' keys. These keys map to
two-element arrays, in which the first element is the lower bound two-element arrays, in which the first element is the lower bound
(inclusive) and the second element is the upper bound (inclusive) and the second element is the upper bound
(exclusive). It has the following keys. (exclusive). It has the following keys.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment