edx-enterprise-data 8.9.0__py3-none-any.whl → 8.11.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.9.0.dist-info → edx_enterprise_data-8.11.0.dist-info}/METADATA +1 -1
- {edx_enterprise_data-8.9.0.dist-info → edx_enterprise_data-8.11.0.dist-info}/RECORD +27 -17
- enterprise_data/__init__.py +1 -1
- enterprise_data/admin_analytics/constants.py +8 -0
- enterprise_data/admin_analytics/database/__init__.py +4 -0
- enterprise_data/admin_analytics/database/queries/__init__.py +5 -0
- enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py +21 -0
- enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py +61 -0
- enterprise_data/admin_analytics/database/tables/__init__.py +5 -0
- enterprise_data/admin_analytics/database/tables/base.py +18 -0
- enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py +38 -0
- enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py +76 -0
- enterprise_data/admin_analytics/{database.py → database/utils.py} +3 -2
- enterprise_data/api/v1/serializers.py +31 -1
- enterprise_data/api/v1/urls.py +14 -0
- enterprise_data/api/v1/views/analytics_engagements.py +395 -0
- enterprise_data/api/v1/views/analytics_enrollments.py +1 -0
- enterprise_data/api/v1/views/analytics_leaderboard.py +4 -1
- enterprise_data/api/v1/views/enterprise_admin.py +15 -44
- enterprise_data/api/v1/views/enterprise_completions.py +2 -0
- enterprise_data/renderers.py +14 -0
- enterprise_data/tests/admin_analytics/mock_analytics_data.py +41 -1
- enterprise_data/tests/admin_analytics/test_analytics_engagements.py +390 -0
- enterprise_data/tests/api/v1/views/test_enterprise_admin.py +43 -20
- {edx_enterprise_data-8.9.0.dist-info → edx_enterprise_data-8.11.0.dist-info}/LICENSE +0 -0
- {edx_enterprise_data-8.9.0.dist-info → edx_enterprise_data-8.11.0.dist-info}/WHEEL +0 -0
- {edx_enterprise_data-8.9.0.dist-info → edx_enterprise_data-8.11.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,390 @@
|
|
1
|
+
"""Unittests for analytics_enrollments.py"""
|
2
|
+
|
3
|
+
from datetime import datetime
|
4
|
+
|
5
|
+
import ddt
|
6
|
+
from mock import patch
|
7
|
+
from rest_framework import status
|
8
|
+
from rest_framework.reverse import reverse
|
9
|
+
from rest_framework.test import APITransactionTestCase
|
10
|
+
|
11
|
+
from enterprise_data.admin_analytics.constants import EngagementChart, ResponseType
|
12
|
+
from enterprise_data.api.v1.serializers import AdvanceAnalyticsEngagementStatsSerializer as EngagementSerializer
|
13
|
+
from enterprise_data.tests.admin_analytics.mock_analytics_data import (
|
14
|
+
ENGAGEMENT_STATS_CSVS,
|
15
|
+
ENGAGEMENTS,
|
16
|
+
engagements_csv_content,
|
17
|
+
engagements_dataframe,
|
18
|
+
enrollments_dataframe,
|
19
|
+
)
|
20
|
+
from enterprise_data.tests.mixins import JWTTestMixin
|
21
|
+
from enterprise_data.tests.test_utils import UserFactory
|
22
|
+
from enterprise_data_roles.constants import ENTERPRISE_DATA_ADMIN_ROLE
|
23
|
+
from enterprise_data_roles.models import EnterpriseDataFeatureRole, EnterpriseDataRoleAssignment
|
24
|
+
|
25
|
+
INVALID_CALCULATION_ERROR = (
|
26
|
+
f"Calculation must be one of {EngagementSerializer.CALCULATION_CHOICES}"
|
27
|
+
)
|
28
|
+
INVALID_GRANULARITY_ERROR = (
|
29
|
+
f"Granularity must be one of {EngagementSerializer.GRANULARITY_CHOICES}"
|
30
|
+
)
|
31
|
+
INVALID_CSV_ERROR1 = f"chart_type must be one of {EngagementSerializer.CHART_TYPES}"
|
32
|
+
|
33
|
+
|
34
|
+
@ddt.ddt
|
35
|
+
class TestIndividualEngagementsAPI(JWTTestMixin, APITransactionTestCase):
|
36
|
+
"""Tests for AdvanceAnalyticsIndividualEngagementsView."""
|
37
|
+
|
38
|
+
def setUp(self):
|
39
|
+
"""
|
40
|
+
Setup method.
|
41
|
+
"""
|
42
|
+
super().setUp()
|
43
|
+
self.user = UserFactory(is_staff=True)
|
44
|
+
role, __ = EnterpriseDataFeatureRole.objects.get_or_create(
|
45
|
+
name=ENTERPRISE_DATA_ADMIN_ROLE
|
46
|
+
)
|
47
|
+
self.role_assignment = EnterpriseDataRoleAssignment.objects.create(
|
48
|
+
role=role, user=self.user
|
49
|
+
)
|
50
|
+
self.client.force_authenticate(user=self.user)
|
51
|
+
|
52
|
+
self.enterprise_uuid = "ee5e6b3a-069a-4947-bb8d-d2dbc323396c"
|
53
|
+
self.set_jwt_cookie()
|
54
|
+
|
55
|
+
self.url = reverse(
|
56
|
+
"v1:enterprise-admin-analytics-engagements",
|
57
|
+
kwargs={"enterprise_uuid": self.enterprise_uuid},
|
58
|
+
)
|
59
|
+
|
60
|
+
fetch_max_enrollment_datetime_patcher = patch(
|
61
|
+
'enterprise_data.admin_analytics.utils.fetch_max_enrollment_datetime',
|
62
|
+
return_value=datetime.now()
|
63
|
+
)
|
64
|
+
|
65
|
+
fetch_max_enrollment_datetime_patcher.start()
|
66
|
+
self.addCleanup(fetch_max_enrollment_datetime_patcher.stop)
|
67
|
+
|
68
|
+
def verify_engagement_data(self, results, results_count):
|
69
|
+
"""Verify the received engagement data."""
|
70
|
+
attrs = [
|
71
|
+
"email",
|
72
|
+
"course_title",
|
73
|
+
"activity_date",
|
74
|
+
"course_subject",
|
75
|
+
]
|
76
|
+
|
77
|
+
assert len(results) == results_count
|
78
|
+
|
79
|
+
filtered_data = []
|
80
|
+
for engagement in ENGAGEMENTS:
|
81
|
+
for result in results:
|
82
|
+
if engagement["email"] == result["email"]:
|
83
|
+
data = {attr: engagement[attr] for attr in attrs}
|
84
|
+
data["learning_time_hours"] = round(engagement["learning_time_seconds"] / 3600, 1)
|
85
|
+
filtered_data.append(data)
|
86
|
+
break
|
87
|
+
|
88
|
+
received_data = sorted(results, key=lambda x: x["email"])
|
89
|
+
expected_data = sorted(filtered_data, key=lambda x: x["email"])
|
90
|
+
assert received_data == expected_data
|
91
|
+
|
92
|
+
@patch(
|
93
|
+
"enterprise_data.api.v1.views.analytics_engagements.fetch_and_cache_enrollments_data"
|
94
|
+
)
|
95
|
+
@patch(
|
96
|
+
"enterprise_data.api.v1.views.analytics_engagements.fetch_and_cache_engagements_data"
|
97
|
+
)
|
98
|
+
def test_get(self, mock_fetch_and_cache_engagements_data, mock_fetch_and_cache_enrollments_data):
|
99
|
+
"""
|
100
|
+
Test the GET method for the AdvanceAnalyticsIndividualEngagementsView works.
|
101
|
+
"""
|
102
|
+
mock_fetch_and_cache_enrollments_data.return_value = enrollments_dataframe()
|
103
|
+
mock_fetch_and_cache_engagements_data.return_value = engagements_dataframe()
|
104
|
+
|
105
|
+
response = self.client.get(self.url, {"page_size": 2})
|
106
|
+
assert response.status_code == status.HTTP_200_OK
|
107
|
+
data = response.json()
|
108
|
+
assert data["next"] == f"http://testserver{self.url}?page=2&page_size=2"
|
109
|
+
assert data["previous"] is None
|
110
|
+
assert data["current_page"] == 1
|
111
|
+
assert data["num_pages"] == 5
|
112
|
+
assert data["count"] == 9
|
113
|
+
self.verify_engagement_data(data["results"], 2)
|
114
|
+
|
115
|
+
response = self.client.get(self.url, {"page_size": 2, "page": 2})
|
116
|
+
assert response.status_code == status.HTTP_200_OK
|
117
|
+
data = response.json()
|
118
|
+
assert data["next"] == f"http://testserver{self.url}?page=3&page_size=2"
|
119
|
+
assert data["previous"] == f"http://testserver{self.url}?page_size=2"
|
120
|
+
assert data["current_page"] == 2
|
121
|
+
assert data["num_pages"] == 5
|
122
|
+
assert data["count"] == 9
|
123
|
+
self.verify_engagement_data(data["results"], 2)
|
124
|
+
|
125
|
+
response = self.client.get(self.url, {"page_size": 2, "page": 5})
|
126
|
+
assert response.status_code == status.HTTP_200_OK
|
127
|
+
data = response.json()
|
128
|
+
assert data["next"] is None
|
129
|
+
assert data["previous"] == f"http://testserver{self.url}?page=4&page_size=2"
|
130
|
+
assert data["current_page"] == 5
|
131
|
+
assert data["num_pages"] == 5
|
132
|
+
assert data["count"] == 9
|
133
|
+
self.verify_engagement_data(data["results"], 1)
|
134
|
+
|
135
|
+
response = self.client.get(self.url, {"page_size": 9})
|
136
|
+
assert response.status_code == status.HTTP_200_OK
|
137
|
+
data = response.json()
|
138
|
+
assert data["next"] is None
|
139
|
+
assert data["previous"] is None
|
140
|
+
assert data["current_page"] == 1
|
141
|
+
assert data["num_pages"] == 1
|
142
|
+
assert data["count"] == 9
|
143
|
+
self.verify_engagement_data(data["results"], 9)
|
144
|
+
|
145
|
+
@patch(
|
146
|
+
"enterprise_data.api.v1.views.analytics_engagements.fetch_and_cache_enrollments_data"
|
147
|
+
)
|
148
|
+
@patch(
|
149
|
+
"enterprise_data.api.v1.views.analytics_engagements.fetch_and_cache_engagements_data"
|
150
|
+
)
|
151
|
+
def test_get_csv(self, mock_fetch_and_cache_engagements_data, mock_fetch_and_cache_enrollments_data):
|
152
|
+
"""
|
153
|
+
Test the GET method for the AdvanceAnalyticsIndividualEngagementsView return correct CSV data.
|
154
|
+
"""
|
155
|
+
mock_fetch_and_cache_enrollments_data.return_value = enrollments_dataframe()
|
156
|
+
mock_fetch_and_cache_engagements_data.return_value = engagements_dataframe()
|
157
|
+
start_date = enrollments_dataframe().enterprise_enrollment_date.min().strftime('%Y/%m/%d')
|
158
|
+
end_date = datetime.now().strftime('%Y/%m/%d')
|
159
|
+
response = self.client.get(self.url, {"response_type": ResponseType.CSV.value})
|
160
|
+
assert response.status_code == status.HTTP_200_OK
|
161
|
+
|
162
|
+
# verify the response headers
|
163
|
+
assert response["Content-Type"] == "text/csv"
|
164
|
+
filename = f"""Individual Engagements, {start_date} - {end_date}.csv"""
|
165
|
+
assert (
|
166
|
+
response["Content-Disposition"] == f'attachment; filename="{filename}"'
|
167
|
+
)
|
168
|
+
|
169
|
+
# verify the response content
|
170
|
+
content = b"".join(response.streaming_content)
|
171
|
+
assert content == engagements_csv_content()
|
172
|
+
|
173
|
+
@ddt.data(
|
174
|
+
{
|
175
|
+
"params": {"start_date": 1},
|
176
|
+
"error": {
|
177
|
+
"start_date": [
|
178
|
+
"Date has wrong format. Use one of these formats instead: YYYY-MM-DD."
|
179
|
+
]
|
180
|
+
},
|
181
|
+
},
|
182
|
+
{
|
183
|
+
"params": {"end_date": 2},
|
184
|
+
"error": {
|
185
|
+
"end_date": [
|
186
|
+
"Date has wrong format. Use one of these formats instead: YYYY-MM-DD."
|
187
|
+
]
|
188
|
+
},
|
189
|
+
},
|
190
|
+
{
|
191
|
+
"params": {"start_date": "2024-01-01", "end_date": "2023-01-01"},
|
192
|
+
"error": {
|
193
|
+
"non_field_errors": [
|
194
|
+
"start_date should be less than or equal to end_date."
|
195
|
+
]
|
196
|
+
},
|
197
|
+
},
|
198
|
+
{
|
199
|
+
"params": {"calculation": "invalid"},
|
200
|
+
"error": {"calculation": [INVALID_CALCULATION_ERROR]},
|
201
|
+
},
|
202
|
+
{
|
203
|
+
"params": {"granularity": "invalid"},
|
204
|
+
"error": {"granularity": [INVALID_GRANULARITY_ERROR]},
|
205
|
+
},
|
206
|
+
{"params": {"chart_type": "invalid"}, "error": {"chart_type": [INVALID_CSV_ERROR1]}},
|
207
|
+
)
|
208
|
+
@ddt.unpack
|
209
|
+
def test_get_invalid_query_params(self, params, error):
|
210
|
+
"""
|
211
|
+
Test the GET method return correct error if any query param value is incorrect.
|
212
|
+
"""
|
213
|
+
response = self.client.get(self.url, params)
|
214
|
+
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
215
|
+
assert response.json() == error
|
216
|
+
|
217
|
+
|
218
|
+
@ddt.ddt
|
219
|
+
class TestEngagementStatsAPI(JWTTestMixin, APITransactionTestCase):
|
220
|
+
"""Tests for AdvanceAnalyticsEngagementStatsView."""
|
221
|
+
|
222
|
+
def setUp(self):
|
223
|
+
"""
|
224
|
+
Setup method.
|
225
|
+
"""
|
226
|
+
super().setUp()
|
227
|
+
self.user = UserFactory(is_staff=True)
|
228
|
+
role, __ = EnterpriseDataFeatureRole.objects.get_or_create(
|
229
|
+
name=ENTERPRISE_DATA_ADMIN_ROLE
|
230
|
+
)
|
231
|
+
self.role_assignment = EnterpriseDataRoleAssignment.objects.create(
|
232
|
+
role=role, user=self.user
|
233
|
+
)
|
234
|
+
self.client.force_authenticate(user=self.user)
|
235
|
+
|
236
|
+
self.enterprise_uuid = "ee5e6b3a-069a-4947-bb8d-d2dbc323396c"
|
237
|
+
self.set_jwt_cookie()
|
238
|
+
|
239
|
+
self.url = reverse(
|
240
|
+
"v1:enterprise-admin-analytics-engagements-stats",
|
241
|
+
kwargs={"enterprise_uuid": self.enterprise_uuid},
|
242
|
+
)
|
243
|
+
|
244
|
+
fetch_max_enrollment_datetime_patcher = patch(
|
245
|
+
'enterprise_data.admin_analytics.utils.fetch_max_enrollment_datetime',
|
246
|
+
return_value=datetime.now()
|
247
|
+
)
|
248
|
+
|
249
|
+
fetch_max_enrollment_datetime_patcher.start()
|
250
|
+
self.addCleanup(fetch_max_enrollment_datetime_patcher.stop)
|
251
|
+
|
252
|
+
def verify_engagement_data(self, results):
|
253
|
+
"""Verify the received engagement data."""
|
254
|
+
attrs = [
|
255
|
+
"email",
|
256
|
+
"course_title",
|
257
|
+
"activity_date",
|
258
|
+
"course_subject",
|
259
|
+
]
|
260
|
+
|
261
|
+
filtered_data = []
|
262
|
+
for engagement in ENGAGEMENTS:
|
263
|
+
for result in results:
|
264
|
+
if engagement["email"] == result["email"]:
|
265
|
+
data = {attr: engagement[attr] for attr in attrs}
|
266
|
+
data["learning_time_hours"] = round(engagement["learning_time_seconds"] / 3600, 1)
|
267
|
+
filtered_data.append(data)
|
268
|
+
break
|
269
|
+
|
270
|
+
received_data = sorted(results, key=lambda x: x["email"])
|
271
|
+
expected_data = sorted(filtered_data, key=lambda x: x["email"])
|
272
|
+
assert received_data == expected_data
|
273
|
+
|
274
|
+
@patch(
|
275
|
+
"enterprise_data.api.v1.views.analytics_engagements.fetch_and_cache_enrollments_data"
|
276
|
+
)
|
277
|
+
@patch(
|
278
|
+
"enterprise_data.api.v1.views.analytics_engagements.fetch_and_cache_engagements_data"
|
279
|
+
)
|
280
|
+
def test_get(self, mock_fetch_and_cache_engagements_data, mock_fetch_and_cache_enrollments_data):
|
281
|
+
"""
|
282
|
+
Test the GET method for the AdvanceAnalyticsEnrollmentStatsView works.
|
283
|
+
"""
|
284
|
+
mock_fetch_and_cache_enrollments_data.return_value = enrollments_dataframe()
|
285
|
+
mock_fetch_and_cache_engagements_data.return_value = engagements_dataframe()
|
286
|
+
|
287
|
+
response = self.client.get(self.url)
|
288
|
+
assert response.status_code == status.HTTP_200_OK
|
289
|
+
data = response.json()
|
290
|
+
assert data == {
|
291
|
+
'engagements_over_time': [
|
292
|
+
{'activity_date': '2021-07-19T00:00:00', 'enroll_type': 'certificate', 'sum': 0.0},
|
293
|
+
{'activity_date': '2021-07-26T00:00:00', 'enroll_type': 'certificate', 'sum': 4.4},
|
294
|
+
{'activity_date': '2021-07-27T00:00:00', 'enroll_type': 'certificate', 'sum': 1.2},
|
295
|
+
{'activity_date': '2021-08-05T00:00:00', 'enroll_type': 'certificate', 'sum': 3.6},
|
296
|
+
{'activity_date': '2021-08-21T00:00:00', 'enroll_type': 'certificate', 'sum': 2.7},
|
297
|
+
{'activity_date': '2021-09-02T00:00:00', 'enroll_type': 'certificate', 'sum': 1.3},
|
298
|
+
{'activity_date': '2021-09-21T00:00:00', 'enroll_type': 'certificate', 'sum': 1.5},
|
299
|
+
{'activity_date': '2022-05-17T00:00:00', 'enroll_type': 'certificate', 'sum': 0.0}
|
300
|
+
],
|
301
|
+
'top_courses_by_engagement': [
|
302
|
+
{
|
303
|
+
'course_key': 'Kcpr+XoR30',
|
304
|
+
'course_title': 'Assimilated even-keeled focus group',
|
305
|
+
'enroll_type': 'certificate',
|
306
|
+
'count': 0.0
|
307
|
+
},
|
308
|
+
{
|
309
|
+
'course_key': 'luGg+KNt30',
|
310
|
+
'course_title': 'Synergized reciprocal encoding',
|
311
|
+
'enroll_type': 'certificate',
|
312
|
+
'count': 14.786944444444444
|
313
|
+
}
|
314
|
+
],
|
315
|
+
'top_subjects_by_engagement': [
|
316
|
+
{
|
317
|
+
'course_subject': 'business-management',
|
318
|
+
'enroll_type': 'certificate',
|
319
|
+
'count': 14.786944444444444
|
320
|
+
},
|
321
|
+
{
|
322
|
+
'course_subject': 'engineering',
|
323
|
+
'enroll_type': 'certificate',
|
324
|
+
'count': 0.0
|
325
|
+
}
|
326
|
+
]
|
327
|
+
}
|
328
|
+
|
329
|
+
@patch("enterprise_data.api.v1.views.analytics_engagements.fetch_and_cache_enrollments_data")
|
330
|
+
@patch("enterprise_data.api.v1.views.analytics_engagements.fetch_and_cache_engagements_data")
|
331
|
+
@ddt.data(
|
332
|
+
EngagementChart.ENGAGEMENTS_OVER_TIME.value,
|
333
|
+
EngagementChart.TOP_COURSES_BY_ENGAGEMENTS.value,
|
334
|
+
EngagementChart.TOP_SUBJECTS_BY_ENGAGEMENTS.value,
|
335
|
+
)
|
336
|
+
def test_get_csv(self, chart_type, mock_fetch_and_cache_engagements_data, mock_fetch_and_cache_enrollments_data):
|
337
|
+
"""
|
338
|
+
Test that AdvanceAnalyticsEngagementStatsView return correct CSV data.
|
339
|
+
"""
|
340
|
+
mock_fetch_and_cache_enrollments_data.return_value = enrollments_dataframe()
|
341
|
+
mock_fetch_and_cache_engagements_data.return_value = engagements_dataframe()
|
342
|
+
response = self.client.get(self.url, {"response_type": ResponseType.CSV.value, "chart_type": chart_type})
|
343
|
+
assert response.status_code == status.HTTP_200_OK
|
344
|
+
assert response["Content-Type"] == "text/csv"
|
345
|
+
# verify the response content
|
346
|
+
assert response.content == ENGAGEMENT_STATS_CSVS[chart_type]
|
347
|
+
|
348
|
+
@ddt.data(
|
349
|
+
{
|
350
|
+
"params": {"start_date": 1},
|
351
|
+
"error": {
|
352
|
+
"start_date": [
|
353
|
+
"Date has wrong format. Use one of these formats instead: YYYY-MM-DD."
|
354
|
+
]
|
355
|
+
},
|
356
|
+
},
|
357
|
+
{
|
358
|
+
"params": {"end_date": 2},
|
359
|
+
"error": {
|
360
|
+
"end_date": [
|
361
|
+
"Date has wrong format. Use one of these formats instead: YYYY-MM-DD."
|
362
|
+
]
|
363
|
+
},
|
364
|
+
},
|
365
|
+
{
|
366
|
+
"params": {"start_date": "2024-01-01", "end_date": "2023-01-01"},
|
367
|
+
"error": {
|
368
|
+
"non_field_errors": [
|
369
|
+
"start_date should be less than or equal to end_date."
|
370
|
+
]
|
371
|
+
},
|
372
|
+
},
|
373
|
+
{
|
374
|
+
"params": {"calculation": "invalid"},
|
375
|
+
"error": {"calculation": [INVALID_CALCULATION_ERROR]},
|
376
|
+
},
|
377
|
+
{
|
378
|
+
"params": {"granularity": "invalid"},
|
379
|
+
"error": {"granularity": [INVALID_GRANULARITY_ERROR]},
|
380
|
+
},
|
381
|
+
{"params": {"chart_type": "invalid"}, "error": {"chart_type": [INVALID_CSV_ERROR1]}},
|
382
|
+
)
|
383
|
+
@ddt.unpack
|
384
|
+
def test_get_invalid_query_params(self, params, error):
|
385
|
+
"""
|
386
|
+
Test the GET method return correct error if any query param value is incorrect.
|
387
|
+
"""
|
388
|
+
response = self.client.get(self.url, params)
|
389
|
+
assert response.status_code == status.HTTP_400_BAD_REQUEST
|
390
|
+
assert response.json() == error
|
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
Test cases for enterprise_admin views
|
3
3
|
"""
|
4
|
+
from datetime import datetime
|
4
5
|
from unittest import mock
|
5
6
|
from uuid import uuid4
|
6
7
|
|
@@ -11,10 +12,13 @@ from rest_framework import status
|
|
11
12
|
from rest_framework.reverse import reverse
|
12
13
|
from rest_framework.test import APITransactionTestCase
|
13
14
|
|
15
|
+
from enterprise_data.admin_analytics.database.queries import (
|
16
|
+
FactEngagementAdminDashQueries,
|
17
|
+
FactEnrollmentAdminDashQueries,
|
18
|
+
)
|
14
19
|
from enterprise_data.tests.mixins import JWTTestMixin
|
15
20
|
from enterprise_data.tests.test_utils import (
|
16
21
|
UserFactory,
|
17
|
-
get_dummy_engagements_data,
|
18
22
|
get_dummy_enrollments_data,
|
19
23
|
get_dummy_enterprise_api_data,
|
20
24
|
get_dummy_skills_data,
|
@@ -52,19 +56,30 @@ class TestEnterpriseAdminAnalyticsAggregatesView(JWTTestMixin, APITransactionTes
|
|
52
56
|
self.addCleanup(mocked_get_enterprise_customer.stop)
|
53
57
|
self.enterprise_id = 'ee5e6b3a-069a-4947-bb8d-d2dbc323396c'
|
54
58
|
self.set_jwt_cookie()
|
59
|
+
self.enrollment_queries = FactEnrollmentAdminDashQueries()
|
60
|
+
self.engagement_queries = FactEngagementAdminDashQueries()
|
55
61
|
|
56
|
-
def _mock_run_query(self, query):
|
62
|
+
def _mock_run_query(self, query, *args, **kwargs):
|
57
63
|
"""
|
58
64
|
mock implementation of run_query.
|
59
65
|
"""
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
]
|
66
|
+
mock_responses = {
|
67
|
+
self.enrollment_queries.get_enrollment_date_range_query(): [[
|
68
|
+
datetime.strptime('2021-01-01', "%Y-%m-%d"),
|
69
|
+
datetime.strptime('2021-12-31', "%Y-%m-%d"),
|
70
|
+
]],
|
71
|
+
self.enrollment_queries.get_enrollment_and_course_count_query(): [[
|
72
|
+
100, 10
|
73
|
+
]],
|
74
|
+
self.enrollment_queries.get_completion_count_query(): [[
|
75
|
+
50
|
76
|
+
]],
|
77
|
+
self.engagement_queries.get_learning_hours_and_daily_sessions_query(): [[
|
78
|
+
100, 10
|
79
|
+
]],
|
80
|
+
'SELECT MAX(created) FROM enterprise_learner_enrollment': [[datetime.strptime('2021-01-01', "%Y-%m-%d")]]
|
81
|
+
}
|
82
|
+
return mock_responses[query]
|
68
83
|
|
69
84
|
def test_get_admin_analytics_aggregates(self):
|
70
85
|
"""
|
@@ -72,16 +87,24 @@ class TestEnterpriseAdminAnalyticsAggregatesView(JWTTestMixin, APITransactionTes
|
|
72
87
|
"""
|
73
88
|
url = reverse('v1:enterprise-admin-analytics-aggregates', kwargs={'enterprise_id': self.enterprise_id})
|
74
89
|
with patch('enterprise_data.admin_analytics.data_loaders.run_query', side_effect=self._mock_run_query):
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
90
|
+
with patch(
|
91
|
+
'enterprise_data.admin_analytics.database.tables.fact_engagement_admin_dash.run_query',
|
92
|
+
side_effect=self._mock_run_query
|
93
|
+
):
|
94
|
+
with patch(
|
95
|
+
'enterprise_data.admin_analytics.database.tables.fact_enrollment_admin_dash.run_query',
|
96
|
+
side_effect=self._mock_run_query
|
97
|
+
):
|
98
|
+
response = self.client.get(url)
|
99
|
+
assert response.status_code == status.HTTP_200_OK
|
100
|
+
assert 'enrolls' in response.json()
|
101
|
+
assert 'courses' in response.json()
|
102
|
+
assert 'completions' in response.json()
|
103
|
+
assert 'hours' in response.json()
|
104
|
+
assert 'sessions' in response.json()
|
105
|
+
assert 'last_updated_at' in response.json()
|
106
|
+
assert 'min_enrollment_date' in response.json()
|
107
|
+
assert 'max_enrollment_date' in response.json()
|
85
108
|
|
86
109
|
|
87
110
|
@ddt.ddt
|
File without changes
|
File without changes
|
File without changes
|