edx-enterprise-data 9.1.1__py3-none-any.whl → 9.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.
Files changed (24) hide show
  1. {edx_enterprise_data-9.1.1.dist-info → edx_enterprise_data-9.2.0.dist-info}/METADATA +1 -1
  2. {edx_enterprise_data-9.1.1.dist-info → edx_enterprise_data-9.2.0.dist-info}/RECORD +21 -24
  3. enterprise_data/__init__.py +1 -1
  4. enterprise_data/admin_analytics/constants.py +3 -17
  5. enterprise_data/admin_analytics/data_loaders.py +0 -125
  6. enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py +85 -0
  7. enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py +50 -0
  8. enterprise_data/api/v1/serializers.py +1 -38
  9. enterprise_data/api/v1/urls.py +2 -2
  10. enterprise_data/api/v1/views/analytics_completions.py +0 -2
  11. enterprise_data/api/v1/views/analytics_engagements.py +0 -2
  12. enterprise_data/api/v1/views/analytics_enrollments.py +0 -2
  13. enterprise_data/api/v1/views/analytics_leaderboard.py +65 -102
  14. enterprise_data/renderers.py +2 -2
  15. enterprise_data/tests/admin_analytics/mock_analytics_data.py +15 -60
  16. enterprise_data/tests/admin_analytics/test_analytics_leaderboard.py +48 -81
  17. enterprise_data/tests/admin_analytics/test_data_loaders.py +1 -58
  18. enterprise_data/utils.py +0 -16
  19. enterprise_data/admin_analytics/utils.py +0 -180
  20. enterprise_data/api/v1/paginators.py +0 -121
  21. enterprise_data/tests/admin_analytics/test_utils.py +0 -102
  22. {edx_enterprise_data-9.1.1.dist-info → edx_enterprise_data-9.2.0.dist-info}/LICENSE +0 -0
  23. {edx_enterprise_data-9.1.1.dist-info → edx_enterprise_data-9.2.0.dist-info}/WHEEL +0 -0
  24. {edx_enterprise_data-9.1.1.dist-info → edx_enterprise_data-9.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: 9.1.1
3
+ Version: 9.2.0
4
4
  Summary: Enterprise Reporting
5
5
  Home-page: https://github.com/openedx/edx-enterprise-data
6
6
  Author: edX
@@ -1,27 +1,26 @@
1
- enterprise_data/__init__.py,sha256=kPh037UQO6bJ1Xw2Ntz6f2XrskuXBXe6wyxcOMOA_Cg,123
1
+ enterprise_data/__init__.py,sha256=lw4RdMlnQpEaiuEI442W84A9nCk5Y2AFB3wxyg2DhY0,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
5
5
  enterprise_data/filters.py,sha256=D2EiK12MMpBoz6eOUmTpoJEhj_sH7bA93NRRAdvkDVo,6163
6
6
  enterprise_data/models.py,sha256=khGcOh7NWP8KGu84t78Y2zAu3knREeXA_prApmU2NX8,24428
7
7
  enterprise_data/paginators.py,sha256=YPrC5TeXFt-ymenT2H8H2nCbDCnAzJQlH9kFPElRxWE,269
8
- enterprise_data/renderers.py,sha256=94JNmPj6OuOcoqMfwl_ieTwy-fQPJO7IbYpYQhxvHJY,3022
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=_zK3BErjZSIkP_JNzK8m-DR5pRTnxKylP9I-vURaRcE,3009
11
+ enterprise_data/utils.py,sha256=Hrmz6uB8LsPSrau-lK0vvBWzRKTMl_Tygxv8qWG2NNw,2471
12
12
  enterprise_data/admin_analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- enterprise_data/admin_analytics/constants.py,sha256=bTU05drUaePj9ZjLVxUwFMq_8hibafXqPnVrmrQt4Og,532
14
- enterprise_data/admin_analytics/data_loaders.py,sha256=z5OHCmsjQuu1lBw43mSSCbdSQ40fmL_WtodV_Tcnj5U,4791
15
- enterprise_data/admin_analytics/utils.py,sha256=DftyBaSKnf0f6E3adH8yVAvGNeQGROywufqnJxZ0W84,6295
13
+ enterprise_data/admin_analytics/constants.py,sha256=-6uLAq5DUeA_rv5eUb9SeqlG3iVWV30qUS8asbK4430,160
14
+ enterprise_data/admin_analytics/data_loaders.py,sha256=NixI-4M3D4MnI279x5hqqTw84uKpQy0TRib_g-0Bt5Q,726
16
15
  enterprise_data/admin_analytics/database/__init__.py,sha256=vNSWKf2VV5xMegN7htJJtxtQEb0ASLC6frE2w0ZpYpE,104
17
16
  enterprise_data/admin_analytics/database/utils.py,sha256=5u-d6ZQW95mF_r4bH8Xdi7DgpYAuDFOG_q0P-bjKXHU,1712
18
17
  enterprise_data/admin_analytics/database/queries/__init__.py,sha256=IC5TLOr_GnydbrVbl2mWhwO3aUbYeHuDmfPTLmwGhZA,218
19
- enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py,sha256=QNoeuUQ2Fg9IWSxayA8mBbrnRYNPaXVBWhA7-dbquNc,4445
18
+ enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py,sha256=28R1uISsLmMrbvTZXF2bGsVCCOyfQzXY-tMzThDndHo,7809
20
19
  enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py,sha256=brRgmgtzPLsOdt1vYrk_EpoQLm2izmOdBapxAxNkXzA,8420
21
20
  enterprise_data/admin_analytics/database/queries/skills_daily_rollup_admin_dash.py,sha256=PgWwvtVCK5lbiq6z44lH0fwbkdWYukhyXZL9X8lNWCY,4099
22
21
  enterprise_data/admin_analytics/database/tables/__init__.py,sha256=Z-c3P9hqR-dC9uYKe63qHkQG9Nms8cLE2jRN-4jeMM0,289
23
22
  enterprise_data/admin_analytics/database/tables/base.py,sha256=1KyKsC18pW3m-5U-T6pdt5rIwsz6Wp3QFFbD3r6L6YQ,395
24
- enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py,sha256=VM-5S1ayJuFaWhBoJd1odTqWaFsWDeLZkoaPixAG2dQ,5544
23
+ enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py,sha256=cA-Y63LQVDD7UHv5rYdX2091R7W6clcT2DXO0VbMhVQ,7354
25
24
  enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py,sha256=q1XB7aDwFzllS5lBQNfS2y7drehqiK7o0J1eiYzqFOk,10854
26
25
  enterprise_data/admin_analytics/database/tables/skills_daily_rollup_admin_dash.py,sha256=9PqLeVqByrz7R0qumRbwJlr5lzIWn7Fl7WEGM0aJVlw,3131
27
26
  enterprise_data/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -31,14 +30,13 @@ enterprise_data/api/v0/serializers.py,sha256=dngZTk6DhRxApchQKCMp1B_c8aVnQtH0NCq
31
30
  enterprise_data/api/v0/urls.py,sha256=vzJjqIo_S3AXWs9Us8XTaJc3FnxLbYzAkmLyuDQqum0,699
32
31
  enterprise_data/api/v0/views.py,sha256=4RslZ4NZOU-844bnebEQ71ji2utRY7jEijqC45oQQD0,14380
33
32
  enterprise_data/api/v1/__init__.py,sha256=1aAzAYU5hk-RW6cKUxa1645cbZMxn7GIZ7OMjWc9MKI,46
34
- enterprise_data/api/v1/paginators.py,sha256=f0xsilLaU94jSBltJk46tR1rLEIt7YrqSzMAAVtPXjA,3592
35
- enterprise_data/api/v1/serializers.py,sha256=leVxK1fVAk_25TDnuMLDHKvOw7GvTmU7zSYXiX0nmZU,11620
36
- enterprise_data/api/v1/urls.py,sha256=zoztENyCI_8TcKpE88Q6pkKSHiWCC9DascjC9ul4arc,3975
33
+ enterprise_data/api/v1/serializers.py,sha256=JOpHjP7n-NgXsShT_Wf6VLVlF-supKw2PW0oZxiwwrI,10323
34
+ enterprise_data/api/v1/urls.py,sha256=vPBZ7p9hsj-lMowqbp29sd2hKhHtICCMVal6th4eQxg,3995
37
35
  enterprise_data/api/v1/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- enterprise_data/api/v1/views/analytics_completions.py,sha256=Px-hwNpkgwZN-Kwrl9MEfpY-TNuVglp1pyJEG9QnPFQ,6391
39
- enterprise_data/api/v1/views/analytics_engagements.py,sha256=yvnHALXVkxQHuX7M8Mom1vNEH-2vvPHZMVlm1SUvTTE,6429
40
- enterprise_data/api/v1/views/analytics_enrollments.py,sha256=RF-OOSVOPbESIUmrLbSM229lMPQ8RokJ6MqTpWoxIgY,6413
41
- enterprise_data/api/v1/views/analytics_leaderboard.py,sha256=2DALqzUIbe4-ZGgHHIkYAKJ5L1ik2ruPtQNYtTdPba4,5974
36
+ enterprise_data/api/v1/views/analytics_completions.py,sha256=4jg2I50TNn2Q6InLCI9eS1MBiFo4SMRsTx4kZJtNmpI,6268
37
+ enterprise_data/api/v1/views/analytics_engagements.py,sha256=W9DNalWIgoeo_KXqapDj2_ireRyXU47nKHw95sAEtxM,6306
38
+ enterprise_data/api/v1/views/analytics_enrollments.py,sha256=HXgoFwvXk-CZcf6qbVOfYSCnkfDxGvfHxl0j3RcKz7A,6290
39
+ enterprise_data/api/v1/views/analytics_leaderboard.py,sha256=zwd-2A2sctjAdx5BUjCqiwcZ5aNvM8wfm386pR-RwGQ,4114
42
40
  enterprise_data/api/v1/views/base.py,sha256=Kkmd5zgEBAhvwS_GoGXSK6lgbDNwSPioYn-QbnizI3w,3416
43
41
  enterprise_data/api/v1/views/enterprise_admin.py,sha256=DsR1oHFhe6LCIFjIJ4YLLZ7PUChvNlFfdZD-sxHoijY,7388
44
42
  enterprise_data/api/v1/views/enterprise_learner.py,sha256=E1eU0DgCIhe4Bx70uZku89-RwEKay4aYt7X0IbPhJzA,18650
@@ -114,14 +112,13 @@ enterprise_data/tests/test_models.py,sha256=MWBY-LY5TPBjZ4GlvpM-h4W-BvRKr2Rml8Bz
114
112
  enterprise_data/tests/test_utils.py,sha256=vbmYM7DMN-lHS2p4yaa0Yd6uSGXd2qoZRDE9X3J4Sec,18385
115
113
  enterprise_data/tests/test_views.py,sha256=UvDRNTxruy5zBK_KgUy2cBMbwlaTW_vkM0-TCXbQZiY,69667
116
114
  enterprise_data/tests/admin_analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
117
- enterprise_data/tests/admin_analytics/mock_analytics_data.py,sha256=NYmgPKIZrIK7HqbjxQ_PKqhY5zmafoePM4NcqE5zfpc,17401
115
+ enterprise_data/tests/admin_analytics/mock_analytics_data.py,sha256=qN73YxoD1kAtKsQo94avR0DGMCsJClrpqFxxYC6reqE,15248
118
116
  enterprise_data/tests/admin_analytics/mock_enrollments.py,sha256=u61tkqSdVKExURJF7arvBoXvbdkF39CkjxQiTvVMXIE,4467
119
117
  enterprise_data/tests/admin_analytics/test_analytics_engagements.py,sha256=gfO6RI2gbDMhOD4p4h7l6sZpisglBreTPtJRqNxIgo8,10440
120
118
  enterprise_data/tests/admin_analytics/test_analytics_enrollments.py,sha256=uFa6gnlf-CthctdYTWsw-pLLOxkELGiq8YFq5CyWFbs,10826
121
- enterprise_data/tests/admin_analytics/test_analytics_leaderboard.py,sha256=VSEyDAHfWBJvqmx9yzd4NnPAqK3TqaKrMBWswMAdzfU,6206
122
- enterprise_data/tests/admin_analytics/test_data_loaders.py,sha256=b4BjN88FX9WjE6XJjkJZnoEvWVB_DovBGJ_wh-HgT9I,3514
119
+ enterprise_data/tests/admin_analytics/test_analytics_leaderboard.py,sha256=iFT47BVlgf93u1K2l3_8HDcXY-9vWKp3iUaWM7V0C2U,5476
120
+ enterprise_data/tests/admin_analytics/test_data_loaders.py,sha256=0ZsxW4J5bFDsujLXa0PpIP2BjuKK4EDQRF7C6gfgCfA,1067
123
121
  enterprise_data/tests/admin_analytics/test_enterprise_completions.py,sha256=6BsCnaXdTMaQXoqy4SWNxAkGimc75lBCdBHjR2MdOFU,7233
124
- enterprise_data/tests/admin_analytics/test_utils.py,sha256=4qL_ZK-sGzbMMqiOrBrPmzdIPno7KohiaIfd7FMehic,5260
125
122
  enterprise_data/tests/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
123
  enterprise_data/tests/api/v0/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
124
  enterprise_data/tests/api/v0/test_serializers.py,sha256=Gfty6gy6OQLN318uL1OCPhAZOqSUL50FWc0nC23VMnc,6257
@@ -170,8 +167,8 @@ enterprise_reporting/tests/test_send_enterprise_reports.py,sha256=WtL-RqGgu2x5PP
170
167
  enterprise_reporting/tests/test_utils.py,sha256=Zt_TA0LVb-B6fQGkUkAKKVlUKKnQh8jnw1US1jKe7g8,9493
171
168
  enterprise_reporting/tests/test_vertica_client.py,sha256=-R2yNCGUjRtoXwLMBloVFQkFYrJoo613VCr61gwI3kQ,140
172
169
  enterprise_reporting/tests/utils.py,sha256=xms2LM7DV3wczXEfctOK1ddel1EE0J_YSr17UzbCDy4,1401
173
- edx_enterprise_data-9.1.1.dist-info/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
174
- edx_enterprise_data-9.1.1.dist-info/METADATA,sha256=Ho4zJPXuhcjv1t9mIm2miwCj8jJewDjj4vKj8waDPXo,1569
175
- edx_enterprise_data-9.1.1.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
176
- edx_enterprise_data-9.1.1.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
177
- edx_enterprise_data-9.1.1.dist-info/RECORD,,
170
+ edx_enterprise_data-9.2.0.dist-info/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
171
+ edx_enterprise_data-9.2.0.dist-info/METADATA,sha256=Y3R-klk-iA_cgSs0EiuD_zzzPnx-4_JBwF9ZMrirwpg,1569
172
+ edx_enterprise_data-9.2.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
173
+ edx_enterprise_data-9.2.0.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
174
+ edx_enterprise_data-9.2.0.dist-info/RECORD,,
@@ -2,4 +2,4 @@
2
2
  Enterprise data api application. This Django app exposes API endpoints used by enterprises.
3
3
  """
4
4
 
5
- __version__ = "9.1.1"
5
+ __version__ = "9.2.0"
@@ -1,24 +1,10 @@
1
- """Advanced Analytics Constants"""
1
+ """
2
+ Constants for admin analytics app.
3
+ """
2
4
 
3
5
  from enum import Enum
4
6
 
5
7
 
6
- class Granularity(Enum):
7
- """Granularity choices"""
8
- DAILY = 'Daily'
9
- WEEKLY = 'Weekly'
10
- MONTHLY = 'Monthly'
11
- QUARTERLY = 'Quarterly'
12
-
13
-
14
- class Calculation(Enum):
15
- """Calculation choices"""
16
- TOTAL = 'Total'
17
- RUNNING_TOTAL = 'Running Total'
18
- MOVING_AVERAGE_3_PERIOD = 'Moving Average (3 Period)'
19
- MOVING_AVERAGE_7_PERIOD = 'Moving Average (7 Period)'
20
-
21
-
22
8
  class ResponseType(Enum):
23
9
  """Response type choices"""
24
10
  JSON = 'json'
@@ -3,138 +3,13 @@ Utility functions for fetching data from the database.
3
3
  """
4
4
  from logging import getLogger
5
5
 
6
- import numpy
7
6
  import pandas
8
7
 
9
- from django.http import Http404
10
-
11
8
  from enterprise_data.admin_analytics.database import run_query
12
- from enterprise_data.utils import timer
13
9
 
14
10
  LOGGER = getLogger(__name__)
15
11
 
16
12
 
17
- def get_select_query(table: str, columns: list, enterprise_uuid: str) -> str:
18
- """
19
- Generate a SELECT query for the given table and columns.
20
-
21
- Arguments:
22
- table (str): The table to query.
23
- columns (list): The columns to select.
24
- enterprise_uuid (str): The UUID of the enterprise customer.
25
-
26
- Returns:
27
- (str): The SELECT query.
28
- """
29
- return f'SELECT {", ".join(columns)} FROM {table} WHERE enterprise_customer_uuid = "{enterprise_uuid}"'
30
-
31
-
32
- def fetch_enrollment_data(enterprise_uuid: str):
33
- """
34
- Fetch enrollment data from the database for the given enterprise customer.
35
-
36
- Arguments:
37
- enterprise_uuid (str): The UUID of the enterprise customer.
38
-
39
- Returns:
40
- (pandas.DataFrame): The enrollment data.
41
- """
42
- enterprise_uuid = enterprise_uuid.replace('-', '')
43
-
44
- columns = [
45
- 'enterprise_customer_name',
46
- 'enterprise_customer_uuid',
47
- 'lms_enrollment_id',
48
- 'user_id',
49
- 'email',
50
- 'course_key',
51
- 'courserun_key',
52
- 'course_id',
53
- 'course_subject',
54
- 'course_title',
55
- 'enterprise_enrollment_date',
56
- 'lms_enrollment_mode',
57
- 'enroll_type',
58
- 'program_title',
59
- 'date_certificate_awarded',
60
- 'grade_percent',
61
- 'cert_awarded',
62
- 'date_certificate_created_raw',
63
- 'passed_date_raw',
64
- 'passed_date',
65
- 'has_passed',
66
- ]
67
- query = get_select_query(
68
- table='fact_enrollment_admin_dash',
69
- columns=columns,
70
- enterprise_uuid=enterprise_uuid,
71
- )
72
-
73
- with timer('fetch_enrollment_data'):
74
- results = run_query(query=query)
75
-
76
- if not results:
77
- raise Http404(f'No enrollment data found for enterprise {enterprise_uuid}')
78
-
79
- LOGGER.info(f'[PLOTLY] Enrollment data fetched successfully. Records: {len(results)}')
80
- enrollments = pandas.DataFrame(numpy.array(results), columns=columns)
81
- LOGGER.info('[PLOTLY] Enrollment data converted to DataFrame.')
82
-
83
- # Convert date columns to datetime.
84
- enrollments['enterprise_enrollment_date'] = enrollments['enterprise_enrollment_date'].astype('datetime64[ns]')
85
- enrollments['date_certificate_awarded'] = enrollments['date_certificate_awarded'].astype('datetime64[ns]')
86
- enrollments['date_certificate_created_raw'] = enrollments['date_certificate_created_raw'].astype('datetime64[ns]')
87
- enrollments['passed_date_raw'] = enrollments['passed_date_raw'].astype('datetime64[ns]')
88
- enrollments['passed_date'] = enrollments['passed_date'].astype('datetime64[ns]')
89
-
90
- return enrollments
91
-
92
-
93
- def fetch_engagement_data(enterprise_uuid: str):
94
- """
95
- Fetch engagement data from the database for the given enterprise customer.
96
-
97
- Arguments:
98
- enterprise_uuid (str): The UUID of the enterprise customer.
99
-
100
- Returns:
101
- (pandas.DataFrame): The engagement data.
102
- """
103
- enterprise_uuid = enterprise_uuid.replace('-', '')
104
-
105
- columns = [
106
- 'user_id',
107
- 'email',
108
- 'enterprise_customer_uuid',
109
- 'course_key',
110
- 'enroll_type',
111
- 'activity_date',
112
- 'course_title',
113
- 'course_subject',
114
- 'is_engaged',
115
- 'is_engaged_video',
116
- 'is_engaged_forum',
117
- 'is_engaged_problem',
118
- 'is_active',
119
- 'learning_time_seconds',
120
- ]
121
- query = get_select_query(
122
- table='fact_enrollment_engagement_day_admin_dash', columns=columns, enterprise_uuid=enterprise_uuid
123
- )
124
-
125
- with timer('fetch_engagement_data'):
126
- results = run_query(query=query)
127
- if not results:
128
- raise Http404(f'No engagement data found for enterprise {enterprise_uuid}')
129
-
130
- LOGGER.info(f'[PLOTLY] Engagement data fetched successfully. Records: {len(results)}')
131
- engagement = pandas.DataFrame(numpy.array(results), columns=columns)
132
- LOGGER.info('[PLOTLY] Engagement data converted to DataFrame.')
133
- engagement['activity_date'] = engagement['activity_date'].astype('datetime64[ns]')
134
-
135
- return engagement
136
-
137
-
138
13
  def fetch_max_enrollment_datetime():
139
14
  """
140
15
  Fetch the latest created date from the enterprise_learner_enrollment table.
@@ -109,3 +109,88 @@ class FactEngagementAdminDashQueries:
109
109
  GROUP BY activity_date, enroll_type
110
110
  ORDER BY activity_date;
111
111
  """
112
+
113
+ @staticmethod
114
+ def get_all_leaderboard_data_query():
115
+ """
116
+ Get the query to fetch the leaderboard data.
117
+
118
+ Query should fetch the leaderboard data for the enterprise customer to show in the data table.
119
+
120
+ Returns:
121
+ (str): Query to fetch the leaderboard data.
122
+ """
123
+ return """
124
+ WITH Engagement AS (
125
+ SELECT
126
+ email,
127
+ ROUND(SUM(learning_time_seconds) / 3600, 1) as learning_time_hours,
128
+ SUM(is_engaged) as session_count,
129
+ CASE
130
+ WHEN SUM(is_engaged) = 0 THEN 0.0
131
+ ELSE ROUND(SUM(learning_time_seconds) / 3600 / SUM(is_engaged), 1)
132
+ END AS average_session_length
133
+ FROM fact_enrollment_engagement_day_admin_dash
134
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
135
+ (activity_date BETWEEN %(start_date)s AND %(end_date)s) AND
136
+ is_engaged = 1
137
+ GROUP BY email
138
+ ),
139
+ Completions AS (
140
+ SELECT email, count(course_key) as course_completion_count
141
+ FROM fact_enrollment_admin_dash
142
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
143
+ (passed_date BETWEEN %(start_date)s AND %(end_date)s) AND
144
+ has_passed = 1
145
+ GROUP BY email
146
+ )
147
+ SELECT
148
+ Engagement.email,
149
+ Engagement.learning_time_hours,
150
+ Engagement.session_count,
151
+ Engagement.average_session_length,
152
+ Completions.course_completion_count
153
+ FROM Engagement
154
+ LEFT JOIN Completions
155
+ ON Engagement.email = Completions.email
156
+ ORDER BY
157
+ Engagement.learning_time_hours DESC,
158
+ Engagement.session_count DESC,
159
+ Completions.course_completion_count DESC
160
+ LIMIT %(limit)s OFFSET %(offset)s;
161
+ """
162
+
163
+ @staticmethod
164
+ def get_leaderboard_data_count_query():
165
+ """
166
+ Get the query to fetch the leaderboard row count.
167
+
168
+ Query should fetch the count of rows for the leaderboard data for the enterprise customer.
169
+
170
+ Returns:
171
+ (str): Query to fetch the leaderboard row count.
172
+ """
173
+ return """
174
+ WITH Engagement AS (
175
+ SELECT
176
+ email
177
+ FROM fact_enrollment_engagement_day_admin_dash
178
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
179
+ (activity_date BETWEEN %(start_date)s AND %(end_date)s) AND
180
+ is_engaged = 1
181
+ GROUP BY email
182
+ ),
183
+ Completions AS (
184
+ SELECT email, count(course_key) as course_completion_count
185
+ FROM fact_enrollment_admin_dash
186
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
187
+ (passed_date BETWEEN %(start_date)s AND %(end_date)s) AND
188
+ has_passed = 1
189
+ GROUP BY email
190
+ )
191
+ SELECT
192
+ count(*)
193
+ FROM Engagement
194
+ LEFT JOIN Completions
195
+ ON Engagement.email = Completions.email
196
+ """
@@ -152,3 +152,53 @@ class FactEngagementAdminDashTable(BaseTable):
152
152
  },
153
153
  as_dict=True,
154
154
  )
155
+
156
+ def get_all_leaderboard_data(
157
+ self, enterprise_customer_uuid: UUID, start_date: date, end_date: date, limit: int, offset: int
158
+ ):
159
+ """
160
+ Get the leaderboard data for the given enterprise customer.
161
+
162
+ Arguments:
163
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
164
+ start_date (date): The start date.
165
+ end_date (date): The end date.
166
+ limit (int): The maximum number of records to return.
167
+ offset (int): The number of records to skip.
168
+
169
+ Returns:
170
+ list[dict]: The leaderboard data.
171
+ """
172
+ return run_query(
173
+ query=self.queries.get_all_leaderboard_data_query(),
174
+ params={
175
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
176
+ 'start_date': start_date,
177
+ 'end_date': end_date,
178
+ 'limit': limit,
179
+ 'offset': offset,
180
+ },
181
+ as_dict=True,
182
+ )
183
+
184
+ def get_leaderboard_data_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
185
+ """
186
+ Get the total number of leaderboard records for the given enterprise customer.
187
+
188
+ Arguments:
189
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
190
+ start_date (date): The start date.
191
+ end_date (date): The end date.
192
+
193
+ Returns:
194
+ (int): The total number of leaderboard records.
195
+ """
196
+ results = run_query(
197
+ query=self.queries.get_leaderboard_data_count_query(),
198
+ params={
199
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
200
+ 'start_date': start_date,
201
+ 'end_date': end_date,
202
+ }
203
+ )
204
+ return results[0][0]
@@ -5,7 +5,7 @@ from uuid import UUID
5
5
 
6
6
  from rest_framework import serializers
7
7
 
8
- from enterprise_data.admin_analytics.constants import Calculation, Granularity, ResponseType
8
+ from enterprise_data.admin_analytics.constants import ResponseType
9
9
  from enterprise_data.models import (
10
10
  EnterpriseAdminLearnerProgress,
11
11
  EnterpriseAdminSummarizeInsights,
@@ -241,23 +241,8 @@ class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: d
241
241
  ResponseType.JSON.value,
242
242
  ResponseType.CSV.value
243
243
  ]
244
- GRANULARITY_CHOICES = [
245
- Granularity.DAILY.value,
246
- Granularity.WEEKLY.value,
247
- Granularity.MONTHLY.value,
248
- Granularity.QUARTERLY.value
249
- ]
250
- CALCULATION_CHOICES = [
251
- Calculation.TOTAL.value,
252
- Calculation.RUNNING_TOTAL.value,
253
- Calculation.MOVING_AVERAGE_3_PERIOD.value,
254
- Calculation.MOVING_AVERAGE_7_PERIOD.value
255
- ]
256
-
257
244
  start_date = serializers.DateField(required=False)
258
245
  end_date = serializers.DateField(required=False)
259
- granularity = serializers.CharField(required=False)
260
- calculation = serializers.CharField(required=False)
261
246
  response_type = serializers.CharField(required=False)
262
247
  page = serializers.IntegerField(required=False, min_value=1)
263
248
  page_size = serializers.IntegerField(required=False, min_value=2)
@@ -287,25 +272,3 @@ class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: d
287
272
  if value not in self.RESPONSE_TYPES:
288
273
  raise serializers.ValidationError(f"response_type must be one of {self.RESPONSE_TYPES}")
289
274
  return value
290
-
291
- def validate_granularity(self, value):
292
- """
293
- Validate the granularity value.
294
-
295
- Raises:
296
- serializers.ValidationError: If granularity is not one of the valid choices.
297
- """
298
- if value not in self.GRANULARITY_CHOICES:
299
- raise serializers.ValidationError(f"Granularity must be one of {self.GRANULARITY_CHOICES}")
300
- return value
301
-
302
- def validate_calculation(self, value):
303
- """
304
- Validate the calculation value.
305
-
306
- Raises:
307
- serializers.ValidationError: If calculation is not one of the valid choices
308
- """
309
- if value not in self.CALCULATION_CHOICES:
310
- raise serializers.ValidationError(f"Calculation must be one of {self.CALCULATION_CHOICES}")
311
- return value
@@ -58,8 +58,8 @@ urlpatterns = [
58
58
  ),
59
59
  re_path(
60
60
  fr'^admin/analytics/(?P<enterprise_uuid>{UUID4_REGEX})/leaderboard$',
61
- AdvanceAnalyticsLeaderboardView.as_view(),
62
- name='enterprise-admin-analytics-leaderboard'
61
+ AdvanceAnalyticsLeaderboardView.as_view({'get': 'list'}),
62
+ name='enterprise-admin-analytics-leaderboard-list'
63
63
  ),
64
64
  re_path(
65
65
  fr'^admin/analytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments/stats$',
@@ -15,7 +15,6 @@ from django.http import StreamingHttpResponse
15
15
 
16
16
  from enterprise_data.admin_analytics.constants import ResponseType
17
17
  from enterprise_data.admin_analytics.database.tables import FactEnrollmentAdminDashTable
18
- from enterprise_data.api.v1.paginators import AdvanceAnalyticsPagination
19
18
  from enterprise_data.api.v1.serializers import AdvanceAnalyticsQueryParamSerializer
20
19
  from enterprise_data.api.v1.views.base import AnalyticsPaginationMixin
21
20
  from enterprise_data.renderers import IndividualCompletionsCSVRenderer
@@ -33,7 +32,6 @@ class AdvanceAnalyticsCompletionsView(AnalyticsPaginationMixin, ViewSet):
33
32
  2. `enterprise_data_api_v1.enterprise-learner-completion-stats`: Get completion stats data.
34
33
  """
35
34
  authentication_classes = (JwtAuthentication,)
36
- pagination_class = AdvanceAnalyticsPagination
37
35
  http_method_names = ('get', )
38
36
 
39
37
  @permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
@@ -15,7 +15,6 @@ from django.http import StreamingHttpResponse
15
15
 
16
16
  from enterprise_data.admin_analytics.constants import ResponseType
17
17
  from enterprise_data.admin_analytics.database.tables import FactEngagementAdminDashTable, FactEnrollmentAdminDashTable
18
- from enterprise_data.api.v1.paginators import AdvanceAnalyticsPagination
19
18
  from enterprise_data.api.v1.serializers import AdvanceAnalyticsQueryParamSerializer
20
19
  from enterprise_data.api.v1.views.base import AnalyticsPaginationMixin
21
20
  from enterprise_data.renderers import IndividualEngagementsCSVRenderer
@@ -33,7 +32,6 @@ class AdvanceAnalyticsEngagementView(AnalyticsPaginationMixin, ViewSet):
33
32
  2. `enterprise_data_api_v1.enterprise-learner-engagement-stats`: Get engagement stats data.
34
33
  """
35
34
  authentication_classes = (JwtAuthentication,)
36
- pagination_class = AdvanceAnalyticsPagination
37
35
  http_method_names = ('get', )
38
36
 
39
37
  @permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
@@ -14,7 +14,6 @@ from django.http import StreamingHttpResponse
14
14
 
15
15
  from enterprise_data.admin_analytics.constants import ResponseType
16
16
  from enterprise_data.admin_analytics.database.tables import FactEnrollmentAdminDashTable
17
- from enterprise_data.api.v1.paginators import AdvanceAnalyticsPagination
18
17
  from enterprise_data.api.v1.serializers import AdvanceAnalyticsQueryParamSerializer
19
18
  from enterprise_data.api.v1.views.base import AnalyticsPaginationMixin
20
19
  from enterprise_data.renderers import IndividualEnrollmentsCSVRenderer
@@ -32,7 +31,6 @@ class AdvanceAnalyticsEnrollmentsView(AnalyticsPaginationMixin, ViewSet):
32
31
  2. `enterprise_data_api_v1.enterprise-learner-enrollment-stats`: Get enrollment stats data.
33
32
  """
34
33
  authentication_classes = (JwtAuthentication,)
35
- pagination_class = AdvanceAnalyticsPagination
36
34
  http_method_names = ('get', )
37
35
 
38
36
  @permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)