edx-enterprise-data 8.11.0__py3-none-any.whl → 8.12.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edx-enterprise-data
3
- Version: 8.11.0
3
+ Version: 8.12.0
4
4
  Summary: Enterprise Reporting
5
5
  Home-page: https://github.com/openedx/edx-enterprise-data
6
6
  Author: edX
@@ -1,4 +1,4 @@
1
- enterprise_data/__init__.py,sha256=43cEhgzFHOpsMk6l_FF2WJGDjIlxTPg6xfJU8UsyHrA,124
1
+ enterprise_data/__init__.py,sha256=6EwDYR1Ig68WDaZhWEMjK_4a3LbkjyRay9rmzaGXfPE,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
@@ -15,14 +15,14 @@ enterprise_data/admin_analytics/constants.py,sha256=7WturLuMISekgcHHlgj45PPdPrDT
15
15
  enterprise_data/admin_analytics/data_loaders.py,sha256=4PSsIveNBtpFqwrLUjTl5oywHVYFAMdZrNRHRGMk4XY,6116
16
16
  enterprise_data/admin_analytics/utils.py,sha256=CQuTlg36AALJiopp4us-JN8oTXsw-jDXSJenbphLDME,12270
17
17
  enterprise_data/admin_analytics/database/__init__.py,sha256=vNSWKf2VV5xMegN7htJJtxtQEb0ASLC6frE2w0ZpYpE,104
18
- enterprise_data/admin_analytics/database/utils.py,sha256=Zl-W_w5SVb-QpIwWxbHr0w7z4L2ZFeruJSygWh8g7II,1408
18
+ enterprise_data/admin_analytics/database/utils.py,sha256=5u-d6ZQW95mF_r4bH8Xdi7DgpYAuDFOG_q0P-bjKXHU,1712
19
19
  enterprise_data/admin_analytics/database/queries/__init__.py,sha256=IC5TLOr_GnydbrVbl2mWhwO3aUbYeHuDmfPTLmwGhZA,218
20
20
  enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py,sha256=fq01Ni_sKnvSRoiPQfTnJ8TtRePQe5MBLmI5CpVy36o,747
21
- enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py,sha256=X1muOEhEvqD-tyhrVEQkPrf8_7g7SPaJuMwriUPo3jw,2062
21
+ enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py,sha256=ofV4uYgMC_quHxDFPV_3UTeKffjgcm6ri68VSVelKe4,5425
22
22
  enterprise_data/admin_analytics/database/tables/__init__.py,sha256=3ECKlKlz1wuTER5M57CMFvmLFp0oCnVQXAUeTN96Bs0,213
23
23
  enterprise_data/admin_analytics/database/tables/base.py,sha256=1KyKsC18pW3m-5U-T6pdt5rIwsz6Wp3QFFbD3r6L6YQ,395
24
24
  enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py,sha256=EsG8KgRW84wtA_nJuUknjLYlDtaPSJf_9mWdkO2Bj2I,1293
25
- enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py,sha256=SLnhWEfbQfeaUWT1a7H5ZCTq3XMMrzO_6iG-g6kMry4,2571
25
+ enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py,sha256=ix5QvPnrUZMVs_Fdt742i9PAmrQTXuqHlfW3PJhSQWo,7282
26
26
  enterprise_data/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  enterprise_data/api/urls.py,sha256=POqc_KATHdnpMf9zHtpO46pKD5KAlAExtx7G6iylLcU,273
28
28
  enterprise_data/api/v0/__init__.py,sha256=1aAzAYU5hk-RW6cKUxa1645cbZMxn7GIZ7OMjWc9MKI,46
@@ -31,14 +31,14 @@ enterprise_data/api/v0/urls.py,sha256=vzJjqIo_S3AXWs9Us8XTaJc3FnxLbYzAkmLyuDQqum
31
31
  enterprise_data/api/v0/views.py,sha256=4RslZ4NZOU-844bnebEQ71ji2utRY7jEijqC45oQQD0,14380
32
32
  enterprise_data/api/v1/__init__.py,sha256=1aAzAYU5hk-RW6cKUxa1645cbZMxn7GIZ7OMjWc9MKI,46
33
33
  enterprise_data/api/v1/paginators.py,sha256=f0xsilLaU94jSBltJk46tR1rLEIt7YrqSzMAAVtPXjA,3592
34
- enterprise_data/api/v1/serializers.py,sha256=Kk4zuRNcr4bfMYIwi2iXQAr_NM3OXuXNfxfrz7xO2iE,13194
35
- enterprise_data/api/v1/urls.py,sha256=nFcbPSfAIKW6Eiwd1vN0WMqdtBfh9_bBqAFCVrzRm5I,4091
34
+ enterprise_data/api/v1/serializers.py,sha256=FEuH-_iTlKFJoxM8ABzEYHDAWHsgSYfdQd2WSPdXoeE,13329
35
+ enterprise_data/api/v1/urls.py,sha256=JzaaVsHEAELMLFdhR60_8LRLb9klDeA4zdH1dLxnQEo,4048
36
36
  enterprise_data/api/v1/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
37
  enterprise_data/api/v1/views/analytics_engagements.py,sha256=8H3Fk-hTqJaU3H5Lpu1kFWq7p7xDp1pjge-sK7osDbg,17553
38
- enterprise_data/api/v1/views/analytics_enrollments.py,sha256=trC92GOej6vUoju53KT3dPEDnoPHdukfXEIXPThz76A,16886
38
+ enterprise_data/api/v1/views/analytics_enrollments.py,sha256=nT3eu336mtjZuAXYRGhugxAY_G3BWPkyf0TtybRe_HM,6249
39
39
  enterprise_data/api/v1/views/analytics_leaderboard.py,sha256=2DALqzUIbe4-ZGgHHIkYAKJ5L1ik2ruPtQNYtTdPba4,5974
40
- enterprise_data/api/v1/views/base.py,sha256=FTAxlz5EzvAY657wzVgzhJPFSCHHzct7IDcvm71Smt8,866
41
- enterprise_data/api/v1/views/enterprise_admin.py,sha256=_HfQ46bIC6uU3knCWhGLnsWf_YGtsjYHU5UrwG6sdfA,8568
40
+ enterprise_data/api/v1/views/base.py,sha256=5fXUH3Q3J6t_GJ-Hb3TFOAC_BUED9SiCk-eGzvNqDRE,3395
41
+ enterprise_data/api/v1/views/enterprise_admin.py,sha256=d79WkyBbqLk6pKGWWrnbYdrbOgQztP03Q7NKRA5tZB4,8639
42
42
  enterprise_data/api/v1/views/enterprise_completions.py,sha256=bJG2ZtTbLyiBrj64iJHQNHEKLrJCzl9OuJ7nDtw-9aY,8377
43
43
  enterprise_data/api/v1/views/enterprise_learner.py,sha256=yABjJje3CT8I8YOhWr1_tTkdKtnGJom8eu3EFz_-0BU,18517
44
44
  enterprise_data/api/v1/views/enterprise_offers.py,sha256=VifxgqTLFLVw4extYPlHcN1N_yjXcsYsAlYEnAbpb10,1266
@@ -113,10 +113,10 @@ enterprise_data/tests/test_models.py,sha256=MWBY-LY5TPBjZ4GlvpM-h4W-BvRKr2Rml8Bz
113
113
  enterprise_data/tests/test_utils.py,sha256=vbmYM7DMN-lHS2p4yaa0Yd6uSGXd2qoZRDE9X3J4Sec,18385
114
114
  enterprise_data/tests/test_views.py,sha256=UvDRNTxruy5zBK_KgUy2cBMbwlaTW_vkM0-TCXbQZiY,69667
115
115
  enterprise_data/tests/admin_analytics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
- enterprise_data/tests/admin_analytics/mock_analytics_data.py,sha256=LoYAh5cn_4rv2stGcMP5PELv2l7Eu2DTKG-v7viNTCs,20266
116
+ enterprise_data/tests/admin_analytics/mock_analytics_data.py,sha256=VZHhMKSQn5S-o1wNReSVyEnI_-6EkW-TUV5EvXCl5is,19536
117
117
  enterprise_data/tests/admin_analytics/mock_enrollments.py,sha256=LfuMo9Kn-OQD4z42G3BRuM5MXUUXXlaAMhTqfJf46XE,7266
118
118
  enterprise_data/tests/admin_analytics/test_analytics_engagements.py,sha256=KPXtBPaAOrzfff7W-xERSGx9KtZAJndLbIJx3gopSnE,15689
119
- enterprise_data/tests/admin_analytics/test_analytics_enrollments.py,sha256=UdKRkP6BNbsSo-gm0YCoddT-ReUMI1x9E6HNLSHT7pY,15177
119
+ enterprise_data/tests/admin_analytics/test_analytics_enrollments.py,sha256=GePv5E8BRGRWUHUwGaXvYIsN3dtDpNXUh-yfW5iBTi4,13781
120
120
  enterprise_data/tests/admin_analytics/test_analytics_leaderboard.py,sha256=VSEyDAHfWBJvqmx9yzd4NnPAqK3TqaKrMBWswMAdzfU,6206
121
121
  enterprise_data/tests/admin_analytics/test_data_loaders.py,sha256=o3denJ4aUS1pI5Crksl4C6m-NtCBm8ynoHBnLkf-v2U,4641
122
122
  enterprise_data/tests/admin_analytics/test_enterprise_completions.py,sha256=afkHQFy4bvqZ0pq5Drl1t2nv8zxbgca2jzOQbihlPG0,7359
@@ -169,8 +169,8 @@ enterprise_reporting/tests/test_send_enterprise_reports.py,sha256=WtL-RqGgu2x5PP
169
169
  enterprise_reporting/tests/test_utils.py,sha256=Zt_TA0LVb-B6fQGkUkAKKVlUKKnQh8jnw1US1jKe7g8,9493
170
170
  enterprise_reporting/tests/test_vertica_client.py,sha256=-R2yNCGUjRtoXwLMBloVFQkFYrJoo613VCr61gwI3kQ,140
171
171
  enterprise_reporting/tests/utils.py,sha256=xms2LM7DV3wczXEfctOK1ddel1EE0J_YSr17UzbCDy4,1401
172
- edx_enterprise_data-8.11.0.dist-info/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
173
- edx_enterprise_data-8.11.0.dist-info/METADATA,sha256=21GMLdFJo9VqGsBOCJgld3rUyVhCwGTZPXEYjPb3Ou4,1570
174
- edx_enterprise_data-8.11.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
175
- edx_enterprise_data-8.11.0.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
176
- edx_enterprise_data-8.11.0.dist-info/RECORD,,
172
+ edx_enterprise_data-8.12.0.dist-info/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
173
+ edx_enterprise_data-8.12.0.dist-info/METADATA,sha256=ePpHvJ_L1ZArGfUBHXEcPExU5vTGx17l-HvK14USLIo,1570
174
+ edx_enterprise_data-8.12.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
175
+ edx_enterprise_data-8.12.0.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
176
+ edx_enterprise_data-8.12.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__ = "8.11.0"
5
+ __version__ = "8.12.0"
@@ -7,6 +7,30 @@ class FactEnrollmentAdminDashQueries:
7
7
  """
8
8
  Queries related to the fact_enrollment_admin_dash table.
9
9
  """
10
+ @staticmethod
11
+ def get_enrollment_count_query():
12
+ """
13
+ Get the query to fetch the total number of enrollments for an enterprise customer.
14
+ """
15
+ return """
16
+ SELECT count(*)
17
+ FROM fact_enrollment_admin_dash
18
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
19
+ enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s;
20
+ """
21
+
22
+ @staticmethod
23
+ def get_all_enrollments_query():
24
+ """
25
+ Get the query to fetch all enrollments.
26
+ """
27
+ return """
28
+ SELECT email, course_title, course_subject, enroll_type, enterprise_enrollment_date
29
+ FROM fact_enrollment_admin_dash
30
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
31
+ enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s
32
+ ORDER BY ENTERPRISE_ENROLLMENT_DATE DESC LIMIT %(limit)s OFFSET %(offset)s
33
+ """
10
34
 
11
35
  @staticmethod
12
36
  def get_enrollment_date_range_query():
@@ -59,3 +83,57 @@ class FactEnrollmentAdminDashQueries:
59
83
  WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
60
84
  activity_date BETWEEN %(start_date)s AND %(end_date)s;
61
85
  """
86
+
87
+ @staticmethod
88
+ def get_top_courses_enrollments_query(record_count=20):
89
+ """
90
+ Get the query to fetch the enrollment count by courses.
91
+
92
+ Query will fetch the top N courses by enrollment count. Where N is the value of record_count.
93
+
94
+ Arguments:
95
+ record_count (int): Number of records to fetch.
96
+ """
97
+ # Some local environments raise error when course_title is added in SELECT without GROUP BY.
98
+ # If you face this issue, you can remove course_title from SELECT.
99
+
100
+ # TODO: Re-add course_title after testing.
101
+ return f"""
102
+ SELECT course_key, enroll_type, count(course_key) as enrollment_count
103
+ FROM fact_enrollment_admin_dash
104
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
105
+ enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s
106
+ GROUP BY course_key, enroll_type ORDER BY enrollment_count DESC LIMIT {record_count};
107
+ """
108
+
109
+ @staticmethod
110
+ def get_top_subjects_by_enrollments_query(record_count=20):
111
+ """
112
+ Get the query to fetch the enrollment count by subjects.
113
+
114
+ Query will fetch the top N subjects by enrollment count. Where N is the value of record_count.
115
+
116
+ Arguments:
117
+ record_count (int): Number of records to fetch.
118
+ """
119
+ return f"""
120
+ SELECT course_subject, enroll_type, count(course_subject) enrollment_count
121
+ FROM fact_enrollment_admin_dash
122
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
123
+ enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s
124
+ GROUP BY course_subject, enroll_type ORDER BY enrollment_count DESC LIMIT {record_count};
125
+ """
126
+
127
+ @staticmethod
128
+ def get_enrolment_time_series_data_query():
129
+ """
130
+ Get the query to fetch the enrollment time series data with daily aggregation.
131
+ """
132
+ return """
133
+ SELECT enterprise_enrollment_date, enroll_type, COUNT(*) as enrollment_count
134
+ FROM fact_enrollment_admin_dash
135
+ WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
136
+ enterprise_enrollment_date BETWEEN %(start_date)s AND %(end_date)s
137
+ GROUP BY enterprise_enrollment_date, enroll_type
138
+ ORDER BY enterprise_enrollment_date;
139
+ """
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Module for interacting with the fact_enrollment_admin_dash table.
3
3
  """
4
- from datetime import date
4
+ from datetime import date, datetime
5
5
  from uuid import UUID
6
6
 
7
7
  from ..queries import FactEnrollmentAdminDashQueries
@@ -15,6 +15,56 @@ class FactEnrollmentAdminDashTable(BaseTable):
15
15
  """
16
16
  queries = FactEnrollmentAdminDashQueries()
17
17
 
18
+ def get_enrollment_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
19
+ """
20
+ Get the total number of enrollments for the given enterprise customer.
21
+
22
+ Arguments:
23
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
24
+ start_date (date): The start date.
25
+ end_date (date): The end date.
26
+
27
+ Returns:
28
+ (int): The total number of enrollments.
29
+ """
30
+ results = run_query(
31
+ query=self.queries.get_enrollment_count_query(),
32
+ params={
33
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
34
+ 'start_date': start_date,
35
+ 'end_date': end_date,
36
+ }
37
+ )
38
+ return results[0][0]
39
+
40
+ def get_all_enrollments(
41
+ self, enterprise_customer_uuid: UUID, start_date: date, end_date: date, limit: int, offset: int
42
+ ):
43
+ """
44
+ Get all enrollments for the given enterprise customer.
45
+
46
+ Arguments:
47
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
48
+ start_date (date): The start date.
49
+ end_date (date): The end date.
50
+ limit (int): The maximum number of records to return.
51
+ offset (int): The number of records to skip.
52
+
53
+ Returns:
54
+ list<dict>: A list of dictionaries containing the enrollment data.
55
+ """
56
+ return run_query(
57
+ query=self.queries.get_all_enrollments_query(),
58
+ params={
59
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
60
+ 'start_date': start_date,
61
+ 'end_date': end_date,
62
+ 'limit': limit,
63
+ 'offset': offset,
64
+ },
65
+ as_dict=True,
66
+ )
67
+
18
68
  def get_enrollment_date_range(self, enterprise_customer_uuid: UUID):
19
69
  """
20
70
  Get the enrollment date range for the given enterprise customer.
@@ -23,13 +73,22 @@ class FactEnrollmentAdminDashTable(BaseTable):
23
73
  enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
24
74
 
25
75
  Returns:
26
- (tuple<int, int>): The minimum and maximum enrollment dates.
76
+ (tuple<date, date>): The minimum and maximum enrollment dates.
27
77
  """
28
78
  results = run_query(
29
79
  query=self.queries.get_enrollment_date_range_query(),
30
80
  params={'enterprise_customer_uuid': enterprise_customer_uuid}
31
81
  )
32
- return tuple(results[0])
82
+ min_date, max_date = results[0]
83
+
84
+ # We should return date objects, not datetime objects
85
+ # This is done to counter cases where database values are datetime objects.
86
+ if min_date and isinstance(min_date, datetime):
87
+ min_date = min_date.date()
88
+ if max_date and isinstance(max_date, datetime):
89
+ max_date = max_date.date()
90
+
91
+ return min_date, max_date
33
92
 
34
93
  def get_enrollment_and_course_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
35
94
  """
@@ -74,3 +133,69 @@ class FactEnrollmentAdminDashTable(BaseTable):
74
133
  }
75
134
  )
76
135
  return results[0][0]
136
+
137
+ def get_top_courses_by_enrollments(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
138
+ """
139
+ Get the top courses enrollments for the given enterprise customer.
140
+
141
+ Arguments:
142
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
143
+ start_date (date): The start date.
144
+ end_date (date): The end date.
145
+
146
+ Returns:
147
+ list<dict>: A list of dictionaries containing the course key, course_title and enrollment count.
148
+ """
149
+ return run_query(
150
+ query=self.queries.get_top_courses_enrollments_query(),
151
+ params={
152
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
153
+ 'start_date': start_date,
154
+ 'end_date': end_date,
155
+ },
156
+ as_dict=True,
157
+ )
158
+
159
+ def get_top_subjects_by_enrollments(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
160
+ """
161
+ Get the top subjects by enrollments for the given enterprise customer.
162
+
163
+ Arguments:
164
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
165
+ start_date (date): The start date.
166
+ end_date (date): The end date.
167
+
168
+ Returns:
169
+ list<dict>: A list of dictionaries containing the subject and enrollment count.
170
+ """
171
+ return run_query(
172
+ query=self.queries.get_top_subjects_by_enrollments_query(),
173
+ params={
174
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
175
+ 'start_date': start_date,
176
+ 'end_date': end_date,
177
+ },
178
+ as_dict=True,
179
+ )
180
+
181
+ def get_enrolment_time_series_data(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
182
+ """
183
+ Get the enrollment time series data for the given enterprise customer.
184
+
185
+ Arguments:
186
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
187
+ start_date (date): The start date.
188
+ end_date (date): The end date.
189
+
190
+ Returns:
191
+ list<dict>: A list of dictionaries containing the date and enrollment count.
192
+ """
193
+ return run_query(
194
+ query=self.queries.get_enrolment_time_series_data_query(),
195
+ params={
196
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
197
+ 'start_date': start_date,
198
+ 'end_date': end_date,
199
+ },
200
+ as_dict=True,
201
+ )
@@ -30,22 +30,27 @@ def get_db_connection(database=settings.ENTERPRISE_REPORTING_DB_ALIAS):
30
30
 
31
31
 
32
32
  @timeit
33
- def run_query(query, params: dict = None):
33
+ def run_query(query, params: dict = None, as_dict=False):
34
34
  """
35
35
  Run a query on the database and return the results.
36
36
 
37
37
  Arguments:
38
38
  query (str): The query to run.
39
39
  params (dict): The parameters to pass to the query.
40
+ as_dict (bool): When True, return the results as a dictionary.
40
41
 
41
42
  Returns:
42
- (list): The results of the query.
43
+ (list | dict): The results of the query.
43
44
  """
44
45
  try:
45
46
  with closing(get_db_connection()) as connection:
46
47
  with closing(connection.cursor()) as cursor:
47
48
  cursor.execute(query, params=params)
48
- return cursor.fetchall()
49
+ if as_dict:
50
+ columns = [column[0] for column in cursor.description]
51
+ return [dict(zip(columns, row)) for row in cursor.fetchall()]
52
+ else:
53
+ return cursor.fetchall()
49
54
  except Exception:
50
55
  LOGGER.exception(f'[run_query]: run_query failed for query "{query}".')
51
56
  raise
@@ -265,6 +265,8 @@ class AdvanceAnalyticsQueryParamSerializer(serializers.Serializer): # pylint: d
265
265
  granularity = serializers.CharField(required=False)
266
266
  calculation = serializers.CharField(required=False)
267
267
  response_type = serializers.CharField(required=False)
268
+ page = serializers.IntegerField(required=False, min_value=1)
269
+ page_size = serializers.IntegerField(required=False, min_value=2)
268
270
 
269
271
  def validate(self, attrs):
270
272
  """
@@ -15,10 +15,7 @@ from enterprise_data.api.v1.views.analytics_engagements import (
15
15
  AdvanceAnalyticsEngagementStatsView,
16
16
  AdvanceAnalyticsIndividualEngagementsView,
17
17
  )
18
- from enterprise_data.api.v1.views.analytics_enrollments import (
19
- AdvanceAnalyticsEnrollmentStatsView,
20
- AdvanceAnalyticsIndividualEnrollmentsView,
21
- )
18
+ from enterprise_data.api.v1.views.analytics_enrollments import AdvanceAnalyticsEnrollmentsView
22
19
  from enterprise_data.api.v1.views.analytics_leaderboard import AdvanceAnalyticsLeaderboardView
23
20
  from enterprise_data.constants import UUID4_REGEX
24
21
 
@@ -69,12 +66,12 @@ urlpatterns = [
69
66
  ),
70
67
  re_path(
71
68
  fr'^admin/analytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments/stats$',
72
- AdvanceAnalyticsEnrollmentStatsView.as_view(),
69
+ AdvanceAnalyticsEnrollmentsView.as_view({'get': 'stats'}),
73
70
  name='enterprise-admin-analytics-enrollments-stats'
74
71
  ),
75
72
  re_path(
76
73
  fr'^admin/analytics/(?P<enterprise_uuid>{UUID4_REGEX})/enrollments$',
77
- AdvanceAnalyticsIndividualEnrollmentsView.as_view(),
74
+ AdvanceAnalyticsEnrollmentsView.as_view({'get': 'list'}),
78
75
  name='enterprise-admin-analytics-enrollments'
79
76
  ),
80
77
  re_path(