Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-analytics-data-api
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-analytics-data-api
Commits
679351bb
Commit
679351bb
authored
May 19, 2016
by
Daniel Friedman
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Surface engagement metric ranges according to spec
AN-6899 Better handles edge cases when there's sparse data.
parent
802bcdf5
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
27 additions
and
23 deletions
+27
-23
analytics_data_api/v0/models.py
+3
-3
analytics_data_api/v0/serializers.py
+15
-11
analytics_data_api/v0/tests/views/test_learners.py
+9
-9
No files found.
analytics_data_api/v0/models.py
View file @
679351bb
...
@@ -464,9 +464,9 @@ class ModuleEngagement(models.Model):
...
@@ -464,9 +464,9 @@ class ModuleEngagement(models.Model):
class
ModuleEngagementMetricRanges
(
models
.
Model
):
class
ModuleEngagementMetricRanges
(
models
.
Model
):
"""
"""
Represents the low and high values for a module engagement entity and event
pair,
Represents the low and high values for a module engagement entity and event
known as the metric. The range_type will either be high or low, bounded by
pair, known as the metric. The range_type will either be low, normal, or
low_value and high_value.
high, bounded by
low_value and high_value.
"""
"""
course_id
=
models
.
CharField
(
db_index
=
True
,
max_length
=
255
)
course_id
=
models
.
CharField
(
db_index
=
True
,
max_length
=
255
)
...
...
analytics_data_api/v0/serializers.py
View file @
679351bb
...
@@ -412,28 +412,26 @@ class DateRangeSerializer(serializers.Serializer):
...
@@ -412,28 +412,26 @@ class DateRangeSerializer(serializers.Serializer):
class
EnagementRangeMetricSerializer
(
serializers
.
Serializer
):
class
EnagementRangeMetricSerializer
(
serializers
.
Serializer
):
"""
"""
Serializes ModuleEngagementMetricRanges (low_range and high_range) into
Serializes ModuleEngagementMetricRanges ('low', 'normal', and 'high') into
the below_average, average, above_average ranges represented as arrays.
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.
"""
"""
below_average
=
serializers
.
SerializerMethodField
(
'get_below_average_range'
)
below_average
=
serializers
.
SerializerMethodField
(
'get_below_average_range'
)
average
=
serializers
.
SerializerMethodField
(
'get_average_range'
)
average
=
serializers
.
SerializerMethodField
(
'get_average_range'
)
above_average
=
serializers
.
SerializerMethodField
(
'get_above_average_range'
)
above_average
=
serializers
.
SerializerMethodField
(
'get_above_average_range'
)
def
get_average_range
(
self
,
obj
):
def
get_average_range
(
self
,
obj
):
metric_range
=
[
return
self
.
_transform_range
(
obj
[
'normal_range'
])
obj
[
'low_range'
]
.
high_value
if
obj
[
'low_range'
]
else
None
,
obj
[
'high_range'
]
.
low_value
if
obj
[
'high_range'
]
else
None
,
]
return
metric_range
def
get_below_average_range
(
self
,
obj
):
def
get_below_average_range
(
self
,
obj
):
return
self
.
_
get
_range
(
obj
[
'low_range'
])
return
self
.
_
transform
_range
(
obj
[
'low_range'
])
def
get_above_average_range
(
self
,
obj
):
def
get_above_average_range
(
self
,
obj
):
return
self
.
_
get
_range
(
obj
[
'high_range'
])
return
self
.
_
transform
_range
(
obj
[
'high_range'
])
def
_
get
_range
(
self
,
metric_range
):
def
_
transform
_range
(
self
,
metric_range
):
return
[
metric_range
.
low_value
,
metric_range
.
high_value
]
if
metric_range
else
[
None
,
None
]
return
[
metric_range
.
low_value
,
metric_range
.
high_value
]
if
metric_range
else
None
class
CourseLearnerMetadataSerializer
(
serializers
.
Serializer
):
class
CourseLearnerMetadataSerializer
(
serializers
.
Serializer
):
...
@@ -452,11 +450,17 @@ class CourseLearnerMetadataSerializer(serializers.Serializer):
...
@@ -452,11 +450,17 @@ class CourseLearnerMetadataSerializer(serializers.Serializer):
for
entity_type
in
engagement_entity_types
.
AGGREGATE_TYPES
:
for
entity_type
in
engagement_entity_types
.
AGGREGATE_TYPES
:
for
event
in
engagement_events
.
EVENTS
[
entity_type
]:
for
event
in
engagement_events
.
EVENTS
[
entity_type
]:
metric
=
'{0}_{1}'
.
format
(
entity_type
,
event
)
metric
=
'{0}_{1}'
.
format
(
entity_type
,
event
)
# It's assumed that there may be any combination of low, normal,
# and high ranges in the database for the given course. Some
# edge cases result from a lack of available data; in such
# cases, only some ranges may be returned.
low_range_queryset
=
query_set
.
filter
(
metric
=
metric
,
range_type
=
'low'
)
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'
)
high_range_queryset
=
query_set
.
filter
(
metric
=
metric
,
range_type
=
'high'
)
engagement_ranges
.
update
({
engagement_ranges
.
update
({
metric
:
EnagementRangeMetricSerializer
({
metric
:
EnagementRangeMetricSerializer
({
'low_range'
:
low_range_queryset
[
0
]
if
len
(
low_range_queryset
)
else
None
,
'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
,
'high_range'
:
high_range_queryset
[
0
]
if
len
(
high_range_queryset
)
else
None
,
})
.
data
})
.
data
})
})
...
...
analytics_data_api/v0/tests/views/test_learners.py
View file @
679351bb
...
@@ -569,7 +569,7 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin,
...
@@ -569,7 +569,7 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin,
}
}
}
}
empty_range
=
{
empty_range
=
{
range_type
:
[
None
,
None
]
for
range_type
in
[
'below_average'
,
'average'
,
'above_average'
]
range_type
:
None
for
range_type
in
[
'below_average'
,
'average'
,
'above_average'
]
}
}
for
metric
in
self
.
engagement_metrics
:
for
metric
in
self
.
engagement_metrics
:
empty_engagement_ranges
[
'engagement_ranges'
][
metric
]
=
copy
.
deepcopy
(
empty_range
)
empty_engagement_ranges
[
'engagement_ranges'
][
metric
]
=
copy
.
deepcopy
(
empty_range
)
...
@@ -594,7 +594,7 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin,
...
@@ -594,7 +594,7 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin,
start_date
=
datetime
.
datetime
(
2015
,
7
,
1
,
tzinfo
=
pytz
.
utc
)
start_date
=
datetime
.
datetime
(
2015
,
7
,
1
,
tzinfo
=
pytz
.
utc
)
end_date
=
datetime
.
datetime
(
2015
,
7
,
21
,
tzinfo
=
pytz
.
utc
)
end_date
=
datetime
.
datetime
(
2015
,
7
,
21
,
tzinfo
=
pytz
.
utc
)
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
=
'
high
'
,
low_value
=
90
,
high_value
=
6120
)
metric
=
metric_type
,
range_type
=
'
normal
'
,
low_value
=
90
,
high_value
=
6120
)
expected_ranges
=
self
.
empty_engagement_ranges
expected_ranges
=
self
.
empty_engagement_ranges
expected_ranges
[
'engagement_ranges'
]
.
update
({
expected_ranges
[
'engagement_ranges'
]
.
update
({
'date_range'
:
{
'date_range'
:
{
...
@@ -602,9 +602,9 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin,
...
@@ -602,9 +602,9 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin,
'end'
:
'2015-07-21'
'end'
:
'2015-07-21'
},
},
metric_type
:
{
metric_type
:
{
'below_average'
:
[
None
,
None
]
,
'below_average'
:
None
,
'average'
:
[
None
,
9
0.0
],
'average'
:
[
90.0
,
612
0.0
],
'above_average'
:
[
90.0
,
6120.0
]
'above_average'
:
None
}
}
})
})
...
@@ -631,13 +631,13 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin,
...
@@ -631,13 +631,13 @@ class CourseLearnerMetadataTests(DemoCourseMixin, VerifyCourseIdMixin,
low_ceil
=
100.5
low_ceil
=
100.5
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
=
'low'
,
low_value
=
0
,
high_value
=
low_ceil
)
metric
=
metric_type
,
range_type
=
'low'
,
low_value
=
0
,
high_value
=
low_ceil
)
high
_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
=
'
high'
,
low_value
=
high
_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
],
'below_average'
:
[
0.0
,
low_ceil
],
'average'
:
[
low_ceil
,
high_floor
],
'average'
:
[
normal_floor
,
max_value
],
'above_average'
:
[
high_floor
,
max_value
]
'above_average'
:
None
}
}
return
expected
return
expected
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment