edx-enterprise-data 8.7.0__py3-none-any.whl → 8.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {edx_enterprise_data-8.7.0.dist-info → edx_enterprise_data-8.8.0.dist-info}/METADATA +1 -1
- {edx_enterprise_data-8.7.0.dist-info → edx_enterprise_data-8.8.0.dist-info}/RECORD +20 -17
- enterprise_data/__init__.py +1 -1
- enterprise_data/admin_analytics/constants.py +9 -3
- enterprise_data/admin_analytics/utils.py +46 -10
- enterprise_data/api/v1/paginators.py +1 -1
- enterprise_data/api/v1/serializers.py +39 -30
- enterprise_data/api/v1/urls.py +12 -6
- enterprise_data/api/v1/views/analytics_enrollments.py +42 -53
- enterprise_data/api/v1/views/analytics_leaderboard.py +120 -0
- enterprise_data/api/v1/views/enterprise_completions.py +5 -5
- enterprise_data/renderers.py +14 -0
- enterprise_data/tests/admin_analytics/mock_analytics_data.py +501 -0
- enterprise_data/tests/admin_analytics/mock_enrollments.py +4 -4
- enterprise_data/tests/admin_analytics/test_analytics_enrollments.py +23 -22
- enterprise_data/tests/admin_analytics/test_analytics_leaderboard.py +163 -0
- enterprise_data/tests/admin_analytics/test_enterprise_completions.py +1 -1
- {edx_enterprise_data-8.7.0.dist-info → edx_enterprise_data-8.8.0.dist-info}/LICENSE +0 -0
- {edx_enterprise_data-8.7.0.dist-info → edx_enterprise_data-8.8.0.dist-info}/WHEEL +0 -0
- {edx_enterprise_data-8.7.0.dist-info → edx_enterprise_data-8.8.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
"""Advance Analytics for Leaderboard"""
|
2
|
+
from datetime import datetime
|
3
|
+
|
4
|
+
import numpy as np
|
5
|
+
import pandas as pd
|
6
|
+
from edx_rbac.decorators import permission_required
|
7
|
+
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
|
8
|
+
from rest_framework.views import APIView
|
9
|
+
|
10
|
+
from django.http import StreamingHttpResponse
|
11
|
+
|
12
|
+
from enterprise_data.admin_analytics.constants import ResponseType
|
13
|
+
from enterprise_data.admin_analytics.utils import (
|
14
|
+
fetch_and_cache_engagements_data,
|
15
|
+
fetch_and_cache_enrollments_data,
|
16
|
+
fetch_engagements_cache_expiry_timestamp,
|
17
|
+
fetch_enrollments_cache_expiry_timestamp,
|
18
|
+
)
|
19
|
+
from enterprise_data.api.v1.paginators import AdvanceAnalyticsPagination
|
20
|
+
from enterprise_data.api.v1.serializers import AdvanceAnalyticsQueryParamSerializer
|
21
|
+
from enterprise_data.renderers import LeaderboardCSVRenderer
|
22
|
+
from enterprise_data.utils import date_filter
|
23
|
+
|
24
|
+
|
25
|
+
class AdvanceAnalyticsLeaderboardView(APIView):
|
26
|
+
"""
|
27
|
+
API for getting the advance analytics leaderboard data.
|
28
|
+
"""
|
29
|
+
authentication_classes = (JwtAuthentication,)
|
30
|
+
pagination_class = AdvanceAnalyticsPagination
|
31
|
+
http_method_names = ['get']
|
32
|
+
|
33
|
+
@permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
|
34
|
+
def get(self, request, enterprise_uuid):
|
35
|
+
"""Get leaderboard data"""
|
36
|
+
serializer = AdvanceAnalyticsQueryParamSerializer(data=request.GET)
|
37
|
+
serializer.is_valid(raise_exception=True)
|
38
|
+
|
39
|
+
enrollments_cache_expiry = fetch_enrollments_cache_expiry_timestamp()
|
40
|
+
enrollments_df = fetch_and_cache_enrollments_data(enterprise_uuid, enrollments_cache_expiry)
|
41
|
+
|
42
|
+
engagements_cache_expiry = fetch_engagements_cache_expiry_timestamp()
|
43
|
+
engagements_df = fetch_and_cache_engagements_data(enterprise_uuid, engagements_cache_expiry)
|
44
|
+
|
45
|
+
start_date = serializer.data.get('start_date', enrollments_df.enterprise_enrollment_date.min())
|
46
|
+
end_date = serializer.data.get('end_date', datetime.now())
|
47
|
+
response_type = serializer.data.get('response_type', ResponseType.JSON.value)
|
48
|
+
|
49
|
+
# only include learners who have passed the course
|
50
|
+
enrollments_df = enrollments_df[enrollments_df["has_passed"] == 1]
|
51
|
+
|
52
|
+
# filter enrollments by date
|
53
|
+
enrollments_df = date_filter(start_date, end_date, enrollments_df, "passed_date")
|
54
|
+
|
55
|
+
completions = enrollments_df.groupby(["email"]).size().reset_index()
|
56
|
+
completions.columns = ["email", "course_completions"]
|
57
|
+
|
58
|
+
# filter engagements by date
|
59
|
+
engagements_df = date_filter(start_date, end_date, engagements_df, "activity_date")
|
60
|
+
|
61
|
+
engage = (
|
62
|
+
engagements_df.groupby(["email"])
|
63
|
+
.agg({"is_engaged": ["sum"], "learning_time_seconds": ["sum"]})
|
64
|
+
.reset_index()
|
65
|
+
)
|
66
|
+
engage.columns = ["email", "daily_sessions", "learning_time_seconds"]
|
67
|
+
engage["learning_time_hours"] = round(
|
68
|
+
engage["learning_time_seconds"].astype("float") / 60 / 60, 1
|
69
|
+
)
|
70
|
+
engage["average_session_length"] = round(
|
71
|
+
engage["learning_time_hours"] / engage["daily_sessions"].astype("float"), 1
|
72
|
+
)
|
73
|
+
|
74
|
+
leaderboard_df = engage.merge(completions, on="email", how="left")
|
75
|
+
leaderboard_df = leaderboard_df.sort_values(
|
76
|
+
by=["learning_time_hours", "daily_sessions", "course_completions"],
|
77
|
+
ascending=[False, False, False],
|
78
|
+
)
|
79
|
+
|
80
|
+
# move the aggregated row with email 'null' to the end of the table
|
81
|
+
idx = leaderboard_df.index[leaderboard_df['email'] == 'null']
|
82
|
+
leaderboard_df.loc[idx, 'email'] = 'learners who have not shared consent'
|
83
|
+
leaderboard_df = pd.concat([leaderboard_df.drop(idx), leaderboard_df.loc[idx]])
|
84
|
+
|
85
|
+
# convert `nan` values to `None` because `nan` is not JSON serializable
|
86
|
+
leaderboard_df = leaderboard_df.replace(np.nan, None)
|
87
|
+
|
88
|
+
if response_type == ResponseType.CSV.value:
|
89
|
+
filename = f"""Leaderboard, {start_date} - {end_date}.csv"""
|
90
|
+
leaderboard_df = leaderboard_df[
|
91
|
+
[
|
92
|
+
"email",
|
93
|
+
"learning_time_hours",
|
94
|
+
"daily_sessions",
|
95
|
+
"average_session_length",
|
96
|
+
"course_completions",
|
97
|
+
]
|
98
|
+
]
|
99
|
+
return StreamingHttpResponse(
|
100
|
+
LeaderboardCSVRenderer().render(self._stream_serialized_data(leaderboard_df)),
|
101
|
+
content_type="text/csv",
|
102
|
+
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
103
|
+
)
|
104
|
+
|
105
|
+
paginator = self.pagination_class()
|
106
|
+
page = paginator.paginate_queryset(leaderboard_df, request)
|
107
|
+
serialized_data = page.data.to_dict(orient='records')
|
108
|
+
response = paginator.get_paginated_response(serialized_data)
|
109
|
+
|
110
|
+
return response
|
111
|
+
|
112
|
+
def _stream_serialized_data(self, leaderboard_df, chunk_size=50000):
|
113
|
+
"""
|
114
|
+
Stream the serialized data.
|
115
|
+
"""
|
116
|
+
total_rows = leaderboard_df.shape[0]
|
117
|
+
for start_index in range(0, total_rows, chunk_size):
|
118
|
+
end_index = min(start_index + chunk_size, total_rows)
|
119
|
+
chunk = leaderboard_df.iloc[start_index:end_index]
|
120
|
+
yield from chunk.to_dict(orient='records')
|
@@ -18,7 +18,7 @@ from enterprise_data.admin_analytics.completions_utils import (
|
|
18
18
|
get_top_courses_by_completions,
|
19
19
|
get_top_subjects_by_completions,
|
20
20
|
)
|
21
|
-
from enterprise_data.admin_analytics.constants import
|
21
|
+
from enterprise_data.admin_analytics.constants import Calculation, Granularity
|
22
22
|
from enterprise_data.admin_analytics.data_loaders import fetch_max_enrollment_datetime
|
23
23
|
from enterprise_data.admin_analytics.utils import ChartType, fetch_and_cache_enrollments_data
|
24
24
|
from enterprise_data.api.v1 import serializers
|
@@ -71,8 +71,8 @@ class EnterrpiseAdminCompletionsStatsView(APIView):
|
|
71
71
|
start_date=start_date,
|
72
72
|
end_date=end_date,
|
73
73
|
enrollments=enrollments.copy(),
|
74
|
-
date_agg=serializer.data.get('granularity',
|
75
|
-
calc=serializer.data.get('calculation',
|
74
|
+
date_agg=serializer.data.get('granularity', Granularity.DAILY.value),
|
75
|
+
calc=serializer.data.get('calculation', Calculation.TOTAL.value),
|
76
76
|
)
|
77
77
|
elif chart_type == ChartType.TOP_COURSES_BY_COMPLETIONS.value:
|
78
78
|
csv_data = get_csv_data_for_top_courses_by_completions(
|
@@ -91,8 +91,8 @@ class EnterrpiseAdminCompletionsStatsView(APIView):
|
|
91
91
|
start_date=start_date,
|
92
92
|
end_date=end_date,
|
93
93
|
dff=enrollments.copy(),
|
94
|
-
date_agg=serializer.data.get('granularity',
|
95
|
-
calc=serializer.data.get('calculation',
|
94
|
+
date_agg=serializer.data.get('granularity', Granularity.DAILY.value),
|
95
|
+
calc=serializer.data.get('calculation', Calculation.TOTAL.value),
|
96
96
|
)
|
97
97
|
top_courses_by_completions = get_top_courses_by_completions(
|
98
98
|
start_date=start_date, end_date=end_date, dff=enrollments.copy()
|
enterprise_data/renderers.py
CHANGED
@@ -43,3 +43,17 @@ class IndividualEnrollmentsCSVRenderer(CSVStreamingRenderer):
|
|
43
43
|
'enroll_type',
|
44
44
|
'enterprise_enrollment_date',
|
45
45
|
]
|
46
|
+
|
47
|
+
|
48
|
+
class LeaderboardCSVRenderer(CSVStreamingRenderer):
|
49
|
+
"""
|
50
|
+
Custom streaming csv renderer for advance analytics leaderboard data.
|
51
|
+
"""
|
52
|
+
|
53
|
+
header = [
|
54
|
+
'email',
|
55
|
+
'learning_time_hours',
|
56
|
+
'daily_sessions',
|
57
|
+
'average_session_length',
|
58
|
+
'course_completions',
|
59
|
+
]
|
@@ -0,0 +1,501 @@
|
|
1
|
+
"""Mock data for enrollments"""
|
2
|
+
|
3
|
+
import pandas as pd
|
4
|
+
|
5
|
+
from enterprise_data.admin_analytics.constants import EnrollmentChart
|
6
|
+
from enterprise_data.admin_analytics.utils import ChartType
|
7
|
+
|
8
|
+
ENROLLMENTS = [
|
9
|
+
{
|
10
|
+
"enterprise_customer_name": "Hill Ltd",
|
11
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
12
|
+
"lms_enrollment_id": 1013,
|
13
|
+
"user_id": 8907,
|
14
|
+
"email": "rebeccanelson@example.com",
|
15
|
+
"course_key": "hEmW+tvk03",
|
16
|
+
"courserun_key": "course-v1:hEmW+tvk03+1T9889",
|
17
|
+
"course_id": "1681",
|
18
|
+
"course_subject": "business-management",
|
19
|
+
"course_title": "Re-engineered tangible approach",
|
20
|
+
"enterprise_enrollment_date": "2021-07-04",
|
21
|
+
"lms_enrollment_mode": "verified",
|
22
|
+
"enroll_type": "certificate",
|
23
|
+
"program_title": "Non-Program",
|
24
|
+
"date_certificate_awarded": "2021-08-25",
|
25
|
+
"grade_percent": 0.99,
|
26
|
+
"cert_awarded": 1,
|
27
|
+
"date_certificate_created_raw": "2021-08-25",
|
28
|
+
"passed_date_raw": "2021-08-25",
|
29
|
+
"passed_date": "2021-08-25",
|
30
|
+
"has_passed": 1,
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"enterprise_customer_name": "Hill Ltd",
|
34
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
35
|
+
"lms_enrollment_id": 9172,
|
36
|
+
"user_id": 8369,
|
37
|
+
"email": "taylorjames@example.com",
|
38
|
+
"course_key": "hEmW+tvk03",
|
39
|
+
"courserun_key": "course-v1:hEmW+tvk03+1T9889",
|
40
|
+
"course_id": "1681",
|
41
|
+
"course_subject": "business-management",
|
42
|
+
"course_title": "Re-engineered tangible approach",
|
43
|
+
"enterprise_enrollment_date": "2021-07-03",
|
44
|
+
"lms_enrollment_mode": "verified",
|
45
|
+
"enroll_type": "certificate",
|
46
|
+
"program_title": "Non-Program",
|
47
|
+
"date_certificate_awarded": "2021-09-01",
|
48
|
+
"grade_percent": 0.93,
|
49
|
+
"cert_awarded": 1,
|
50
|
+
"date_certificate_created_raw": "2021-09-01",
|
51
|
+
"passed_date_raw": "2021-09-01",
|
52
|
+
"passed_date": "2021-09-01",
|
53
|
+
"has_passed": 1,
|
54
|
+
},
|
55
|
+
{
|
56
|
+
"enterprise_customer_name": "Hill Ltd",
|
57
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
58
|
+
"lms_enrollment_id": 9552,
|
59
|
+
"user_id": 8719,
|
60
|
+
"email": "ssmith@example.com",
|
61
|
+
"course_key": "qZJC+KFX86",
|
62
|
+
"courserun_key": "course-v1:qZJC+KFX86+1T8918",
|
63
|
+
"course_id": "1725",
|
64
|
+
"course_subject": "medicine",
|
65
|
+
"course_title": "Secured static capability",
|
66
|
+
"enterprise_enrollment_date": "2021-05-11",
|
67
|
+
"lms_enrollment_mode": "verified",
|
68
|
+
"enroll_type": "certificate",
|
69
|
+
"program_title": "Non-Program",
|
70
|
+
"date_certificate_awarded": None,
|
71
|
+
"grade_percent": 0.0,
|
72
|
+
"cert_awarded": 0,
|
73
|
+
"date_certificate_created_raw": None,
|
74
|
+
"passed_date_raw": None,
|
75
|
+
"passed_date": None,
|
76
|
+
"has_passed": 0,
|
77
|
+
},
|
78
|
+
{
|
79
|
+
"enterprise_customer_name": "Hill Ltd",
|
80
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
81
|
+
"lms_enrollment_id": 3436,
|
82
|
+
"user_id": 3125,
|
83
|
+
"email": "kathleenmartin@example.com",
|
84
|
+
"course_key": "QWXx+Jqz64",
|
85
|
+
"courserun_key": "course-v1:QWXx+Jqz64+1T9449",
|
86
|
+
"course_id": "4878",
|
87
|
+
"course_subject": "social-sciences",
|
88
|
+
"course_title": "Horizontal solution-oriented hub",
|
89
|
+
"enterprise_enrollment_date": "2020-04-03",
|
90
|
+
"lms_enrollment_mode": "verified",
|
91
|
+
"enroll_type": "certificate",
|
92
|
+
"program_title": "Non-Program",
|
93
|
+
"date_certificate_awarded": None,
|
94
|
+
"grade_percent": 0.0,
|
95
|
+
"cert_awarded": 0,
|
96
|
+
"date_certificate_created_raw": None,
|
97
|
+
"passed_date_raw": None,
|
98
|
+
"passed_date": None,
|
99
|
+
"has_passed": 0,
|
100
|
+
},
|
101
|
+
{
|
102
|
+
"enterprise_customer_name": "Hill Ltd",
|
103
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
104
|
+
"lms_enrollment_id": 5934,
|
105
|
+
"user_id": 4853,
|
106
|
+
"email": "amber79@example.com",
|
107
|
+
"course_key": "NOGk+UVD31",
|
108
|
+
"courserun_key": "course-v1:NOGk+UVD31+1T4956",
|
109
|
+
"course_id": "4141",
|
110
|
+
"course_subject": "communication",
|
111
|
+
"course_title": "Streamlined zero-defect attitude",
|
112
|
+
"enterprise_enrollment_date": "2020-04-08",
|
113
|
+
"lms_enrollment_mode": "verified",
|
114
|
+
"enroll_type": "certificate",
|
115
|
+
"program_title": "Non-Program",
|
116
|
+
"date_certificate_awarded": None,
|
117
|
+
"grade_percent": 0.0,
|
118
|
+
"cert_awarded": 0,
|
119
|
+
"date_certificate_created_raw": None,
|
120
|
+
"passed_date_raw": None,
|
121
|
+
"passed_date": None,
|
122
|
+
"has_passed": 0,
|
123
|
+
},
|
124
|
+
]
|
125
|
+
|
126
|
+
ENGAGEMENTS = [
|
127
|
+
{
|
128
|
+
"user_id": 2340,
|
129
|
+
"email": "padillamichelle@example.org",
|
130
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
131
|
+
"course_key": "luGg+KNt30",
|
132
|
+
"enroll_type": "certificate",
|
133
|
+
"activity_date": "2021-08-05",
|
134
|
+
"course_title": "Synergized reciprocal encoding",
|
135
|
+
"course_subject": "business-management",
|
136
|
+
"is_engaged": 1,
|
137
|
+
"is_engaged_video": 1,
|
138
|
+
"is_engaged_forum": 0,
|
139
|
+
"is_engaged_problem": 1,
|
140
|
+
"is_active": 1,
|
141
|
+
"learning_time_seconds": 3724,
|
142
|
+
},
|
143
|
+
{
|
144
|
+
"user_id": 1869,
|
145
|
+
"email": "yallison@example.org",
|
146
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
147
|
+
"course_key": "luGg+KNt30",
|
148
|
+
"enroll_type": "certificate",
|
149
|
+
"activity_date": "2021-07-27",
|
150
|
+
"course_title": "Synergized reciprocal encoding",
|
151
|
+
"course_subject": "business-management",
|
152
|
+
"is_engaged": 1,
|
153
|
+
"is_engaged_video": 1,
|
154
|
+
"is_engaged_forum": 0,
|
155
|
+
"is_engaged_problem": 1,
|
156
|
+
"is_active": 1,
|
157
|
+
"learning_time_seconds": 4335,
|
158
|
+
},
|
159
|
+
{
|
160
|
+
"user_id": 6962,
|
161
|
+
"email": "weaverpatricia@example.net",
|
162
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
163
|
+
"course_key": "luGg+KNt30",
|
164
|
+
"enroll_type": "certificate",
|
165
|
+
"activity_date": "2021-08-05",
|
166
|
+
"course_title": "Synergized reciprocal encoding",
|
167
|
+
"course_subject": "business-management",
|
168
|
+
"is_engaged": 1,
|
169
|
+
"is_engaged_video": 1,
|
170
|
+
"is_engaged_forum": 0,
|
171
|
+
"is_engaged_problem": 1,
|
172
|
+
"is_active": 1,
|
173
|
+
"learning_time_seconds": 9441,
|
174
|
+
},
|
175
|
+
{
|
176
|
+
"user_id": 6798,
|
177
|
+
"email": "seth57@example.org",
|
178
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
179
|
+
"course_key": "luGg+KNt30",
|
180
|
+
"enroll_type": "certificate",
|
181
|
+
"activity_date": "2021-08-21",
|
182
|
+
"course_title": "Synergized reciprocal encoding",
|
183
|
+
"course_subject": "business-management",
|
184
|
+
"is_engaged": 1,
|
185
|
+
"is_engaged_video": 1,
|
186
|
+
"is_engaged_forum": 1,
|
187
|
+
"is_engaged_problem": 1,
|
188
|
+
"is_active": 1,
|
189
|
+
"learning_time_seconds": 9898,
|
190
|
+
},
|
191
|
+
{
|
192
|
+
"user_id": 8530,
|
193
|
+
"email": "mackwilliam@example.com",
|
194
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
195
|
+
"course_key": "luGg+KNt30",
|
196
|
+
"enroll_type": "certificate",
|
197
|
+
"activity_date": "2021-10-24",
|
198
|
+
"course_title": "Synergized reciprocal encoding",
|
199
|
+
"course_subject": "business-management",
|
200
|
+
"is_engaged": 0,
|
201
|
+
"is_engaged_video": 0,
|
202
|
+
"is_engaged_forum": 0,
|
203
|
+
"is_engaged_problem": 0,
|
204
|
+
"is_active": 1,
|
205
|
+
"learning_time_seconds": 0,
|
206
|
+
},
|
207
|
+
{
|
208
|
+
"user_id": 7584,
|
209
|
+
"email": "graceperez@example.com",
|
210
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
211
|
+
"course_key": "luGg+KNt30",
|
212
|
+
"enroll_type": "certificate",
|
213
|
+
"activity_date": "2022-05-17",
|
214
|
+
"course_title": "Synergized reciprocal encoding",
|
215
|
+
"course_subject": "business-management",
|
216
|
+
"is_engaged": 0,
|
217
|
+
"is_engaged_video": 0,
|
218
|
+
"is_engaged_forum": 0,
|
219
|
+
"is_engaged_problem": 0,
|
220
|
+
"is_active": 1,
|
221
|
+
"learning_time_seconds": 21,
|
222
|
+
},
|
223
|
+
{
|
224
|
+
"user_id": 68,
|
225
|
+
"email": "yferguson@example.net",
|
226
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
227
|
+
"course_key": "luGg+KNt30",
|
228
|
+
"enroll_type": "certificate",
|
229
|
+
"activity_date": "2021-09-02",
|
230
|
+
"course_title": "Synergized reciprocal encoding",
|
231
|
+
"course_subject": "business-management",
|
232
|
+
"is_engaged": 1,
|
233
|
+
"is_engaged_video": 1,
|
234
|
+
"is_engaged_forum": 0,
|
235
|
+
"is_engaged_problem": 1,
|
236
|
+
"is_active": 1,
|
237
|
+
"learning_time_seconds": 4747,
|
238
|
+
},
|
239
|
+
{
|
240
|
+
"user_id": 4278,
|
241
|
+
"email": "caseyjohnny@example.com",
|
242
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
243
|
+
"course_key": "Kcpr+XoR30",
|
244
|
+
"enroll_type": "certificate",
|
245
|
+
"activity_date": "2022-05-20",
|
246
|
+
"course_title": "Assimilated even-keeled focus group",
|
247
|
+
"course_subject": "engineering",
|
248
|
+
"is_engaged": 0,
|
249
|
+
"is_engaged_video": 0,
|
250
|
+
"is_engaged_forum": 0,
|
251
|
+
"is_engaged_problem": 0,
|
252
|
+
"is_active": 1,
|
253
|
+
"learning_time_seconds": 0,
|
254
|
+
},
|
255
|
+
{
|
256
|
+
"user_id": 8726,
|
257
|
+
"email": "webertodd@example.com",
|
258
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
259
|
+
"course_key": "luGg+KNt30",
|
260
|
+
"enroll_type": "certificate",
|
261
|
+
"activity_date": "2021-09-21",
|
262
|
+
"course_title": "Synergized reciprocal encoding",
|
263
|
+
"course_subject": "business-management",
|
264
|
+
"is_engaged": 1,
|
265
|
+
"is_engaged_video": 1,
|
266
|
+
"is_engaged_forum": 1,
|
267
|
+
"is_engaged_problem": 1,
|
268
|
+
"is_active": 1,
|
269
|
+
"learning_time_seconds": 5285,
|
270
|
+
},
|
271
|
+
{
|
272
|
+
"user_id": 282,
|
273
|
+
"email": "crystal86@example.net",
|
274
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
275
|
+
"course_key": "luGg+KNt30",
|
276
|
+
"enroll_type": "certificate",
|
277
|
+
"activity_date": "2021-07-11",
|
278
|
+
"course_title": "Synergized reciprocal encoding",
|
279
|
+
"course_subject": "business-management",
|
280
|
+
"is_engaged": 0,
|
281
|
+
"is_engaged_video": 0,
|
282
|
+
"is_engaged_forum": 0,
|
283
|
+
"is_engaged_problem": 0,
|
284
|
+
"is_active": 1,
|
285
|
+
"learning_time_seconds": 0,
|
286
|
+
},
|
287
|
+
{
|
288
|
+
"user_id": 2731,
|
289
|
+
"email": "paul77@example.org",
|
290
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
291
|
+
"course_key": "luGg+KNt30",
|
292
|
+
"enroll_type": "certificate",
|
293
|
+
"activity_date": "2021-07-26",
|
294
|
+
"course_title": "Synergized reciprocal encoding",
|
295
|
+
"course_subject": "business-management",
|
296
|
+
"is_engaged": 1,
|
297
|
+
"is_engaged_video": 1,
|
298
|
+
"is_engaged_forum": 1,
|
299
|
+
"is_engaged_problem": 1,
|
300
|
+
"is_active": 1,
|
301
|
+
"learning_time_seconds": 15753,
|
302
|
+
},
|
303
|
+
{
|
304
|
+
"user_id": 4038,
|
305
|
+
"email": "samanthaclarke@example.org",
|
306
|
+
"enterprise_customer_uuid": "33ce656295e04ecfa2a77d407eb96f69",
|
307
|
+
"course_key": "luGg+KNt30",
|
308
|
+
"enroll_type": "certificate",
|
309
|
+
"activity_date": "2021-07-19",
|
310
|
+
"course_title": "Synergized reciprocal encoding",
|
311
|
+
"course_subject": "business-management",
|
312
|
+
"is_engaged": 0,
|
313
|
+
"is_engaged_video": 0,
|
314
|
+
"is_engaged_forum": 0,
|
315
|
+
"is_engaged_problem": 0,
|
316
|
+
"is_active": 1,
|
317
|
+
"learning_time_seconds": 29,
|
318
|
+
}
|
319
|
+
]
|
320
|
+
|
321
|
+
ENROLLMENT_STATS_CSVS = {
|
322
|
+
EnrollmentChart.ENROLLMENTS_OVER_TIME.value: (
|
323
|
+
b'enterprise_enrollment_date,certificate\n'
|
324
|
+
b'2020-04-03,1\n'
|
325
|
+
b'2020-04-08,1\n'
|
326
|
+
b'2021-05-11,1\n'
|
327
|
+
b'2021-07-03,1\n'
|
328
|
+
b'2021-07-04,1\n'
|
329
|
+
),
|
330
|
+
EnrollmentChart.TOP_COURSES_BY_ENROLLMENTS.value: (
|
331
|
+
b'course_key,course_title,certificate\n'
|
332
|
+
b'NOGk+UVD31,Streamlined zero-defect attitude,1\n'
|
333
|
+
b'QWXx+Jqz64,Horizontal solution-oriented hub,1\n'
|
334
|
+
b'hEmW+tvk03,Re-engineered tangible approach,2\n'
|
335
|
+
b'qZJC+KFX86,Secured static capability,1\n'
|
336
|
+
),
|
337
|
+
EnrollmentChart.TOP_SUBJECTS_BY_ENROLLMENTS.value: (
|
338
|
+
b'course_subject,certificate\nbusiness-management,2\ncommunication,1\nmedicine,1\nsocial-sciences,1\n'
|
339
|
+
)
|
340
|
+
}
|
341
|
+
|
342
|
+
|
343
|
+
def enrollments_dataframe():
|
344
|
+
"""Return a DataFrame of enrollments."""
|
345
|
+
enrollments = pd.DataFrame(ENROLLMENTS)
|
346
|
+
|
347
|
+
enrollments['enterprise_enrollment_date'] = enrollments['enterprise_enrollment_date'].astype('datetime64[ns]')
|
348
|
+
enrollments['date_certificate_awarded'] = enrollments['date_certificate_awarded'].astype('datetime64[ns]')
|
349
|
+
enrollments['date_certificate_created_raw'] = enrollments['date_certificate_created_raw'].astype('datetime64[ns]')
|
350
|
+
enrollments['passed_date_raw'] = enrollments['passed_date_raw'].astype('datetime64[ns]')
|
351
|
+
enrollments['passed_date'] = enrollments['passed_date'].astype('datetime64[ns]')
|
352
|
+
|
353
|
+
return enrollments
|
354
|
+
|
355
|
+
|
356
|
+
def engagements_dataframe():
|
357
|
+
"""Return a DataFrame of engagements."""
|
358
|
+
engagements = pd.DataFrame(ENGAGEMENTS)
|
359
|
+
engagements['activity_date'] = engagements['activity_date'].astype('datetime64[ns]')
|
360
|
+
return engagements
|
361
|
+
|
362
|
+
|
363
|
+
def enrollments_csv_content():
|
364
|
+
"""Return the CSV content of enrollments."""
|
365
|
+
return (
|
366
|
+
b'email,course_title,course_subject,enroll_type,enterprise_enrollment_date\r\n'
|
367
|
+
b'rebeccanelson@example.com,Re-engineered tangible approach,business-management,certificate,2021-07-04\r\n'
|
368
|
+
b'taylorjames@example.com,Re-engineered tangible approach,business-management,certificate,2021-07-03\r\n'
|
369
|
+
b'ssmith@example.com,Secured static capability,medicine,certificate,2021-05-11\r\n'
|
370
|
+
b'amber79@example.com,Streamlined zero-defect attitude,communication,certificate,2020-04-08\r\n'
|
371
|
+
b'kathleenmartin@example.com,Horizontal solution-oriented hub,social-sciences,certificate,2020-04-03\r\n'
|
372
|
+
)
|
373
|
+
|
374
|
+
|
375
|
+
def leaderboard_csv_content():
|
376
|
+
"""Return the CSV content of leaderboard."""
|
377
|
+
return (
|
378
|
+
b'email,learning_time_hours,daily_sessions,average_session_length,course_completions\r\n'
|
379
|
+
b'paul77@example.org,4.4,1,4.4,\r\nseth57@example.org,2.7,1,2.7,\r\n'
|
380
|
+
b'weaverpatricia@example.net,2.6,1,2.6,\r\nwebertodd@example.com,1.5,1,1.5,\r\n'
|
381
|
+
b'yferguson@example.net,1.3,1,1.3,\r\nyallison@example.org,1.2,1,1.2,\r\n'
|
382
|
+
b'padillamichelle@example.org,1.0,1,1.0,\r\ncaseyjohnny@example.com,0.0,0,,\r\n'
|
383
|
+
b'crystal86@example.net,0.0,0,,\r\ngraceperez@example.com,0.0,0,,\r\n'
|
384
|
+
b'mackwilliam@example.com,0.0,0,,\r\nsamanthaclarke@example.org,0.0,0,,\r\n'
|
385
|
+
)
|
386
|
+
|
387
|
+
|
388
|
+
LEADERBOARD_RESPONSE = [
|
389
|
+
{
|
390
|
+
"email": "paul77@example.org",
|
391
|
+
"daily_sessions": 1,
|
392
|
+
"learning_time_seconds": 15753,
|
393
|
+
"learning_time_hours": 4.4,
|
394
|
+
"average_session_length": 4.4,
|
395
|
+
"course_completions": None,
|
396
|
+
},
|
397
|
+
{
|
398
|
+
"email": "seth57@example.org",
|
399
|
+
"daily_sessions": 1,
|
400
|
+
"learning_time_seconds": 9898,
|
401
|
+
"learning_time_hours": 2.7,
|
402
|
+
"average_session_length": 2.7,
|
403
|
+
"course_completions": None,
|
404
|
+
},
|
405
|
+
{
|
406
|
+
"email": "weaverpatricia@example.net",
|
407
|
+
"daily_sessions": 1,
|
408
|
+
"learning_time_seconds": 9441,
|
409
|
+
"learning_time_hours": 2.6,
|
410
|
+
"average_session_length": 2.6,
|
411
|
+
"course_completions": None,
|
412
|
+
},
|
413
|
+
{
|
414
|
+
"email": "webertodd@example.com",
|
415
|
+
"daily_sessions": 1,
|
416
|
+
"learning_time_seconds": 5285,
|
417
|
+
"learning_time_hours": 1.5,
|
418
|
+
"average_session_length": 1.5,
|
419
|
+
"course_completions": None,
|
420
|
+
},
|
421
|
+
{
|
422
|
+
"email": "yferguson@example.net",
|
423
|
+
"daily_sessions": 1,
|
424
|
+
"learning_time_seconds": 4747,
|
425
|
+
"learning_time_hours": 1.3,
|
426
|
+
"average_session_length": 1.3,
|
427
|
+
"course_completions": None,
|
428
|
+
},
|
429
|
+
{
|
430
|
+
"email": "yallison@example.org",
|
431
|
+
"daily_sessions": 1,
|
432
|
+
"learning_time_seconds": 4335,
|
433
|
+
"learning_time_hours": 1.2,
|
434
|
+
"average_session_length": 1.2,
|
435
|
+
"course_completions": None,
|
436
|
+
},
|
437
|
+
{
|
438
|
+
"email": "padillamichelle@example.org",
|
439
|
+
"daily_sessions": 1,
|
440
|
+
"learning_time_seconds": 3724,
|
441
|
+
"learning_time_hours": 1.0,
|
442
|
+
"average_session_length": 1.0,
|
443
|
+
"course_completions": None,
|
444
|
+
},
|
445
|
+
{
|
446
|
+
"email": "caseyjohnny@example.com",
|
447
|
+
"daily_sessions": 0,
|
448
|
+
"learning_time_seconds": 0,
|
449
|
+
"learning_time_hours": 0.0,
|
450
|
+
"average_session_length": None,
|
451
|
+
"course_completions": None,
|
452
|
+
},
|
453
|
+
{
|
454
|
+
"email": "crystal86@example.net",
|
455
|
+
"daily_sessions": 0,
|
456
|
+
"learning_time_seconds": 0,
|
457
|
+
"learning_time_hours": 0.0,
|
458
|
+
"average_session_length": None,
|
459
|
+
"course_completions": None,
|
460
|
+
},
|
461
|
+
{
|
462
|
+
"email": "graceperez@example.com",
|
463
|
+
"daily_sessions": 0,
|
464
|
+
"learning_time_seconds": 21,
|
465
|
+
"learning_time_hours": 0.0,
|
466
|
+
"average_session_length": None,
|
467
|
+
"course_completions": None,
|
468
|
+
},
|
469
|
+
{
|
470
|
+
"email": "mackwilliam@example.com",
|
471
|
+
"daily_sessions": 0,
|
472
|
+
"learning_time_seconds": 0,
|
473
|
+
"learning_time_hours": 0.0,
|
474
|
+
"average_session_length": None,
|
475
|
+
"course_completions": None,
|
476
|
+
},
|
477
|
+
{
|
478
|
+
"email": "samanthaclarke@example.org",
|
479
|
+
"daily_sessions": 0,
|
480
|
+
"learning_time_seconds": 29,
|
481
|
+
"learning_time_hours": 0.0,
|
482
|
+
"average_session_length": None,
|
483
|
+
"course_completions": None,
|
484
|
+
},
|
485
|
+
]
|
486
|
+
|
487
|
+
COMPLETIONS_STATS_CSVS = {
|
488
|
+
ChartType.COMPLETIONS_OVER_TIME.value: (
|
489
|
+
b'passed_date,certificate\n'
|
490
|
+
b'2021-08-25,1\n'
|
491
|
+
b'2021-09-01,2\n'
|
492
|
+
),
|
493
|
+
ChartType.TOP_COURSES_BY_COMPLETIONS.value: (
|
494
|
+
b'course_key,course_title,certificate\n'
|
495
|
+
b'hEmW+tvk03,Re-engineered tangible approach,2\n'
|
496
|
+
),
|
497
|
+
ChartType.TOP_SUBJECTS_BY_COMPLETIONS.value: (
|
498
|
+
b'course_subject,certificate\n'
|
499
|
+
b'business-management,2\n'
|
500
|
+
)
|
501
|
+
}
|