Skip to content

Commit

Permalink
Merge pull request openedx-unsupported#130 from edx/dsjen/attempt-ratio
Browse files Browse the repository at this point in the history
Updated engagement ranges fields.
  • Loading branch information
dsjen committed Aug 3, 2016
2 parents 7fe4fa6 + 541d424 commit 90e861d
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 30 deletions.
47 changes: 26 additions & 21 deletions analytics_data_api/v0/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,23 +424,23 @@ class DateRangeSerializer(serializers.Serializer):

class EnagementRangeMetricSerializer(serializers.Serializer):
"""
Serializes ModuleEngagementMetricRanges ('low', 'normal', and 'high') into
the below_average, average, and above_average ranges represented as arrays.
If any one of the ranges is not defined, it is not included in the
serialized output.
Serializes ModuleEngagementMetricRanges ('bottom', 'average', and 'top') into
the class_rank_bottom, class_rank_average, and class_rank_top ranges
represented as arrays. If any one of the ranges is not defined, it is not
included in the serialized output.
"""
below_average = serializers.SerializerMethodField('get_below_average_range')
average = serializers.SerializerMethodField('get_average_range')
above_average = serializers.SerializerMethodField('get_above_average_range')
class_rank_bottom = serializers.SerializerMethodField('get_class_rank_bottom')
class_rank_average = serializers.SerializerMethodField('get_class_rank_average')
class_rank_top = serializers.SerializerMethodField('get_class_rank_top')

def get_average_range(self, obj):
return self._transform_range(obj['normal_range'])
def get_class_rank_average(self, obj):
return self._transform_range(obj['average'])

def get_below_average_range(self, obj):
return self._transform_range(obj['low_range'])
def get_class_rank_bottom(self, obj):
return self._transform_range(obj['bottom'])

def get_above_average_range(self, obj):
return self._transform_range(obj['high_range'])
def get_class_rank_top(self, obj):
return self._transform_range(obj['top'])

def _transform_range(self, metric_range):
return [metric_range.low_value, metric_range.high_value] if metric_range else None
Expand All @@ -459,15 +459,20 @@ def get_engagement_ranges(self, obj):
}

for metric in engagement_events.EVENTS:
low_range_queryset = query_set.filter(metric=metric, range_type='low')
normal_range_queryset = query_set.filter(metric=metric, range_type='normal')
high_range_queryset = query_set.filter(metric=metric, range_type='high')
# construct the range type to class rank pairs
ranges_ranks = [('normal', 'average')]
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({
metric: EnagementRangeMetricSerializer({
'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
metric: EnagementRangeMetricSerializer(serializer_kwargs).data
})

return engagement_ranges
23 changes: 16 additions & 7 deletions analytics_data_api/v0/tests/views/test_learners.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,7 @@ def empty_engagement_ranges(self):
}
}
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:
empty_engagement_ranges['engagement_ranges'][metric] = copy.deepcopy(empty_range)
Expand All @@ -593,9 +593,9 @@ def test_one_engagement_range(self):
'end': '2015-07-21'
},
metric_type: {
'below_average': None,
'average': [90.0, 6120.0],
'above_average': None
'class_rank_bottom': None,
'class_rank_average': [90.0, 6120.0],
'class_rank_top': None
}
})

Expand Down Expand Up @@ -625,11 +625,20 @@ def _get_full_engagement_ranges(self):
normal_floor = 800.8
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)

expected['engagement_ranges'][metric_type] = {
'below_average': [0.0, low_ceil],
'average': [normal_floor, max_value],
'above_average': None
'class_rank_average': [normal_floor, max_value],
}
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

Expand Down
4 changes: 2 additions & 2 deletions analytics_data_api/v0/views/learners.py
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,8 @@ class CourseLearnerMetadata(CourseViewMixin, generics.RetrieveAPIView):
tracks in the course to the number of learners belonging to those
tracks. Examples include "audit" and "verified".
* engagement_ranges: An object containing ranges of learner
engagement with the courseware. Each range has 'below_average',
'average', and 'above_average' keys. These keys map to
engagement with the courseware. Each range has 'class_rank_bottom',
'class_rank_average', and 'class_rank_top' keys. These keys map to
two-element arrays, in which the first element is the lower bound
(inclusive) and the second element is the upper bound
(exclusive). It has the following keys.
Expand Down

0 comments on commit 90e861d

Please sign in to comment.