edx-enterprise-data 9.7.1__py3-none-any.whl → 9.7.2__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-9.7.1.dist-info → edx_enterprise_data-9.7.2.dist-info}/METADATA +1 -1
- {edx_enterprise_data-9.7.1.dist-info → edx_enterprise_data-9.7.2.dist-info}/RECORD +14 -14
- enterprise_data/__init__.py +1 -1
- enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py +107 -12
- enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py +101 -24
- enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py +71 -6
- enterprise_data/api/v1/views/analytics_leaderboard.py +6 -4
- enterprise_data/management/commands/pre_warm_analytics_cache.py +5 -4
- enterprise_data/management/commands/tests/test_pre_warm_analytics_cache.py +14 -5
- enterprise_data/utils.py +17 -0
- enterprise_data_roles/rules.py +1 -1
- {edx_enterprise_data-9.7.1.dist-info → edx_enterprise_data-9.7.2.dist-info}/LICENSE +0 -0
- {edx_enterprise_data-9.7.1.dist-info → edx_enterprise_data-9.7.2.dist-info}/WHEEL +0 -0
- {edx_enterprise_data-9.7.1.dist-info → edx_enterprise_data-9.7.2.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
enterprise_data/__init__.py,sha256=
|
1
|
+
enterprise_data/__init__.py,sha256=eisefvuQWCcSg8_PJEcswjI_4FLGhJInZwRr5GjDMC0,123
|
2
2
|
enterprise_data/apps.py,sha256=aF6hZwDfI2oWj95tUTm_2ikHueQj-jLj-u0GrgzpsQI,414
|
3
3
|
enterprise_data/clients.py,sha256=GvQupy5TVYfO_IKC3yzXSAgNP54r-PtIjidM5ws9Iks,3947
|
4
4
|
enterprise_data/constants.py,sha256=uCKjfpdlMYFZJsAj3n9RMw4Cmg5_6s3NuwocO-fch3s,238
|
@@ -8,19 +8,19 @@ enterprise_data/paginators.py,sha256=YPrC5TeXFt-ymenT2H8H2nCbDCnAzJQlH9kFPElRxWE
|
|
8
8
|
enterprise_data/renderers.py,sha256=d_bJZjeUTyHRBBtpCcslrTyldv6IMYQ_QW-GWijwGHU,3026
|
9
9
|
enterprise_data/signals.py,sha256=8eqNPnlvmfsKf19lGWv5xTIuBgQIqR8EZSp9UYzC8Rc,1024
|
10
10
|
enterprise_data/urls.py,sha256=bqtKF5OEWEwrNmHG3os-pZNuNsmjlhxEqp7yM4TbPf4,243
|
11
|
-
enterprise_data/utils.py,sha256=
|
11
|
+
enterprise_data/utils.py,sha256=sDrpBd62DpybCV41QCxRUaCuvch3qKjEhfUp9cA_GV0,2952
|
12
12
|
enterprise_data/admin_analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
13
|
enterprise_data/admin_analytics/constants.py,sha256=-6uLAq5DUeA_rv5eUb9SeqlG3iVWV30qUS8asbK4430,160
|
14
14
|
enterprise_data/admin_analytics/data_loaders.py,sha256=NixI-4M3D4MnI279x5hqqTw84uKpQy0TRib_g-0Bt5Q,726
|
15
15
|
enterprise_data/admin_analytics/database/__init__.py,sha256=vNSWKf2VV5xMegN7htJJtxtQEb0ASLC6frE2w0ZpYpE,104
|
16
16
|
enterprise_data/admin_analytics/database/utils.py,sha256=5u-d6ZQW95mF_r4bH8Xdi7DgpYAuDFOG_q0P-bjKXHU,1712
|
17
17
|
enterprise_data/admin_analytics/database/queries/__init__.py,sha256=IC5TLOr_GnydbrVbl2mWhwO3aUbYeHuDmfPTLmwGhZA,218
|
18
|
-
enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py,sha256=
|
19
|
-
enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py,sha256=
|
18
|
+
enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py,sha256=eLy9GT0gFGLbw8yZZdEaeCAw0pTEUsy0-Ud5g2T9T80,10836
|
19
|
+
enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py,sha256=w30BiDOcKzV_FCdtxIG-_7zSCu5f3x5pgBRFCGjHu04,11011
|
20
20
|
enterprise_data/admin_analytics/database/queries/skills_daily_rollup_admin_dash.py,sha256=PgWwvtVCK5lbiq6z44lH0fwbkdWYukhyXZL9X8lNWCY,4099
|
21
21
|
enterprise_data/admin_analytics/database/tables/__init__.py,sha256=Z-c3P9hqR-dC9uYKe63qHkQG9Nms8cLE2jRN-4jeMM0,289
|
22
22
|
enterprise_data/admin_analytics/database/tables/base.py,sha256=1KyKsC18pW3m-5U-T6pdt5rIwsz6Wp3QFFbD3r6L6YQ,395
|
23
|
-
enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py,sha256=
|
23
|
+
enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py,sha256=675LqIERXTbQ3m2deoj10Xsm88nnEzepIRvJMYKHbl8,12840
|
24
24
|
enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py,sha256=cRRBFRc2p54BkW_h7GuUud-gWzJrqKep9dYgxOf7tIY,11741
|
25
25
|
enterprise_data/admin_analytics/database/tables/skills_daily_rollup_admin_dash.py,sha256=3xNwSi0wfCyBHcXPd6-9Ujs1NUm8kmZRg_gPrZzp9nQ,3233
|
26
26
|
enterprise_data/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -36,7 +36,7 @@ enterprise_data/api/v1/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
36
36
|
enterprise_data/api/v1/views/analytics_completions.py,sha256=xT9ywSRNjbQNLJjsm2N4cCyAXeibXY1800uirDn1prg,6260
|
37
37
|
enterprise_data/api/v1/views/analytics_engagements.py,sha256=TK6i0tSk0QObda0ysunEFzYrlsvLvSEypJRLJEi7JCk,6298
|
38
38
|
enterprise_data/api/v1/views/analytics_enrollments.py,sha256=6QRjzWapvc6jP3rv2QkGzfZMR2kRcsKqY3xRkvpIyRQ,6282
|
39
|
-
enterprise_data/api/v1/views/analytics_leaderboard.py,sha256=
|
39
|
+
enterprise_data/api/v1/views/analytics_leaderboard.py,sha256=3dyo7_OhyGEEeibemBrRsUOo0jbM4LbDgV5gw3YnVig,4186
|
40
40
|
enterprise_data/api/v1/views/base.py,sha256=Kkmd5zgEBAhvwS_GoGXSK6lgbDNwSPioYn-QbnizI3w,3416
|
41
41
|
enterprise_data/api/v1/views/enterprise_admin.py,sha256=DyN6RS4qY8sgKNIvcx1nRmSGuPm2pLYHZeh5YHXlH2w,8209
|
42
42
|
enterprise_data/api/v1/views/enterprise_learner.py,sha256=NqI_Tlz5v3p4fYZe2RRCg54AizslZdHB3Ckh8YQrhIM,18163
|
@@ -54,14 +54,14 @@ enterprise_data/management/commands/create_enterprise_learner_enrollment_lpr_v1.
|
|
54
54
|
enterprise_data/management/commands/create_enterprise_learner_lpr_v1.py,sha256=bUYmZHA3yK3ZBPhV0wkpRDgH_Q2b5rVQnwSp2hRmh28,1799
|
55
55
|
enterprise_data/management/commands/create_enterprise_offer.py,sha256=0R1eEKWTCGjod4I8qBH2UBD-erqj5mFtM_DG5Vxet_0,1150
|
56
56
|
enterprise_data/management/commands/create_enterprise_user.py,sha256=V_kvOSPZ1DXfAdF1W3AwAtavEYjdYaHBjjfzndZP8lk,1498
|
57
|
-
enterprise_data/management/commands/pre_warm_analytics_cache.py,sha256=
|
57
|
+
enterprise_data/management/commands/pre_warm_analytics_cache.py,sha256=3MCThZiZgfveD3NqFt95WTJ9LFTYxN8ZYbqWF1JinI8,8327
|
58
58
|
enterprise_data/management/commands/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
59
59
|
enterprise_data/management/commands/tests/test_create_dummy_data_lpr_v1.py,sha256=wt9fqAFKQVTqllpZ42dch-n31JavUifUIB9CKNYcnYM,1086
|
60
60
|
enterprise_data/management/commands/tests/test_create_enterprise_enrollment.py,sha256=5CABLk8qAx8RP8mrFtnbJ4-xVkf9-5Mq6iQcx8jBfFc,1742
|
61
61
|
enterprise_data/management/commands/tests/test_create_enterprise_learner_enrollment_lpr_v1.py,sha256=FzOBHcd1FfSVN5AI8oAKvmqj-W8Y-V7x9yJx9R1WrLQ,3845
|
62
62
|
enterprise_data/management/commands/tests/test_create_enterprise_learner_lpr_v1.py,sha256=P7wyyaZ4LMWv1umpCThp4By5DWjtI7kYAmY0AeuY2XI,1637
|
63
63
|
enterprise_data/management/commands/tests/test_create_enterprise_user.py,sha256=0uxG332-jYpaxUIrMtIVRTwvRfxTWEQBIwZlIQO7f2g,1469
|
64
|
-
enterprise_data/management/commands/tests/test_pre_warm_analytics_cache.py,sha256=
|
64
|
+
enterprise_data/management/commands/tests/test_pre_warm_analytics_cache.py,sha256=Nbwsl5CYMbi5cN2IwmUtTs5b_MeU6T0lquk3x8Tuh_4,2108
|
65
65
|
enterprise_data/migrations/0001_initial.py,sha256=g2fNJGLxELthmYrJlPB7cBxbsLWB6u-MotQvE3t95ww,2358
|
66
66
|
enterprise_data/migrations/0002_auto_20180430_1358.py,sha256=roRSgWy2p4hFefNFaajg-q_e8_5FHZih_ou1dLTwWQk,600
|
67
67
|
enterprise_data/migrations/0003_auto_20180501_0603.py,sha256=N9C5QcdRMfzTycO6xqdi7WPkwrktn3y3NEsm30L4pS8,415
|
@@ -136,7 +136,7 @@ enterprise_data_roles/admin.py,sha256=QNP0VeWE092vZzpyxOA5UJK1nNGl5e71B1J0RCwo_n
|
|
136
136
|
enterprise_data_roles/apps.py,sha256=nKi8TyuQ5Q6WGtKs5QeXvUTc3N-YQjKhyBnm2EM3Bng,260
|
137
137
|
enterprise_data_roles/constants.py,sha256=7yHmbAyqeXNX-lg3AC1caFsv97mouAS4NFIrHhwGgQ0,476
|
138
138
|
enterprise_data_roles/models.py,sha256=b4weAWOfQ1sMnODXQBcq4IsclR2NYWWxuOZ95fKnEfs,1679
|
139
|
-
enterprise_data_roles/rules.py,sha256=
|
139
|
+
enterprise_data_roles/rules.py,sha256=4W_qmVb3lQzTJqKqNXSRhW6cjKoZTygnj_4rXB_zljM,1900
|
140
140
|
enterprise_data_roles/migrations/0001_initial.py,sha256=rXIP0mgd5w71bCvGEG2wCaHwFkCekM8nGLyUt9-3gaI,2083
|
141
141
|
enterprise_data_roles/migrations/0002_add_enterprise_data_feature_roles.py,sha256=aDGjqYznGT9DV-dAVVIem9kNPNmcUmafinJIyd6FcJw,947
|
142
142
|
enterprise_data_roles/migrations/0003_add_role_based_access_control_switch.py,sha256=7UAMcY6270OO05V9hDfM25lRFHp5v5DRYXdZpiS9rh4,955
|
@@ -171,8 +171,8 @@ enterprise_reporting/tests/test_send_enterprise_reports.py,sha256=WtL-RqGgu2x5PP
|
|
171
171
|
enterprise_reporting/tests/test_utils.py,sha256=Zt_TA0LVb-B6fQGkUkAKKVlUKKnQh8jnw1US1jKe7g8,9493
|
172
172
|
enterprise_reporting/tests/test_vertica_client.py,sha256=-R2yNCGUjRtoXwLMBloVFQkFYrJoo613VCr61gwI3kQ,140
|
173
173
|
enterprise_reporting/tests/utils.py,sha256=xms2LM7DV3wczXEfctOK1ddel1EE0J_YSr17UzbCDy4,1401
|
174
|
-
edx_enterprise_data-9.7.
|
175
|
-
edx_enterprise_data-9.7.
|
176
|
-
edx_enterprise_data-9.7.
|
177
|
-
edx_enterprise_data-9.7.
|
178
|
-
edx_enterprise_data-9.7.
|
174
|
+
edx_enterprise_data-9.7.2.dist-info/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
|
175
|
+
edx_enterprise_data-9.7.2.dist-info/METADATA,sha256=lEY3yGTUepNDkYUrGi1GHgcj36cSLdHlK5vR2qw7ibI,1569
|
176
|
+
edx_enterprise_data-9.7.2.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
177
|
+
edx_enterprise_data-9.7.2.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
|
178
|
+
edx_enterprise_data-9.7.2.dist-info/RECORD,,
|
enterprise_data/__init__.py
CHANGED
@@ -48,7 +48,7 @@ class FactEngagementAdminDashQueries:
|
|
48
48
|
"""
|
49
49
|
|
50
50
|
@staticmethod
|
51
|
-
def get_top_courses_by_engagement_query(record_count=
|
51
|
+
def get_top_courses_by_engagement_query(record_count=10):
|
52
52
|
"""
|
53
53
|
Get the query to fetch the learning time in hours by courses.
|
54
54
|
|
@@ -61,16 +61,40 @@ class FactEngagementAdminDashQueries:
|
|
61
61
|
(str): Query to fetch the learning time in hours by courses for the top courses by user engagement.
|
62
62
|
"""
|
63
63
|
return f"""
|
64
|
-
|
65
|
-
|
66
|
-
|
64
|
+
WITH filtered_data AS (
|
65
|
+
SELECT
|
66
|
+
course_key,
|
67
|
+
course_title,
|
68
|
+
enroll_type,
|
69
|
+
(learning_time_seconds / 60.0 / 60.0) AS learning_time_hours,
|
70
|
+
activity_date
|
71
|
+
FROM fact_enrollment_engagement_day_admin_dash
|
72
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
67
73
|
activity_date BETWEEN %(start_date)s AND %(end_date)s
|
68
|
-
|
69
|
-
|
74
|
+
),
|
75
|
+
top_10_courses AS (
|
76
|
+
SELECT
|
77
|
+
course_key,
|
78
|
+
SUM(learning_time_hours) as total_learning_time
|
79
|
+
FROM filtered_data
|
80
|
+
GROUP BY course_key
|
81
|
+
ORDER BY total_learning_time DESC
|
82
|
+
LIMIT {record_count}
|
83
|
+
)
|
84
|
+
SELECT
|
85
|
+
d.course_key,
|
86
|
+
d.course_title,
|
87
|
+
d.enroll_type,
|
88
|
+
ROUND(SUM(d.learning_time_hours)) AS learning_time_hours
|
89
|
+
FROM filtered_data d
|
90
|
+
JOIN top_10_courses tc
|
91
|
+
ON d.course_key = tc.course_key
|
92
|
+
GROUP BY d.course_key, d.course_title, d.enroll_type
|
93
|
+
ORDER BY total_learning_time DESC;
|
70
94
|
"""
|
71
95
|
|
72
96
|
@staticmethod
|
73
|
-
def get_top_subjects_by_engagement_query(record_count=
|
97
|
+
def get_top_subjects_by_engagement_query(record_count=10):
|
74
98
|
"""
|
75
99
|
Get the query to fetch the learning time in hours by subjects.
|
76
100
|
|
@@ -83,12 +107,34 @@ class FactEngagementAdminDashQueries:
|
|
83
107
|
(str): Query to fetch the learning time in hours by subjects for the top subjects by user engagement.
|
84
108
|
"""
|
85
109
|
return f"""
|
86
|
-
|
87
|
-
|
88
|
-
|
110
|
+
WITH filtered_data AS (
|
111
|
+
SELECT
|
112
|
+
course_subject,
|
113
|
+
enroll_type,
|
114
|
+
(learning_time_seconds / 60.0 / 60.0) AS learning_time_hours,
|
115
|
+
activity_date
|
116
|
+
FROM fact_enrollment_engagement_day_admin_dash
|
117
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
89
118
|
activity_date BETWEEN %(start_date)s AND %(end_date)s
|
90
|
-
|
91
|
-
|
119
|
+
),
|
120
|
+
top_10_subjects AS (
|
121
|
+
SELECT
|
122
|
+
course_subject,
|
123
|
+
SUM(learning_time_hours) as total_learning_time
|
124
|
+
FROM filtered_data
|
125
|
+
GROUP BY course_subject
|
126
|
+
ORDER BY total_learning_time DESC
|
127
|
+
LIMIT {record_count}
|
128
|
+
)
|
129
|
+
SELECT
|
130
|
+
d.course_subject,
|
131
|
+
d.enroll_type,
|
132
|
+
ROUND(SUM(d.learning_time_hours)) AS learning_time_hours
|
133
|
+
FROM filtered_data d
|
134
|
+
JOIN top_10_subjects ts
|
135
|
+
ON d.course_subject = ts.course_subject
|
136
|
+
GROUP BY d.course_subject, d.enroll_type
|
137
|
+
ORDER BY total_learning_time DESC;
|
92
138
|
"""
|
93
139
|
|
94
140
|
@staticmethod
|
@@ -163,6 +209,55 @@ class FactEngagementAdminDashQueries:
|
|
163
209
|
GROUP BY email;
|
164
210
|
"""
|
165
211
|
|
212
|
+
@staticmethod
|
213
|
+
def get_engagement_data_for_leaderboard_null_email_only_query():
|
214
|
+
"""
|
215
|
+
Get the query to fetch the engagement data for leaderboard for NULL emails only.
|
216
|
+
|
217
|
+
Query should fetch the engagement data for like learning time, session length of
|
218
|
+
the enterprise learners to show in the leaderboard.
|
219
|
+
|
220
|
+
Returns:
|
221
|
+
(str): Query to fetch the engagement data for leaderboard.
|
222
|
+
"""
|
223
|
+
return """
|
224
|
+
SELECT
|
225
|
+
email,
|
226
|
+
ROUND(SUM(learning_time_seconds) / 3600, 1) as learning_time_hours,
|
227
|
+
SUM(is_engaged) as session_count,
|
228
|
+
CASE
|
229
|
+
WHEN SUM(is_engaged) = 0 THEN 0.0
|
230
|
+
ELSE ROUND(SUM(learning_time_seconds) / 3600 / SUM(is_engaged), 1)
|
231
|
+
END AS average_session_length
|
232
|
+
FROM fact_enrollment_engagement_day_admin_dash
|
233
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
234
|
+
(activity_date BETWEEN %(start_date)s AND %(end_date)s) AND
|
235
|
+
is_engaged = 1 AND
|
236
|
+
email is NULL
|
237
|
+
GROUP BY email;
|
238
|
+
"""
|
239
|
+
|
240
|
+
@staticmethod
|
241
|
+
def get_completion_data_for_leaderboard_null_email_only_query():
|
242
|
+
"""
|
243
|
+
Get the query to fetch the completions data for leaderboard for NULL emails.
|
244
|
+
|
245
|
+
Query should fetch the completion data for like course completion count of
|
246
|
+
the enterprise learners to show in the leaderboard.
|
247
|
+
|
248
|
+
Returns:
|
249
|
+
(list<str>): Query to fetch the completions data for leaderboard.
|
250
|
+
"""
|
251
|
+
return """
|
252
|
+
SELECT email, count(course_key) as course_completion_count
|
253
|
+
FROM fact_enrollment_admin_dash
|
254
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
255
|
+
(passed_date BETWEEN %(start_date)s AND %(end_date)s) AND
|
256
|
+
has_passed = 1 AND
|
257
|
+
email is NULL
|
258
|
+
GROUP BY email;
|
259
|
+
"""
|
260
|
+
|
166
261
|
@staticmethod
|
167
262
|
def get_leaderboard_data_count_query():
|
168
263
|
"""
|
@@ -98,7 +98,7 @@ class FactEnrollmentAdminDashQueries:
|
|
98
98
|
"""
|
99
99
|
|
100
100
|
@staticmethod
|
101
|
-
def get_top_courses_by_enrollments_query(record_count=
|
101
|
+
def get_top_courses_by_enrollments_query(record_count=10):
|
102
102
|
"""
|
103
103
|
Get the query to fetch the enrollment count by courses.
|
104
104
|
|
@@ -108,15 +108,32 @@ class FactEnrollmentAdminDashQueries:
|
|
108
108
|
record_count (int): Number of records to fetch.
|
109
109
|
"""
|
110
110
|
return f"""
|
111
|
-
|
112
|
-
|
113
|
-
|
111
|
+
WITH filtered_data AS (
|
112
|
+
SELECT *
|
113
|
+
FROM fact_enrollment_admin_dash
|
114
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
114
115
|
enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s
|
115
|
-
|
116
|
+
),
|
117
|
+
top_10_courses AS (
|
118
|
+
SELECT course_key
|
119
|
+
FROM filtered_data
|
120
|
+
GROUP BY course_key
|
121
|
+
ORDER BY COUNT(*) DESC
|
122
|
+
LIMIT {record_count}
|
123
|
+
)
|
124
|
+
|
125
|
+
SELECT
|
126
|
+
d.course_key,
|
127
|
+
d.enroll_type,
|
128
|
+
COUNT(*) AS enrollment_count
|
129
|
+
FROM filtered_data d
|
130
|
+
JOIN top_10_courses tc
|
131
|
+
ON d.course_key = tc.course_key
|
132
|
+
GROUP BY d.course_key, d.enroll_type;
|
116
133
|
"""
|
117
134
|
|
118
135
|
@staticmethod
|
119
|
-
def get_top_subjects_by_enrollments_query(record_count=
|
136
|
+
def get_top_subjects_by_enrollments_query(record_count=10):
|
120
137
|
"""
|
121
138
|
Get the query to fetch the enrollment count by subjects.
|
122
139
|
|
@@ -126,11 +143,27 @@ class FactEnrollmentAdminDashQueries:
|
|
126
143
|
record_count (int): Number of records to fetch.
|
127
144
|
"""
|
128
145
|
return f"""
|
129
|
-
|
130
|
-
|
131
|
-
|
146
|
+
WITH filtered_data AS (
|
147
|
+
SELECT *
|
148
|
+
FROM fact_enrollment_admin_dash
|
149
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
132
150
|
enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s
|
133
|
-
|
151
|
+
),
|
152
|
+
top_10_subjects AS (
|
153
|
+
SELECT course_subject
|
154
|
+
FROM filtered_data
|
155
|
+
GROUP BY course_subject
|
156
|
+
ORDER BY COUNT(*) DESC
|
157
|
+
LIMIT {record_count}
|
158
|
+
)
|
159
|
+
SELECT
|
160
|
+
d.course_subject,
|
161
|
+
d.enroll_type,
|
162
|
+
COUNT(*) AS enrollment_count
|
163
|
+
FROM filtered_data d
|
164
|
+
JOIN top_10_subjects ts
|
165
|
+
ON d.course_subject = ts.course_subject
|
166
|
+
GROUP BY d.course_subject, d.enroll_type;
|
134
167
|
"""
|
135
168
|
|
136
169
|
@staticmethod
|
@@ -162,7 +195,7 @@ class FactEnrollmentAdminDashQueries:
|
|
162
195
|
"""
|
163
196
|
|
164
197
|
@staticmethod
|
165
|
-
def get_top_courses_by_completions_query(record_count=
|
198
|
+
def get_top_courses_by_completions_query(record_count=10):
|
166
199
|
"""
|
167
200
|
Get the query to fetch the completion count by courses.
|
168
201
|
|
@@ -175,17 +208,40 @@ class FactEnrollmentAdminDashQueries:
|
|
175
208
|
(str): Query to fetch the enrollment count by courses for the top courses by enrollment count.
|
176
209
|
"""
|
177
210
|
return f"""
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
211
|
+
WITH filtered_data AS (
|
212
|
+
SELECT
|
213
|
+
course_key,
|
214
|
+
course_title,
|
215
|
+
enroll_type,
|
216
|
+
passed_date
|
217
|
+
FROM fact_enrollment_admin_dash
|
218
|
+
WHERE has_passed = 1 AND
|
219
|
+
enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
182
220
|
passed_date BETWEEN %(start_date)s AND %(end_date)s
|
183
|
-
|
184
|
-
|
221
|
+
),
|
222
|
+
top_10_courses AS (
|
223
|
+
SELECT
|
224
|
+
course_key,
|
225
|
+
COUNT(*) AS total_completion_count
|
226
|
+
FROM filtered_data
|
227
|
+
GROUP BY course_key
|
228
|
+
ORDER BY total_completion_count DESC
|
229
|
+
LIMIT {record_count}
|
230
|
+
)
|
231
|
+
SELECT
|
232
|
+
d.course_key,
|
233
|
+
d.course_title,
|
234
|
+
d.enroll_type,
|
235
|
+
COUNT(*) AS completion_count
|
236
|
+
FROM filtered_data d
|
237
|
+
JOIN top_10_courses tc
|
238
|
+
ON d.course_key = tc.course_key
|
239
|
+
GROUP BY d.course_key, d.course_title, d.enroll_type
|
240
|
+
ORDER BY total_completion_count DESC;
|
185
241
|
"""
|
186
242
|
|
187
243
|
@staticmethod
|
188
|
-
def get_top_subjects_by_completions_query(record_count=
|
244
|
+
def get_top_subjects_by_completions_query(record_count=10):
|
189
245
|
"""
|
190
246
|
Get the query to fetch the completion count by subjects.
|
191
247
|
|
@@ -198,13 +254,34 @@ class FactEnrollmentAdminDashQueries:
|
|
198
254
|
(str): Query to fetch the completion count by subjects for the top subjects by completion count.
|
199
255
|
"""
|
200
256
|
return f"""
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
257
|
+
WITH filtered_data AS (
|
258
|
+
SELECT
|
259
|
+
course_subject,
|
260
|
+
enroll_type,
|
261
|
+
passed_date
|
262
|
+
FROM fact_enrollment_admin_dash
|
263
|
+
WHERE has_passed = 1 AND
|
264
|
+
enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
205
265
|
passed_date BETWEEN %(start_date)s AND %(end_date)s
|
206
|
-
|
207
|
-
|
266
|
+
),
|
267
|
+
top_10_subjects AS (
|
268
|
+
SELECT
|
269
|
+
course_subject,
|
270
|
+
COUNT(*) AS total_completion_count
|
271
|
+
FROM filtered_data
|
272
|
+
GROUP BY course_subject
|
273
|
+
ORDER BY total_completion_count DESC
|
274
|
+
LIMIT {record_count}
|
275
|
+
)
|
276
|
+
SELECT
|
277
|
+
d.course_subject,
|
278
|
+
d.enroll_type,
|
279
|
+
COUNT(*) AS completion_count
|
280
|
+
FROM filtered_data d
|
281
|
+
JOIN top_10_subjects ts
|
282
|
+
ON d.course_subject = ts.course_subject
|
283
|
+
GROUP BY d.course_subject, d.enroll_type
|
284
|
+
ORDER BY total_completion_count DESC;
|
208
285
|
"""
|
209
286
|
|
210
287
|
@staticmethod
|
@@ -5,6 +5,7 @@ from datetime import date
|
|
5
5
|
from uuid import UUID
|
6
6
|
|
7
7
|
from enterprise_data.cache.decorators import cache_it
|
8
|
+
from enterprise_data.utils import find_first
|
8
9
|
|
9
10
|
from ..queries import FactEngagementAdminDashQueries
|
10
11
|
from ..utils import run_query
|
@@ -168,7 +169,14 @@ class FactEngagementAdminDashTable(BaseTable):
|
|
168
169
|
|
169
170
|
@cache_it()
|
170
171
|
def _get_engagement_data_for_leaderboard(
|
171
|
-
self,
|
172
|
+
self,
|
173
|
+
enterprise_customer_uuid: UUID,
|
174
|
+
start_date: date,
|
175
|
+
end_date: date,
|
176
|
+
limit: int,
|
177
|
+
offset: int,
|
178
|
+
include_null_email: bool,
|
179
|
+
|
172
180
|
):
|
173
181
|
"""
|
174
182
|
Get the engagement data for leaderboard.
|
@@ -182,11 +190,12 @@ class FactEngagementAdminDashTable(BaseTable):
|
|
182
190
|
end_date (date): The end date.
|
183
191
|
limit (int): The maximum number of records to return.
|
184
192
|
offset (int): The number of records to skip.
|
193
|
+
include_null_email (bool): If True, only fetch data for NULL emails.
|
185
194
|
|
186
195
|
Returns:
|
187
196
|
list[dict]: The engagement data for leaderboard.
|
188
197
|
"""
|
189
|
-
|
198
|
+
engagements = run_query(
|
190
199
|
query=self.queries.get_engagement_data_for_leaderboard_query(),
|
191
200
|
params={
|
192
201
|
'enterprise_customer_uuid': enterprise_customer_uuid,
|
@@ -198,9 +207,27 @@ class FactEngagementAdminDashTable(BaseTable):
|
|
198
207
|
as_dict=True,
|
199
208
|
)
|
200
209
|
|
210
|
+
if include_null_email:
|
211
|
+
engagement_for_null_email = run_query(
|
212
|
+
query=self.queries.get_engagement_data_for_leaderboard_null_email_only_query(),
|
213
|
+
params={
|
214
|
+
'enterprise_customer_uuid': enterprise_customer_uuid,
|
215
|
+
'start_date': start_date,
|
216
|
+
'end_date': end_date,
|
217
|
+
},
|
218
|
+
as_dict=True,
|
219
|
+
)
|
220
|
+
engagements += engagement_for_null_email
|
221
|
+
return engagements
|
222
|
+
|
201
223
|
@cache_it()
|
202
224
|
def _get_completion_data_for_leaderboard_query(
|
203
|
-
self,
|
225
|
+
self,
|
226
|
+
enterprise_customer_uuid: UUID,
|
227
|
+
start_date: date,
|
228
|
+
end_date: date,
|
229
|
+
email_list: list,
|
230
|
+
include_null_email: bool,
|
204
231
|
):
|
205
232
|
"""
|
206
233
|
Get the completion data for leaderboard.
|
@@ -213,11 +240,13 @@ class FactEngagementAdminDashTable(BaseTable):
|
|
213
240
|
start_date (date): The start date.
|
214
241
|
end_date (date): The end date.
|
215
242
|
email_list (list<str>): List of emails of the enterprise learners.
|
243
|
+
include_null_email (bool): If True, only fetch data for NULL emails.
|
216
244
|
|
217
245
|
Returns:
|
218
246
|
list[dict]: The engagement data for leaderboard.
|
219
247
|
"""
|
220
|
-
|
248
|
+
|
249
|
+
completions = run_query(
|
221
250
|
query=self.queries.get_completion_data_for_leaderboard_query(email_list),
|
222
251
|
params={
|
223
252
|
'enterprise_customer_uuid': enterprise_customer_uuid,
|
@@ -227,8 +256,28 @@ class FactEngagementAdminDashTable(BaseTable):
|
|
227
256
|
as_dict=True,
|
228
257
|
)
|
229
258
|
|
259
|
+
if include_null_email:
|
260
|
+
completions_for_null_email = run_query(
|
261
|
+
query=self.queries.get_completion_data_for_leaderboard_null_email_only_query(),
|
262
|
+
params={
|
263
|
+
'enterprise_customer_uuid': enterprise_customer_uuid,
|
264
|
+
'start_date': start_date,
|
265
|
+
'end_date': end_date,
|
266
|
+
},
|
267
|
+
as_dict=True,
|
268
|
+
)
|
269
|
+
completions += completions_for_null_email
|
270
|
+
|
271
|
+
return completions
|
272
|
+
|
230
273
|
def get_all_leaderboard_data(
|
231
|
-
self,
|
274
|
+
self,
|
275
|
+
enterprise_customer_uuid: UUID,
|
276
|
+
start_date: date,
|
277
|
+
end_date: date,
|
278
|
+
limit: int,
|
279
|
+
offset: int,
|
280
|
+
total_count: int,
|
232
281
|
):
|
233
282
|
"""
|
234
283
|
Get the leaderboard data for the given enterprise customer.
|
@@ -239,32 +288,48 @@ class FactEngagementAdminDashTable(BaseTable):
|
|
239
288
|
end_date (date): The end date.
|
240
289
|
limit (int): The maximum number of records to return.
|
241
290
|
offset (int): The number of records to skip.
|
291
|
+
total_count (int): The total number of records.
|
242
292
|
|
243
293
|
Returns:
|
244
294
|
list[dict]: The leaderboard data.
|
245
295
|
"""
|
296
|
+
include_null_email = False
|
297
|
+
# If this is the last or only page, we need to include NULL emails record.
|
298
|
+
if total_count <= offset + limit:
|
299
|
+
include_null_email = True
|
300
|
+
|
246
301
|
engagement_data = self._get_engagement_data_for_leaderboard(
|
247
302
|
enterprise_customer_uuid=enterprise_customer_uuid,
|
248
303
|
start_date=start_date,
|
249
304
|
end_date=end_date,
|
250
305
|
limit=limit,
|
251
306
|
offset=offset,
|
307
|
+
include_null_email=include_null_email,
|
252
308
|
)
|
253
309
|
# If there is no data, no need to proceed.
|
254
310
|
if not engagement_data:
|
255
311
|
return []
|
256
312
|
|
257
|
-
engagement_data_dict = {
|
313
|
+
engagement_data_dict = {
|
314
|
+
engagement['email']: engagement for engagement in engagement_data if engagement['email']
|
315
|
+
}
|
258
316
|
completion_data = self._get_completion_data_for_leaderboard_query(
|
259
317
|
enterprise_customer_uuid=enterprise_customer_uuid,
|
260
318
|
start_date=start_date,
|
261
319
|
end_date=end_date,
|
262
320
|
email_list=list(engagement_data_dict.keys()),
|
321
|
+
include_null_email=include_null_email,
|
263
322
|
)
|
264
323
|
for completion in completion_data:
|
265
324
|
email = completion['email']
|
266
325
|
engagement_data_dict[email]['course_completion_count'] = completion['course_completion_count']
|
267
326
|
|
327
|
+
if include_null_email:
|
328
|
+
engagement_data_dict['None'] = find_first(engagement_data, lambda x: x['email'] is None) or {}
|
329
|
+
completion = find_first(completion_data, lambda x: x['email'] is None) or \
|
330
|
+
{'course_completion_count': 'Unknown'}
|
331
|
+
engagement_data_dict['None']['course_completion_count'] = completion['course_completion_count']
|
332
|
+
|
268
333
|
return list(engagement_data_dict.values())
|
269
334
|
|
270
335
|
@cache_it()
|
@@ -49,17 +49,18 @@ class AdvanceAnalyticsLeaderboardView(AnalyticsPaginationMixin, ViewSet):
|
|
49
49
|
end_date = serializer.data.get('end_date', date.today())
|
50
50
|
page = serializer.data.get('page', 1)
|
51
51
|
page_size = serializer.data.get('page_size', 100)
|
52
|
-
|
52
|
+
total_count = FactEngagementAdminDashTable().get_leaderboard_data_count(
|
53
53
|
enterprise_customer_uuid=enterprise_uuid,
|
54
54
|
start_date=start_date,
|
55
55
|
end_date=end_date,
|
56
|
-
limit=page_size,
|
57
|
-
offset=(page - 1) * page_size,
|
58
56
|
)
|
59
|
-
|
57
|
+
leaderboard = FactEngagementAdminDashTable().get_all_leaderboard_data(
|
60
58
|
enterprise_customer_uuid=enterprise_uuid,
|
61
59
|
start_date=start_date,
|
62
60
|
end_date=end_date,
|
61
|
+
limit=page_size,
|
62
|
+
offset=(page - 1) * page_size,
|
63
|
+
total_count=total_count,
|
63
64
|
)
|
64
65
|
response_type = request.query_params.get('response_type', ResponseType.JSON.value)
|
65
66
|
|
@@ -102,6 +103,7 @@ class AdvanceAnalyticsLeaderboardView(AnalyticsPaginationMixin, ViewSet):
|
|
102
103
|
end_date=end_date,
|
103
104
|
limit=page_size,
|
104
105
|
offset=offset,
|
106
|
+
total_count=total_count,
|
105
107
|
)
|
106
108
|
yield from leaderboard
|
107
109
|
offset += page_size
|
@@ -161,17 +161,18 @@ class Command(BaseCommand):
|
|
161
161
|
start_date=start_date,
|
162
162
|
end_date=end_date,
|
163
163
|
)
|
164
|
-
enterprise_engagement_table.
|
164
|
+
total_count = enterprise_engagement_table.get_leaderboard_data_count(
|
165
165
|
enterprise_customer_uuid=enterprise_customer_uuid,
|
166
166
|
start_date=start_date,
|
167
167
|
end_date=end_date,
|
168
|
-
limit=page_size,
|
169
|
-
offset=0,
|
170
168
|
)
|
171
|
-
enterprise_engagement_table.
|
169
|
+
enterprise_engagement_table.get_all_leaderboard_data(
|
172
170
|
enterprise_customer_uuid=enterprise_customer_uuid,
|
173
171
|
start_date=start_date,
|
174
172
|
end_date=end_date,
|
173
|
+
limit=page_size,
|
174
|
+
offset=0,
|
175
|
+
total_count=total_count,
|
175
176
|
)
|
176
177
|
|
177
178
|
@staticmethod
|
@@ -30,9 +30,18 @@ class Test(TestCase):
|
|
30
30
|
get_enrollment_date_range_patcher.start()
|
31
31
|
self.addCleanup(get_enrollment_date_range_patcher.stop)
|
32
32
|
|
33
|
-
@patch(
|
34
|
-
|
35
|
-
|
33
|
+
@patch(
|
34
|
+
'enterprise_data.admin_analytics.database.tables.fact_engagement_admin_dash.run_query',
|
35
|
+
MagicMock(return_value=[])
|
36
|
+
)
|
37
|
+
@patch(
|
38
|
+
'enterprise_data.admin_analytics.database.tables.fact_enrollment_admin_dash.run_query',
|
39
|
+
MagicMock(return_value=[])
|
40
|
+
)
|
41
|
+
@patch(
|
42
|
+
'enterprise_data.admin_analytics.database.tables.skills_daily_rollup_admin_dash.run_query',
|
43
|
+
MagicMock(return_value=[])
|
44
|
+
)
|
36
45
|
@patch('enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_top_enterprises')
|
37
46
|
@patch('enterprise_data.cache.decorators.cache.set')
|
38
47
|
@patch('enterprise_data.cache.decorators.cache.get')
|
@@ -47,5 +56,5 @@ class Test(TestCase):
|
|
47
56
|
|
48
57
|
call_command('pre_warm_analytics_cache')
|
49
58
|
|
50
|
-
assert mock_get_cache.call_count ==
|
51
|
-
assert mock_set_cache.call_count ==
|
59
|
+
assert mock_get_cache.call_count == 23
|
60
|
+
assert mock_set_cache.call_count == 23
|
enterprise_data/utils.py
CHANGED
@@ -97,3 +97,20 @@ def primary_subject_truncate(x):
|
|
97
97
|
return x
|
98
98
|
else:
|
99
99
|
return "other"
|
100
|
+
|
101
|
+
|
102
|
+
def find_first(iterable, condition):
|
103
|
+
"""
|
104
|
+
Find the first item in an iterable that satisfies the condition.
|
105
|
+
|
106
|
+
Arguments:
|
107
|
+
iterable (iterable): The iterable to search.
|
108
|
+
condition (function): The condition to satisfy.
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
The first item that satisfies the condition, or None if no item satisfies the condition.
|
112
|
+
"""
|
113
|
+
try:
|
114
|
+
return next(item for item in iterable if condition(item))
|
115
|
+
except StopIteration:
|
116
|
+
return None
|
enterprise_data_roles/rules.py
CHANGED
@@ -57,5 +57,5 @@ def request_user_has_explicit_access(*args, **kwargs):
|
|
57
57
|
|
58
58
|
rules.add_perm(
|
59
59
|
'can_access_enterprise',
|
60
|
-
request_user_has_implicit_access | request_user_has_explicit_access
|
60
|
+
request_user_has_implicit_access | request_user_has_explicit_access
|
61
61
|
)
|
File without changes
|
File without changes
|
File without changes
|