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.
- {edx_enterprise_data-8.11.0.dist-info → edx_enterprise_data-8.12.0.dist-info}/METADATA +1 -1
- {edx_enterprise_data-8.11.0.dist-info → edx_enterprise_data-8.12.0.dist-info}/RECORD +16 -16
- enterprise_data/__init__.py +1 -1
- enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py +78 -0
- enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py +128 -3
- enterprise_data/admin_analytics/database/utils.py +8 -3
- enterprise_data/api/v1/serializers.py +2 -0
- enterprise_data/api/v1/urls.py +3 -6
- enterprise_data/api/v1/views/analytics_enrollments.py +90 -332
- enterprise_data/api/v1/views/base.py +79 -0
- enterprise_data/api/v1/views/enterprise_admin.py +5 -3
- enterprise_data/tests/admin_analytics/mock_analytics_data.py +0 -12
- enterprise_data/tests/admin_analytics/test_analytics_enrollments.py +72 -115
- {edx_enterprise_data-8.11.0.dist-info → edx_enterprise_data-8.12.0.dist-info}/LICENSE +0 -0
- {edx_enterprise_data-8.11.0.dist-info → edx_enterprise_data-8.12.0.dist-info}/WHEEL +0 -0
- {edx_enterprise_data-8.11.0.dist-info → edx_enterprise_data-8.12.0.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,4 @@
|
|
1
1
|
"""Unittests for analytics_enrollments.py"""
|
2
|
-
|
3
2
|
from datetime import datetime
|
4
3
|
|
5
4
|
import ddt
|
@@ -8,15 +7,10 @@ from rest_framework import status
|
|
8
7
|
from rest_framework.reverse import reverse
|
9
8
|
from rest_framework.test import APITransactionTestCase
|
10
9
|
|
11
|
-
from enterprise_data.admin_analytics.constants import
|
10
|
+
from enterprise_data.admin_analytics.constants import ResponseType
|
12
11
|
from enterprise_data.api.v1.serializers import AdvanceAnalyticsEnrollmentStatsSerializer as EnrollmentStatsSerializer
|
13
12
|
from enterprise_data.api.v1.serializers import AdvanceAnalyticsQueryParamSerializer
|
14
|
-
from enterprise_data.tests.admin_analytics.mock_analytics_data import
|
15
|
-
ENROLLMENT_STATS_CSVS,
|
16
|
-
ENROLLMENTS,
|
17
|
-
enrollments_csv_content,
|
18
|
-
enrollments_dataframe,
|
19
|
-
)
|
13
|
+
from enterprise_data.tests.admin_analytics.mock_analytics_data import ENROLLMENTS
|
20
14
|
from enterprise_data.tests.mixins import JWTTestMixin
|
21
15
|
from enterprise_data.tests.test_utils import UserFactory
|
22
16
|
from enterprise_data_roles.constants import ENTERPRISE_DATA_ADMIN_ROLE
|
@@ -59,8 +53,8 @@ class TestIndividualEnrollmentsAPI(JWTTestMixin, APITransactionTestCase):
|
|
59
53
|
)
|
60
54
|
|
61
55
|
fetch_max_enrollment_datetime_patcher = patch(
|
62
|
-
'enterprise_data.api.v1.views.analytics_enrollments.
|
63
|
-
return_value=datetime.now()
|
56
|
+
'enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_enrollment_date_range',
|
57
|
+
return_value=(datetime.now(), datetime.now())
|
64
58
|
)
|
65
59
|
|
66
60
|
fetch_max_enrollment_datetime_patcher.start()
|
@@ -69,11 +63,11 @@ class TestIndividualEnrollmentsAPI(JWTTestMixin, APITransactionTestCase):
|
|
69
63
|
def verify_enrollment_data(self, results, results_count):
|
70
64
|
"""Verify the received enrollment data."""
|
71
65
|
attrs = [
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
66
|
+
'email',
|
67
|
+
'course_title',
|
68
|
+
'course_subject',
|
69
|
+
'enroll_type',
|
70
|
+
'enterprise_enrollment_date',
|
77
71
|
]
|
78
72
|
|
79
73
|
assert len(results) == results_count
|
@@ -89,16 +83,16 @@ class TestIndividualEnrollmentsAPI(JWTTestMixin, APITransactionTestCase):
|
|
89
83
|
expected_data = sorted(filtered_data, key=lambda x: x["email"])
|
90
84
|
assert received_data == expected_data
|
91
85
|
|
92
|
-
@patch(
|
93
|
-
|
94
|
-
)
|
95
|
-
def test_get(self, mock_fetch_and_cache_enrollments_data):
|
86
|
+
@patch('enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_enrollment_count')
|
87
|
+
@patch('enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_all_enrollments')
|
88
|
+
def test_get(self, mock_get_all_enrollments, mock_get_enrollment_count):
|
96
89
|
"""
|
97
90
|
Test the GET method for the AdvanceAnalyticsIndividualEnrollmentsView works.
|
98
91
|
"""
|
99
|
-
|
92
|
+
mock_get_all_enrollments.return_value = ENROLLMENTS
|
93
|
+
mock_get_enrollment_count.return_value = len(ENROLLMENTS)
|
100
94
|
|
101
|
-
response = self.client.get(self.url
|
95
|
+
response = self.client.get(self.url + '?page_size=2')
|
102
96
|
assert response.status_code == status.HTTP_200_OK
|
103
97
|
assert response["Content-Type"] == "application/json"
|
104
98
|
data = response.json()
|
@@ -107,9 +101,8 @@ class TestIndividualEnrollmentsAPI(JWTTestMixin, APITransactionTestCase):
|
|
107
101
|
assert data["current_page"] == 1
|
108
102
|
assert data["num_pages"] == 3
|
109
103
|
assert data["count"] == 5
|
110
|
-
self.verify_enrollment_data(data["results"], 2)
|
111
104
|
|
112
|
-
response = self.client.get(self.url
|
105
|
+
response = self.client.get(self.url + '?page_size=2&page=2')
|
113
106
|
assert response.status_code == status.HTTP_200_OK
|
114
107
|
assert response["Content-Type"] == "application/json"
|
115
108
|
data = response.json()
|
@@ -118,9 +111,8 @@ class TestIndividualEnrollmentsAPI(JWTTestMixin, APITransactionTestCase):
|
|
118
111
|
assert data["current_page"] == 2
|
119
112
|
assert data["num_pages"] == 3
|
120
113
|
assert data["count"] == 5
|
121
|
-
self.verify_enrollment_data(data["results"], 2)
|
122
114
|
|
123
|
-
response = self.client.get(self.url
|
115
|
+
response = self.client.get(self.url + '?page_size=2&page=3')
|
124
116
|
assert response.status_code == status.HTTP_200_OK
|
125
117
|
assert response["Content-Type"] == "application/json"
|
126
118
|
data = response.json()
|
@@ -129,9 +121,8 @@ class TestIndividualEnrollmentsAPI(JWTTestMixin, APITransactionTestCase):
|
|
129
121
|
assert data["current_page"] == 3
|
130
122
|
assert data["num_pages"] == 3
|
131
123
|
assert data["count"] == 5
|
132
|
-
self.verify_enrollment_data(data["results"], 1)
|
133
124
|
|
134
|
-
response = self.client.get(self.url
|
125
|
+
response = self.client.get(self.url + '?page_size=5')
|
135
126
|
assert response.status_code == status.HTTP_200_OK
|
136
127
|
assert response["Content-Type"] == "application/json"
|
137
128
|
data = response.json()
|
@@ -140,16 +131,15 @@ class TestIndividualEnrollmentsAPI(JWTTestMixin, APITransactionTestCase):
|
|
140
131
|
assert data["current_page"] == 1
|
141
132
|
assert data["num_pages"] == 1
|
142
133
|
assert data["count"] == 5
|
143
|
-
self.verify_enrollment_data(data["results"], 5)
|
144
134
|
|
145
|
-
@patch(
|
146
|
-
|
147
|
-
)
|
148
|
-
def test_get_csv(self, mock_fetch_and_cache_enrollments_data):
|
135
|
+
@patch('enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_enrollment_count')
|
136
|
+
@patch('enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_all_enrollments')
|
137
|
+
def test_get_csv(self, mock_get_all_enrollments, mock_get_enrollment_count):
|
149
138
|
"""
|
150
139
|
Test the GET method for the AdvanceAnalyticsIndividualEnrollmentsView return correct CSV data.
|
151
140
|
"""
|
152
|
-
|
141
|
+
mock_get_all_enrollments.return_value = ENROLLMENTS
|
142
|
+
mock_get_enrollment_count.return_value = len(ENROLLMENTS)
|
153
143
|
response = self.client.get(self.url, {"response_type": ResponseType.CSV.value})
|
154
144
|
assert response.status_code == status.HTTP_200_OK
|
155
145
|
|
@@ -157,8 +147,33 @@ class TestIndividualEnrollmentsAPI(JWTTestMixin, APITransactionTestCase):
|
|
157
147
|
assert response["Content-Type"] == "text/csv"
|
158
148
|
|
159
149
|
# verify the response content
|
160
|
-
content = b"".join(response.streaming_content)
|
161
|
-
assert content ==
|
150
|
+
content = b"".join(response.streaming_content).decode().splitlines()
|
151
|
+
assert len(content) == 6
|
152
|
+
|
153
|
+
# Verify CSV header.
|
154
|
+
assert 'email,course_title,course_subject,enroll_type,enterprise_enrollment_date' == content[0]
|
155
|
+
|
156
|
+
# verify the content
|
157
|
+
assert (
|
158
|
+
'rebeccanelson@example.com,Re-engineered tangible approach,business-management,certificate,2021-07-04'
|
159
|
+
in content
|
160
|
+
)
|
161
|
+
assert (
|
162
|
+
'taylorjames@example.com,Re-engineered tangible approach,business-management,certificate,2021-07-03'
|
163
|
+
in content
|
164
|
+
)
|
165
|
+
assert (
|
166
|
+
'ssmith@example.com,Secured static capability,medicine,certificate,2021-05-11'
|
167
|
+
in content
|
168
|
+
)
|
169
|
+
assert (
|
170
|
+
'amber79@example.com,Streamlined zero-defect attitude,communication,certificate,2020-04-08'
|
171
|
+
in content
|
172
|
+
)
|
173
|
+
assert (
|
174
|
+
'kathleenmartin@example.com,Horizontal solution-oriented hub,social-sciences,certificate,2020-04-03'
|
175
|
+
in content
|
176
|
+
)
|
162
177
|
|
163
178
|
@ddt.data(
|
164
179
|
{
|
@@ -232,8 +247,8 @@ class TestEnrollmentStatsAPI(JWTTestMixin, APITransactionTestCase):
|
|
232
247
|
)
|
233
248
|
|
234
249
|
fetch_max_enrollment_datetime_patcher = patch(
|
235
|
-
'enterprise_data.api.v1.views.analytics_enrollments.
|
236
|
-
return_value=datetime.now()
|
250
|
+
'enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_enrollment_date_range',
|
251
|
+
return_value=(datetime.now(), datetime.now())
|
237
252
|
)
|
238
253
|
|
239
254
|
fetch_max_enrollment_datetime_patcher.start()
|
@@ -261,93 +276,35 @@ class TestEnrollmentStatsAPI(JWTTestMixin, APITransactionTestCase):
|
|
261
276
|
assert received_data == expected_data
|
262
277
|
|
263
278
|
@patch(
|
264
|
-
|
279
|
+
'enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.'
|
280
|
+
'get_top_subjects_by_enrollments'
|
281
|
+
)
|
282
|
+
@patch(
|
283
|
+
'enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_top_courses_by_enrollments'
|
284
|
+
)
|
285
|
+
@patch(
|
286
|
+
'enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_enrolment_time_series_data'
|
265
287
|
)
|
266
|
-
def test_get(
|
288
|
+
def test_get(
|
289
|
+
self,
|
290
|
+
mock_get_enrolment_time_series_data,
|
291
|
+
mock_get_top_courses_by_enrollments,
|
292
|
+
mock_get_top_subjects_by_enrollments,
|
293
|
+
):
|
267
294
|
"""
|
268
295
|
Test the GET method for the AdvanceAnalyticsEnrollmentStatsView works.
|
269
296
|
"""
|
270
|
-
|
297
|
+
mock_get_enrolment_time_series_data.return_value = []
|
298
|
+
mock_get_top_courses_by_enrollments.return_value = []
|
299
|
+
mock_get_top_subjects_by_enrollments.return_value = []
|
271
300
|
|
272
301
|
response = self.client.get(self.url)
|
273
302
|
assert response.status_code == status.HTTP_200_OK
|
274
303
|
assert response["Content-Type"] == "application/json"
|
275
304
|
data = response.json()
|
276
|
-
assert
|
277
|
-
|
278
|
-
|
279
|
-
"enterprise_enrollment_date": "2020-04-03T00:00:00",
|
280
|
-
"enroll_type": "certificate",
|
281
|
-
"count": 1,
|
282
|
-
},
|
283
|
-
{
|
284
|
-
"enterprise_enrollment_date": "2020-04-08T00:00:00",
|
285
|
-
"enroll_type": "certificate",
|
286
|
-
"count": 1,
|
287
|
-
},
|
288
|
-
{
|
289
|
-
"enterprise_enrollment_date": "2021-05-11T00:00:00",
|
290
|
-
"enroll_type": "certificate",
|
291
|
-
"count": 1,
|
292
|
-
},
|
293
|
-
{
|
294
|
-
"enterprise_enrollment_date": "2021-07-03T00:00:00",
|
295
|
-
"enroll_type": "certificate",
|
296
|
-
"count": 1,
|
297
|
-
},
|
298
|
-
{
|
299
|
-
"enterprise_enrollment_date": "2021-07-04T00:00:00",
|
300
|
-
"enroll_type": "certificate",
|
301
|
-
"count": 1,
|
302
|
-
},
|
303
|
-
],
|
304
|
-
"top_courses_by_enrollments": [
|
305
|
-
{"course_key": "NOGk+UVD31", "enroll_type": "certificate", "count": 1},
|
306
|
-
{"course_key": "QWXx+Jqz64", "enroll_type": "certificate", "count": 1},
|
307
|
-
{"course_key": "hEmW+tvk03", "enroll_type": "certificate", "count": 2},
|
308
|
-
{"course_key": "qZJC+KFX86", "enroll_type": "certificate", "count": 1},
|
309
|
-
],
|
310
|
-
"top_subjects_by_enrollments": [
|
311
|
-
{
|
312
|
-
"course_subject": "business-management",
|
313
|
-
"enroll_type": "certificate",
|
314
|
-
"count": 2,
|
315
|
-
},
|
316
|
-
{
|
317
|
-
"course_subject": "communication",
|
318
|
-
"enroll_type": "certificate",
|
319
|
-
"count": 1,
|
320
|
-
},
|
321
|
-
{
|
322
|
-
"course_subject": "medicine",
|
323
|
-
"enroll_type": "certificate",
|
324
|
-
"count": 1,
|
325
|
-
},
|
326
|
-
{
|
327
|
-
"course_subject": "social-sciences",
|
328
|
-
"enroll_type": "certificate",
|
329
|
-
"count": 1,
|
330
|
-
},
|
331
|
-
],
|
332
|
-
}
|
333
|
-
|
334
|
-
@patch("enterprise_data.api.v1.views.analytics_enrollments.fetch_and_cache_enrollments_data")
|
335
|
-
@ddt.data(
|
336
|
-
EnrollmentChart.ENROLLMENTS_OVER_TIME.value,
|
337
|
-
EnrollmentChart.TOP_COURSES_BY_ENROLLMENTS.value,
|
338
|
-
EnrollmentChart.TOP_SUBJECTS_BY_ENROLLMENTS.value,
|
339
|
-
)
|
340
|
-
def test_get_csv(self, chart_type, mock_fetch_and_cache_enrollments_data):
|
341
|
-
"""
|
342
|
-
Test that AdvanceAnalyticsEnrollmentStatsView return correct CSV data.
|
343
|
-
"""
|
344
|
-
mock_fetch_and_cache_enrollments_data.return_value = enrollments_dataframe()
|
345
|
-
|
346
|
-
response = self.client.get(self.url, {"response_type": ResponseType.CSV.value, "chart_type": chart_type})
|
347
|
-
assert response.status_code == status.HTTP_200_OK
|
348
|
-
assert response["Content-Type"] == "text/csv"
|
349
|
-
# verify the response content
|
350
|
-
assert response.content == ENROLLMENT_STATS_CSVS[chart_type]
|
305
|
+
assert 'enrollments_over_time' in data
|
306
|
+
assert 'top_courses_by_enrollments' in data
|
307
|
+
assert 'top_subjects_by_enrollments' in data
|
351
308
|
|
352
309
|
@ddt.data(
|
353
310
|
{
|
File without changes
|
File without changes
|
File without changes
|