edx-enterprise-data 8.7.0__py3-none-any.whl → 8.8.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edx-enterprise-data
3
- Version: 8.7.0
3
+ Version: 8.8.1
4
4
  Summary: Enterprise Reporting
5
5
  Home-page: https://github.com/openedx/edx-enterprise-data
6
6
  Author: edX
@@ -1,20 +1,20 @@
1
- enterprise_data/__init__.py,sha256=uaNc7nZk0jrnfiaQzk9MA9gKcNIQ30m99eUJ0R4k1X8,123
1
+ enterprise_data/__init__.py,sha256=SFfjo0qp4b4IjWzit7qM4Eib5E1or2vxpohALjmvTNM,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=eh-FZFbP_yWcfDemavUGB7vYIJA2PjW_dvM79qxYZz8,2085
8
+ enterprise_data/renderers.py,sha256=9gIzavWspZTk4vDfVKXJtdn0tSZ2xNgkF-Akf7AWIDM,2389
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=kNO4nW_GBpBiIBlVUkCb4Xo0k1oVshT8nDOBP5eWoV8,2643
11
+ enterprise_data/utils.py,sha256=KykylyK9KTauj239-_V5w5iQHQBhFF3iuY3Df7p6Q5E,3017
12
12
  enterprise_data/admin_analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  enterprise_data/admin_analytics/completions_utils.py,sha256=kGmLy7x6aD0coNYgzLa5XzJypLkGTT5clDHLSH_QFDE,9442
14
- enterprise_data/admin_analytics/constants.py,sha256=6Gc9rP-J3nGgAk3fhzNlyR1HMq8Apjxs9ViWJiYrri4,722
15
- enterprise_data/admin_analytics/data_loaders.py,sha256=x1XNYdtJV1G9cv0SeBZqYitRV8-GlJXtEZ2cc2OJU7M,5415
14
+ enterprise_data/admin_analytics/constants.py,sha256=aHDgTHdsjbKNpgtNLDsl4giqhhrRkCGi72ysGIEk0Ao,817
15
+ enterprise_data/admin_analytics/data_loaders.py,sha256=YpSyygATVFtItcWlkIHvjsX5Lh1qMw6onDK-ZHP_AUw,5586
16
16
  enterprise_data/admin_analytics/database.py,sha256=mNS_9xE5h6O7oMMzr6kr6LDTTSNvKzo8vaM-YG8tOd8,1312
17
- enterprise_data/admin_analytics/utils.py,sha256=w1GpdJCvt-ocwwLEaag4YVO5XH31NCvjwAGFYAiwUZE,10213
17
+ enterprise_data/admin_analytics/utils.py,sha256=CQuTlg36AALJiopp4us-JN8oTXsw-jDXSJenbphLDME,12270
18
18
  enterprise_data/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  enterprise_data/api/urls.py,sha256=POqc_KATHdnpMf9zHtpO46pKD5KAlAExtx7G6iylLcU,273
20
20
  enterprise_data/api/v0/__init__.py,sha256=1aAzAYU5hk-RW6cKUxa1645cbZMxn7GIZ7OMjWc9MKI,46
@@ -22,14 +22,15 @@ enterprise_data/api/v0/serializers.py,sha256=dngZTk6DhRxApchQKCMp1B_c8aVnQtH0NCq
22
22
  enterprise_data/api/v0/urls.py,sha256=vzJjqIo_S3AXWs9Us8XTaJc3FnxLbYzAkmLyuDQqum0,699
23
23
  enterprise_data/api/v0/views.py,sha256=4RslZ4NZOU-844bnebEQ71ji2utRY7jEijqC45oQQD0,14380
24
24
  enterprise_data/api/v1/__init__.py,sha256=1aAzAYU5hk-RW6cKUxa1645cbZMxn7GIZ7OMjWc9MKI,46
25
- enterprise_data/api/v1/paginators.py,sha256=OHbuBP7hAFJ_ce0UAMfJ1pARMMzqvzVYiYeFMw3xZLU,3592
26
- enterprise_data/api/v1/serializers.py,sha256=SaMFlNRMPIqaKelFZa7nYn3su7DJFS2wJfYZ5sUl_gM,12000
27
- enterprise_data/api/v1/urls.py,sha256=JLjkMzTiJmHtDjN5_z4hOe0s1ug8Ec3A4Ll1E5pwVmY,3206
25
+ enterprise_data/api/v1/paginators.py,sha256=f0xsilLaU94jSBltJk46tR1rLEIt7YrqSzMAAVtPXjA,3592
26
+ enterprise_data/api/v1/serializers.py,sha256=9F2LGa8IKvglgeYNHw3Q0eEZUWknwHZMNZOdpDviEo4,12327
27
+ enterprise_data/api/v1/urls.py,sha256=xFsBf3TTsdblFAiHq1Bj3h82Ye1PS3cgqLC0pIso2js,3504
28
28
  enterprise_data/api/v1/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
- enterprise_data/api/v1/views/analytics_enrollments.py,sha256=SvuK53i_4S3etqktoJ0h5ky4dntD176u6DXZuqHTsEg,16352
29
+ enterprise_data/api/v1/views/analytics_enrollments.py,sha256=om8i5HTDxDMSny1fy49Wh38LdYLLz-k-oFQJqlQLOxg,16812
30
+ enterprise_data/api/v1/views/analytics_leaderboard.py,sha256=EE34fJWYp8OGbIj8oRLlgMhbSm37w98oPXn1aPIZWWE,5860
30
31
  enterprise_data/api/v1/views/base.py,sha256=FTAxlz5EzvAY657wzVgzhJPFSCHHzct7IDcvm71Smt8,866
31
- enterprise_data/api/v1/views/enterprise_admin.py,sha256=7f1RHlXxmH8oLr0WLxdGPNsxdhjubwyqNIefb7PMH68,9149
32
- enterprise_data/api/v1/views/enterprise_completions.py,sha256=LGwwCiJSBVhsm52ROIDAJQkLAaj4Wbxh-A-1INfZbOE,7433
32
+ enterprise_data/api/v1/views/enterprise_admin.py,sha256=F34FbOz1gLwHPUAQOg3mOjZoKirbCRrjZS2xFJSUBpw,9274
33
+ enterprise_data/api/v1/views/enterprise_completions.py,sha256=OkH6eYNql6UGVU-Xjx7PzwzaHeUHfa6e4WJmkUoHGDM,8221
33
34
  enterprise_data/api/v1/views/enterprise_learner.py,sha256=yABjJje3CT8I8YOhWr1_tTkdKtnGJom8eu3EFz_-0BU,18517
34
35
  enterprise_data/api/v1/views/enterprise_offers.py,sha256=VifxgqTLFLVw4extYPlHcN1N_yjXcsYsAlYEnAbpb10,1266
35
36
  enterprise_data/fixtures/enterprise_enrollment.json,sha256=6onPXXR29pMdTdbl_mn81sDi3Re5jkLUZz2TPMB_1IY,5786
@@ -103,10 +104,12 @@ enterprise_data/tests/test_models.py,sha256=MWBY-LY5TPBjZ4GlvpM-h4W-BvRKr2Rml8Bz
103
104
  enterprise_data/tests/test_utils.py,sha256=vbmYM7DMN-lHS2p4yaa0Yd6uSGXd2qoZRDE9X3J4Sec,18385
104
105
  enterprise_data/tests/test_views.py,sha256=UvDRNTxruy5zBK_KgUy2cBMbwlaTW_vkM0-TCXbQZiY,69667
105
106
  enterprise_data/tests/admin_analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
- enterprise_data/tests/admin_analytics/mock_enrollments.py,sha256=kINSGZx7M3CcsVZ04AZAy4AqNGVbRD47OfI2_3dEVSs,7262
107
- enterprise_data/tests/admin_analytics/test_analytics_enrollments.py,sha256=WNdJCM52zUJikBTl3VakPIvNiVNvUed-8vk275njSdY,14847
107
+ enterprise_data/tests/admin_analytics/mock_analytics_data.py,sha256=mLGTIP89VAQgqKtYlYnKUAKyZATzKnBtI_5aJwMmr7Q,18344
108
+ enterprise_data/tests/admin_analytics/mock_enrollments.py,sha256=LfuMo9Kn-OQD4z42G3BRuM5MXUUXXlaAMhTqfJf46XE,7266
109
+ enterprise_data/tests/admin_analytics/test_analytics_enrollments.py,sha256=UdKRkP6BNbsSo-gm0YCoddT-ReUMI1x9E6HNLSHT7pY,15177
110
+ enterprise_data/tests/admin_analytics/test_analytics_leaderboard.py,sha256=VSEyDAHfWBJvqmx9yzd4NnPAqK3TqaKrMBWswMAdzfU,6206
108
111
  enterprise_data/tests/admin_analytics/test_data_loaders.py,sha256=o3denJ4aUS1pI5Crksl4C6m-NtCBm8ynoHBnLkf-v2U,4641
109
- enterprise_data/tests/admin_analytics/test_enterprise_completions.py,sha256=BXptZVfnTReJfMSog-ZMDnoIe6Gh_sX2GDRzyugxSMI,7356
112
+ enterprise_data/tests/admin_analytics/test_enterprise_completions.py,sha256=afkHQFy4bvqZ0pq5Drl1t2nv8zxbgca2jzOQbihlPG0,7359
110
113
  enterprise_data/tests/admin_analytics/test_utils.py,sha256=y33HXy6BDOoftdcz3qYlOYhgx7JSXDki-OLzBdTpiwA,11449
111
114
  enterprise_data/tests/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
112
115
  enterprise_data/tests/api/v0/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -156,8 +159,8 @@ enterprise_reporting/tests/test_send_enterprise_reports.py,sha256=WtL-RqGgu2x5PP
156
159
  enterprise_reporting/tests/test_utils.py,sha256=Zt_TA0LVb-B6fQGkUkAKKVlUKKnQh8jnw1US1jKe7g8,9493
157
160
  enterprise_reporting/tests/test_vertica_client.py,sha256=-R2yNCGUjRtoXwLMBloVFQkFYrJoo613VCr61gwI3kQ,140
158
161
  enterprise_reporting/tests/utils.py,sha256=xms2LM7DV3wczXEfctOK1ddel1EE0J_YSr17UzbCDy4,1401
159
- edx_enterprise_data-8.7.0.dist-info/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
160
- edx_enterprise_data-8.7.0.dist-info/METADATA,sha256=6ZncVDwM9a49UHdde9Rk1fhjP1BEaradbGjKCHSX_WE,1569
161
- edx_enterprise_data-8.7.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
162
- edx_enterprise_data-8.7.0.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
163
- edx_enterprise_data-8.7.0.dist-info/RECORD,,
162
+ edx_enterprise_data-8.8.1.dist-info/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
163
+ edx_enterprise_data-8.8.1.dist-info/METADATA,sha256=hQ_trgT7XN1RJtLpZUIF33MB0ycCnKRAf1aOKxatsWw,1569
164
+ edx_enterprise_data-8.8.1.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
165
+ edx_enterprise_data-8.8.1.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
166
+ edx_enterprise_data-8.8.1.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__ = "8.7.0"
5
+ __version__ = "8.8.1"
@@ -3,7 +3,7 @@
3
3
  from enum import Enum
4
4
 
5
5
 
6
- class GRANULARITY(Enum):
6
+ class Granularity(Enum):
7
7
  """Granularity choices"""
8
8
  DAILY = 'Daily'
9
9
  WEEKLY = 'Weekly'
@@ -11,7 +11,7 @@ class GRANULARITY(Enum):
11
11
  QUARTERLY = 'Quarterly'
12
12
 
13
13
 
14
- class CALCULATION(Enum):
14
+ class Calculation(Enum):
15
15
  """Calculation choices"""
16
16
  TOTAL = 'Total'
17
17
  RUNNING_TOTAL = 'Running Total'
@@ -19,9 +19,15 @@ class CALCULATION(Enum):
19
19
  MOVING_AVERAGE_7_PERIOD = 'Moving Average (7 Period)'
20
20
 
21
21
 
22
- class ENROLLMENT_CSV(Enum):
22
+ class EnrollmentChart(Enum):
23
23
  """CSV choices"""
24
24
  ENROLLMENTS_OVER_TIME = 'enrollments_over_time'
25
25
  TOP_COURSES_BY_ENROLLMENTS = 'top_courses_by_enrollments'
26
26
  TOP_SUBJECTS_BY_ENROLLMENTS = 'top_subjects_by_enrollments'
27
27
  INDIVIDUAL_ENROLLMENTS = 'individual_enrollments'
28
+
29
+
30
+ class ResponseType(Enum):
31
+ """Response type choices"""
32
+ JSON = 'json'
33
+ CSV = 'csv'
@@ -7,6 +7,7 @@ import pandas
7
7
  from django.http import Http404
8
8
 
9
9
  from enterprise_data.admin_analytics.database import run_query
10
+ from enterprise_data.utils import timer
10
11
 
11
12
 
12
13
  def get_select_query(table: str, columns: list, enterprise_uuid: str) -> str:
@@ -65,7 +66,8 @@ def fetch_enrollment_data(enterprise_uuid: str):
65
66
  enterprise_uuid=enterprise_uuid,
66
67
  )
67
68
 
68
- results = run_query(query=query)
69
+ with timer('fetch_enrollment_data'):
70
+ results = run_query(query=query)
69
71
  if not results:
70
72
  raise Http404(f'No enrollment data found for enterprise {enterprise_uuid}')
71
73
 
@@ -113,7 +115,8 @@ def fetch_engagement_data(enterprise_uuid: str):
113
115
  table='fact_enrollment_engagement_day_admin_dash', columns=columns, enterprise_uuid=enterprise_uuid
114
116
  )
115
117
 
116
- results = run_query(query=query)
118
+ with timer('fetch_engagement_data'):
119
+ results = run_query(query=query)
117
120
  if not results:
118
121
  raise Http404(f'No engagement data found for enterprise {enterprise_uuid}')
119
122
 
@@ -171,7 +174,8 @@ def fetch_skills_data(enterprise_uuid: str):
171
174
  table='skills_daily_rollup_admin_dash', columns=cols, enterprise_uuid=enterprise_uuid
172
175
  )
173
176
 
174
- skills = run_query(query=query)
177
+ with timer('fetch_skills_data'):
178
+ skills = run_query(query=query)
175
179
 
176
180
  if not skills:
177
181
  raise Http404(f'No skills data found for enterprise {enterprise_uuid}')
@@ -1,15 +1,23 @@
1
1
  """
2
2
  Utility functions for fetching data from the database.
3
3
  """
4
- from datetime import datetime
4
+ from datetime import datetime, timedelta
5
5
  from enum import Enum
6
+ from logging import getLogger
6
7
 
7
8
  from edx_django_utils.cache import TieredCache, get_cache_key
8
9
 
9
- from enterprise_data.admin_analytics.constants import CALCULATION, GRANULARITY
10
- from enterprise_data.admin_analytics.data_loaders import fetch_engagement_data, fetch_enrollment_data, fetch_skills_data
10
+ from enterprise_data.admin_analytics.constants import Calculation, Granularity
11
+ from enterprise_data.admin_analytics.data_loaders import (
12
+ fetch_engagement_data,
13
+ fetch_enrollment_data,
14
+ fetch_max_enrollment_datetime,
15
+ fetch_skills_data,
16
+ )
11
17
  from enterprise_data.utils import date_filter, primary_subject_truncate
12
18
 
19
+ LOGGER = getLogger(__name__)
20
+
13
21
 
14
22
  class ChartType(Enum):
15
23
  """
@@ -23,14 +31,45 @@ class ChartType(Enum):
23
31
  TOP_SUBJECTS_BY_COMPLETIONS = 'top_subjects_by_completions'
24
32
 
25
33
 
34
+ def fetch_enrollments_cache_expiry_timestamp():
35
+ """Calculate cache expiry timestamp"""
36
+ # TODO: Implement correct cache expiry logic for `enrollments` data.
37
+ # Current cache expiry logic is based on `enterprise_learner_enrollment` table,
38
+ # Which has nothing to do with the `enrollments` data. Instead cache expiry should
39
+ # be based on `fact_enrollment_admin_dash` table. Currently we have no timestamp in
40
+ # `fact_enrollment_admin_dash` table that can be used for cache expiry. Add a new
41
+ # column in the table for this purpose and then use that column for cache expiry.
42
+ last_updated_at = fetch_max_enrollment_datetime()
43
+ cache_expiry = (
44
+ last_updated_at + timedelta(days=1) if last_updated_at else datetime.now()
45
+ )
46
+ return cache_expiry
47
+
48
+
49
+ def fetch_engagements_cache_expiry_timestamp():
50
+ """Calculate cache expiry timestamp"""
51
+ # TODO: Implement correct cache expiry logic for `engagements` data.
52
+ # Current cache expiry logic is based on `enterprise_learner_enrollment` table,
53
+ # Which has nothing to do with the `engagements` data. Instead cache expiry should
54
+ # be based on `fact_enrollment_engagement_day_admin_dash` table. Currently we have
55
+ # no timestamp in `fact_enrollment_engagement_day_admin_dash` table that can be used
56
+ # for cache expiry. Add a new column in the table for this purpose and then use that
57
+ # column for cache expiry.
58
+ last_updated_at = fetch_max_enrollment_datetime()
59
+ cache_expiry = (
60
+ last_updated_at + timedelta(days=1) if last_updated_at else datetime.now()
61
+ )
62
+ return cache_expiry
63
+
64
+
26
65
  def granularity_aggregation(level, group, date, data_frame, aggregation_type="count"):
27
66
  """Aggregate data based on granularity"""
28
67
  df = data_frame
29
68
 
30
69
  period_mapping = {
31
- GRANULARITY.WEEKLY.value: "W",
32
- GRANULARITY.MONTHLY.value: "M",
33
- GRANULARITY.QUARTERLY.value: "Q"
70
+ Granularity.WEEKLY.value: "W",
71
+ Granularity.MONTHLY.value: "M",
72
+ Granularity.QUARTERLY.value: "Q"
34
73
  }
35
74
 
36
75
  if level in period_mapping:
@@ -52,15 +91,15 @@ def calculation_aggregation(calc, data_frame, aggregation_type="count"):
52
91
  df = data_frame
53
92
 
54
93
  window_mapping = {
55
- CALCULATION.MOVING_AVERAGE_3_PERIOD.value: 3,
56
- CALCULATION.MOVING_AVERAGE_7_PERIOD.value: 7,
94
+ Calculation.MOVING_AVERAGE_3_PERIOD.value: 3,
95
+ Calculation.MOVING_AVERAGE_7_PERIOD.value: 7,
57
96
  }
58
97
 
59
98
  aggregation_column = "count" if aggregation_type == "count" else "sum"
60
99
 
61
- if calc == CALCULATION.RUNNING_TOTAL.value:
100
+ if calc == Calculation.RUNNING_TOTAL.value:
62
101
  df[aggregation_column] = df.groupby("enroll_type")[aggregation_column].cumsum()
63
- elif calc in [CALCULATION.MOVING_AVERAGE_3_PERIOD.value, CALCULATION.MOVING_AVERAGE_7_PERIOD.value]:
102
+ elif calc in [Calculation.MOVING_AVERAGE_3_PERIOD.value, Calculation.MOVING_AVERAGE_7_PERIOD.value]:
64
103
  df[aggregation_column] = (
65
104
  df.groupby("enroll_type")[aggregation_column]
66
105
  .rolling(window_mapping[calc])
@@ -108,6 +147,7 @@ def fetch_and_cache_enrollments_data(enterprise_id, cache_expiry):
108
147
  cached_response = TieredCache.get_cached_response(cache_key)
109
148
 
110
149
  if cached_response.is_found:
150
+ LOGGER.info(f"Enrollments data found in cache for Enterprise [{enterprise_id}]")
111
151
  return cached_response.value
112
152
  else:
113
153
  enrollments = fetch_enrollment_data(enterprise_id)
@@ -135,6 +175,7 @@ def fetch_and_cache_engagements_data(enterprise_id, cache_expiry):
135
175
  cached_response = TieredCache.get_cached_response(cache_key)
136
176
 
137
177
  if cached_response.is_found:
178
+ LOGGER.info(f"Engagements data found in cache for Enterprise [{enterprise_id}]")
138
179
  return cached_response.value
139
180
  else:
140
181
  engagements = fetch_engagement_data(enterprise_id)
@@ -162,6 +203,7 @@ def fetch_and_cache_skills_data(enterprise_id, cache_expiry):
162
203
  cached_response = TieredCache.get_cached_response(cache_key)
163
204
 
164
205
  if cached_response.is_found:
206
+ LOGGER.info(f"Skills data found in cache for Enterprise [{enterprise_id}]")
165
207
  return cached_response.value
166
208
  else:
167
209
  skills = fetch_skills_data(enterprise_id)
@@ -72,7 +72,7 @@ class AdvanceAnalyticsPagination(PageNumberPagination):
72
72
  max_page_size (int): The maximum allowed page size.
73
73
  """
74
74
  page_size_query_param = "page_size"
75
- page_size = 10
75
+ page_size = 50
76
76
  max_page_size = 100
77
77
 
78
78
  def paginate_queryset(self, queryset, request, view=None):
@@ -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, ENROLLMENT_CSV, GRANULARITY
8
+ from enterprise_data.admin_analytics.constants import Calculation, EnrollmentChart, Granularity, ResponseType
9
9
  from enterprise_data.models import (
10
10
  EnterpriseAdminLearnerProgress,
11
11
  EnterpriseAdminSummarizeInsights,
@@ -237,23 +237,28 @@ class EnterpriseExecEdLCModulePerformanceSerializer(serializers.ModelSerializer)
237
237
 
238
238
  class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: disable=abstract-method
239
239
  """Serializer for validating query params"""
240
+ RESPONSE_TYPES = [
241
+ ResponseType.JSON.value,
242
+ ResponseType.CSV.value
243
+ ]
240
244
  GRANULARITY_CHOICES = [
241
- GRANULARITY.DAILY.value,
242
- GRANULARITY.WEEKLY.value,
243
- GRANULARITY.MONTHLY.value,
244
- GRANULARITY.QUARTERLY.value
245
+ Granularity.DAILY.value,
246
+ Granularity.WEEKLY.value,
247
+ Granularity.MONTHLY.value,
248
+ Granularity.QUARTERLY.value
245
249
  ]
246
250
  CALCULATION_CHOICES = [
247
- CALCULATION.TOTAL.value,
248
- CALCULATION.RUNNING_TOTAL.value,
249
- CALCULATION.MOVING_AVERAGE_3_PERIOD.value,
250
- CALCULATION.MOVING_AVERAGE_7_PERIOD.value
251
+ Calculation.TOTAL.value,
252
+ Calculation.RUNNING_TOTAL.value,
253
+ Calculation.MOVING_AVERAGE_3_PERIOD.value,
254
+ Calculation.MOVING_AVERAGE_7_PERIOD.value
251
255
  ]
252
256
 
253
257
  start_date = serializers.DateField(required=False)
254
258
  end_date = serializers.DateField(required=False)
255
259
  granularity = serializers.CharField(required=False)
256
260
  calculation = serializers.CharField(required=False)
261
+ response_type = serializers.CharField(required=False)
257
262
 
258
263
  def validate(self, attrs):
259
264
  """
@@ -270,6 +275,17 @@ class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: d
270
275
 
271
276
  return attrs
272
277
 
278
+ def validate_response_type(self, value):
279
+ """
280
+ Validate the response_type value.
281
+
282
+ Raises:
283
+ serializers.ValidationError: If response_type is not one of the valid choices in `RESPONSE_TYPES`.
284
+ """
285
+ if value not in self.RESPONSE_TYPES:
286
+ raise serializers.ValidationError(f"response_type must be one of {self.RESPONSE_TYPES}")
287
+ return value
288
+
273
289
  def validate_granularity(self, value):
274
290
  """
275
291
  Validate the granularity value.
@@ -293,32 +309,25 @@ class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: d
293
309
  return value
294
310
 
295
311
 
296
- class AdvanceAnalyticsEnrollmentSerializer(AdvanceAnalyticsQueryParamSerializer): # pylint: disable=abstract-method
297
- """Serializer for validating Advance Analytics Enrollments API"""
298
- CSV_TYPES = [
299
- ENROLLMENT_CSV.INDIVIDUAL_ENROLLMENTS.value
312
+ class AdvanceAnalyticsEnrollmentStatsSerializer(
313
+ AdvanceAnalyticsQueryParamSerializer
314
+ ): # pylint: disable=abstract-method
315
+ """Serializer for validating Advance Analytics Enrollments Stats API"""
316
+ CHART_TYPES = [
317
+ EnrollmentChart.ENROLLMENTS_OVER_TIME.value,
318
+ EnrollmentChart.TOP_COURSES_BY_ENROLLMENTS.value,
319
+ EnrollmentChart.TOP_SUBJECTS_BY_ENROLLMENTS.value
300
320
  ]
301
321
 
302
- csv_type = serializers.CharField(required=False)
322
+ chart_type = serializers.CharField(required=False)
303
323
 
304
- def validate_csv_type(self, value):
324
+ def validate_chart_type(self, value):
305
325
  """
306
- Validate the csv_type value.
326
+ Validate the chart_type value.
307
327
 
308
328
  Raises:
309
- serializers.ValidationError: If csv_type is not one of the valid choices
329
+ serializers.ValidationError: If chart_type is not one of the valid choices
310
330
  """
311
- if value not in self.CSV_TYPES:
312
- raise serializers.ValidationError(f"csv_type must be one of {self.CSV_TYPES}")
331
+ if value not in self.CHART_TYPES:
332
+ raise serializers.ValidationError(f"chart_type must be one of {self.CHART_TYPES}")
313
333
  return value
314
-
315
-
316
- class AdvanceAnalyticsEnrollmentStatsSerializer(
317
- AdvanceAnalyticsEnrollmentSerializer
318
- ): # pylint: disable=abstract-method
319
- """Serializer for validating Advance Analytics Enrollments Stats API"""
320
- CSV_TYPES = [
321
- ENROLLMENT_CSV.ENROLLMENTS_OVER_TIME.value,
322
- ENROLLMENT_CSV.TOP_COURSES_BY_ENROLLMENTS.value,
323
- ENROLLMENT_CSV.TOP_SUBJECTS_BY_ENROLLMENTS.value
324
- ]
@@ -15,6 +15,7 @@ from enterprise_data.api.v1.views.analytics_enrollments import (
15
15
  AdvanceAnalyticsEnrollmentStatsView,
16
16
  AdvanceAnalyticsIndividualEnrollmentsView,
17
17
  )
18
+ from enterprise_data.api.v1.views.analytics_leaderboard import AdvanceAnalyticsLeaderboardView
18
19
  from enterprise_data.constants import UUID4_REGEX
19
20
 
20
21
  app_name = 'enterprise_data_api_v1'
@@ -53,32 +54,37 @@ urlpatterns = [
53
54
  name='enterprise-admin-insights'
54
55
  ),
55
56
  re_path(
56
- fr'^admin/anlaytics/(?P<enterprise_id>{UUID4_REGEX})$',
57
+ fr'^admin/analytics/(?P<enterprise_id>{UUID4_REGEX})$',
57
58
  enterprise_admin_views.EnterpriseAdminAnalyticsAggregatesView.as_view(),
58
59
  name='enterprise-admin-analytics-aggregates'
59
60
  ),
60
61
  re_path(
61
- fr'^admin/anlaytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments/stats$',
62
+ fr'^admin/analytics/(?P<enterprise_uuid>{UUID4_REGEX})/leaderboard$',
63
+ AdvanceAnalyticsLeaderboardView.as_view(),
64
+ name='enterprise-admin-analytics-leaderboard'
65
+ ),
66
+ re_path(
67
+ fr'^admin/analytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments/stats$',
62
68
  AdvanceAnalyticsEnrollmentStatsView.as_view(),
63
69
  name='enterprise-admin-analytics-enrollments-stats'
64
70
  ),
65
71
  re_path(
66
- fr'^admin/anlaytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments$',
72
+ fr'^admin/analytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments$',
67
73
  AdvanceAnalyticsIndividualEnrollmentsView.as_view(),
68
74
  name='enterprise-admin-analytics-enrollments'
69
75
  ),
70
76
  re_path(
71
- fr'^admin/anlaytics/(?P<enterprise_id>{UUID4_REGEX})/skills/stats',
77
+ fr'^admin/analytics/(?P<enterprise_id>{UUID4_REGEX})/skills/stats',
72
78
  enterprise_admin_views.EnterpriseAdminAnalyticsSkillsView.as_view(),
73
79
  name='enterprise-admin-analytics-skills'
74
80
  ),
75
81
  re_path(
76
- fr'^admin/anlaytics/(?P<enterprise_id>{UUID4_REGEX})/completions/stats$',
82
+ fr'^admin/analytics/(?P<enterprise_id>{UUID4_REGEX})/completions/stats$',
77
83
  enterprise_completions_views.EnterrpiseAdminCompletionsStatsView.as_view(),
78
84
  name='enterprise-admin-analytics-completions-stats'
79
85
  ),
80
86
  re_path(
81
- fr'^admin/anlaytics/(?P<enterprise_id>{UUID4_REGEX})/completions$',
87
+ fr'^admin/analytics/(?P<enterprise_id>{UUID4_REGEX})/completions$',
82
88
  enterprise_completions_views.EnterrpiseAdminCompletionsView.as_view(),
83
89
  name='enterprise-admin-analytics-completions'
84
90
  ),