edx-enterprise-data 9.7.1__py3-none-any.whl → 10.2.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-9.7.1.dist-info → edx_enterprise_data-10.2.0.dist-info}/METADATA +3 -4
- {edx_enterprise_data-9.7.1.dist-info → edx_enterprise_data-10.2.0.dist-info}/RECORD +13 -13
- {edx_enterprise_data-9.7.1.dist-info → edx_enterprise_data-10.2.0.dist-info}/WHEEL +1 -1
- enterprise_data/__init__.py +1 -1
- enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py +49 -0
- 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-10.2.0.dist-info}/LICENSE +0 -0
- {edx_enterprise_data-9.7.1.dist-info → edx_enterprise_data-10.2.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: edx-enterprise-data
|
3
|
-
Version:
|
3
|
+
Version: 10.2.0
|
4
4
|
Summary: Enterprise Reporting
|
5
5
|
Home-page: https://github.com/openedx/edx-enterprise-data
|
6
6
|
Author: edX
|
@@ -9,7 +9,6 @@ License: AGPL 3.0
|
|
9
9
|
Classifier: Framework :: Django
|
10
10
|
Classifier: Framework :: Django :: 4.2
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
12
|
-
Classifier: Programming Language :: Python :: 3.8
|
13
12
|
Classifier: Programming Language :: Python :: 3.12
|
14
13
|
License-File: LICENSE
|
15
14
|
Requires-Dist: Django
|
@@ -24,8 +23,8 @@ Requires-Dist: edx-rbac
|
|
24
23
|
Requires-Dist: edx-rest-api-client
|
25
24
|
Requires-Dist: factory-boy
|
26
25
|
Requires-Dist: mysql-connector-python
|
27
|
-
Requires-Dist: numpy
|
28
|
-
Requires-Dist: pandas
|
26
|
+
Requires-Dist: numpy
|
27
|
+
Requires-Dist: pandas
|
29
28
|
Requires-Dist: requests
|
30
29
|
Requires-Dist: rules
|
31
30
|
Provides-Extra: reporting
|
@@ -1,4 +1,4 @@
|
|
1
|
-
enterprise_data/__init__.py,sha256=
|
1
|
+
enterprise_data/__init__.py,sha256=msobjDbSxGnYjz9FZlLzQBbpW1Y81HKRCZo4uSEsQic,124
|
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=
|
18
|
+
enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py,sha256=ms7wFVSC53VD0IHH9sXmfx7Eh0BoEDDtE0gdTRddKzg,9274
|
19
19
|
enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py,sha256=6EX0W3RWRzSrpIbqFJvStnj5Rkfzi9KyFP0UNmb75pU,8814
|
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-
|
175
|
-
edx_enterprise_data-
|
176
|
-
edx_enterprise_data-
|
177
|
-
edx_enterprise_data-
|
178
|
-
edx_enterprise_data-
|
174
|
+
edx_enterprise_data-10.2.0.dist-info/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
|
175
|
+
edx_enterprise_data-10.2.0.dist-info/METADATA,sha256=ZaH_jYk1WE6bFmmmA3vCkGzWgs_q7lZgMtUnbjk52jU,1505
|
176
|
+
edx_enterprise_data-10.2.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
177
|
+
edx_enterprise_data-10.2.0.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
|
178
|
+
edx_enterprise_data-10.2.0.dist-info/RECORD,,
|
enterprise_data/__init__.py
CHANGED
@@ -163,6 +163,55 @@ class FactEngagementAdminDashQueries:
|
|
163
163
|
GROUP BY email;
|
164
164
|
"""
|
165
165
|
|
166
|
+
@staticmethod
|
167
|
+
def get_engagement_data_for_leaderboard_null_email_only_query():
|
168
|
+
"""
|
169
|
+
Get the query to fetch the engagement data for leaderboard for NULL emails only.
|
170
|
+
|
171
|
+
Query should fetch the engagement data for like learning time, session length of
|
172
|
+
the enterprise learners to show in the leaderboard.
|
173
|
+
|
174
|
+
Returns:
|
175
|
+
(str): Query to fetch the engagement data for leaderboard.
|
176
|
+
"""
|
177
|
+
return """
|
178
|
+
SELECT
|
179
|
+
email,
|
180
|
+
ROUND(SUM(learning_time_seconds) / 3600, 1) as learning_time_hours,
|
181
|
+
SUM(is_engaged) as session_count,
|
182
|
+
CASE
|
183
|
+
WHEN SUM(is_engaged) = 0 THEN 0.0
|
184
|
+
ELSE ROUND(SUM(learning_time_seconds) / 3600 / SUM(is_engaged), 1)
|
185
|
+
END AS average_session_length
|
186
|
+
FROM fact_enrollment_engagement_day_admin_dash
|
187
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
188
|
+
(activity_date BETWEEN %(start_date)s AND %(end_date)s) AND
|
189
|
+
is_engaged = 1 AND
|
190
|
+
email is NULL
|
191
|
+
GROUP BY email;
|
192
|
+
"""
|
193
|
+
|
194
|
+
@staticmethod
|
195
|
+
def get_completion_data_for_leaderboard_null_email_only_query():
|
196
|
+
"""
|
197
|
+
Get the query to fetch the completions data for leaderboard for NULL emails.
|
198
|
+
|
199
|
+
Query should fetch the completion data for like course completion count of
|
200
|
+
the enterprise learners to show in the leaderboard.
|
201
|
+
|
202
|
+
Returns:
|
203
|
+
(list<str>): Query to fetch the completions data for leaderboard.
|
204
|
+
"""
|
205
|
+
return """
|
206
|
+
SELECT email, count(course_key) as course_completion_count
|
207
|
+
FROM fact_enrollment_admin_dash
|
208
|
+
WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
|
209
|
+
(passed_date BETWEEN %(start_date)s AND %(end_date)s) AND
|
210
|
+
has_passed = 1 AND
|
211
|
+
email is NULL
|
212
|
+
GROUP BY email;
|
213
|
+
"""
|
214
|
+
|
166
215
|
@staticmethod
|
167
216
|
def get_leaderboard_data_count_query():
|
168
217
|
"""
|
@@ -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
|