edx-enterprise-data 9.0.0__py3-none-any.whl → 9.1.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.0.0.dist-info → edx_enterprise_data-9.1.0.dist-info}/METADATA +4 -3
  2. {edx_enterprise_data-9.0.0.dist-info → edx_enterprise_data-9.1.0.dist-info}/RECORD +22 -23
  3. {edx_enterprise_data-9.0.0.dist-info → edx_enterprise_data-9.1.0.dist-info}/WHEEL +1 -1
  4. enterprise_data/__init__.py +1 -1
  5. enterprise_data/admin_analytics/constants.py +0 -16
  6. enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py +90 -0
  7. enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py +83 -4
  8. enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py +116 -0
  9. enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py +97 -3
  10. enterprise_data/api/v1/serializers.py +1 -55
  11. enterprise_data/api/v1/urls.py +14 -17
  12. enterprise_data/api/v1/views/analytics_completions.py +150 -0
  13. enterprise_data/api/v1/views/analytics_engagements.py +106 -351
  14. enterprise_data/api/v1/views/analytics_enrollments.py +3 -6
  15. enterprise_data/renderers.py +22 -7
  16. enterprise_data/tests/admin_analytics/mock_analytics_data.py +12 -90
  17. enterprise_data/tests/admin_analytics/mock_enrollments.py +0 -66
  18. enterprise_data/tests/admin_analytics/test_analytics_engagements.py +120 -240
  19. enterprise_data/tests/admin_analytics/test_analytics_enrollments.py +12 -81
  20. enterprise_data/tests/admin_analytics/test_enterprise_completions.py +105 -120
  21. enterprise_data/admin_analytics/completions_utils.py +0 -261
  22. enterprise_data/api/v1/views/enterprise_completions.py +0 -200
  23. {edx_enterprise_data-9.0.0.dist-info → edx_enterprise_data-9.1.0.dist-info}/LICENSE +0 -0
  24. {edx_enterprise_data-9.0.0.dist-info → edx_enterprise_data-9.1.0.dist-info}/top_level.txt +0 -0
@@ -1,395 +1,150 @@
1
- """Advance Analytics for Engagements"""
1
+ """
2
+ Views for handling REST endpoints related to Engagements analytics.
3
+ """
4
+
2
5
  from datetime import datetime
6
+ from logging import getLogger
3
7
 
4
8
  from edx_rbac.decorators import permission_required
5
9
  from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
6
- from rest_framework import status as rest_status
10
+ from rest_framework.decorators import action
7
11
  from rest_framework.response import Response
8
- from rest_framework.views import APIView
12
+ from rest_framework.viewsets import ViewSet
9
13
 
10
- from django.http import HttpResponse, StreamingHttpResponse
14
+ from django.http import StreamingHttpResponse
11
15
 
12
- from enterprise_data.admin_analytics.constants import Calculation, EngagementChart, Granularity, ResponseType
13
- from enterprise_data.admin_analytics.utils import (
14
- calculation_aggregation,
15
- fetch_and_cache_engagements_data,
16
- fetch_and_cache_enrollments_data,
17
- fetch_enrollments_cache_expiry_timestamp,
18
- granularity_aggregation,
19
- )
20
- from enterprise_data.api.v1 import serializers
16
+ from enterprise_data.admin_analytics.constants import ResponseType
17
+ from enterprise_data.admin_analytics.database.tables import FactEngagementAdminDashTable, FactEnrollmentAdminDashTable
21
18
  from enterprise_data.api.v1.paginators import AdvanceAnalyticsPagination
19
+ from enterprise_data.api.v1.serializers import AdvanceAnalyticsQueryParamSerializer
20
+ from enterprise_data.api.v1.views.base import AnalyticsPaginationMixin
22
21
  from enterprise_data.renderers import IndividualEngagementsCSVRenderer
23
- from enterprise_data.utils import date_filter
22
+ from enterprise_data.utils import timer
24
23
 
24
+ LOGGER = getLogger(__name__)
25
25
 
26
- class AdvanceAnalyticsIndividualEngagementsView(APIView):
27
- """
28
- API for getting the advance analytics individual engagements data.
26
+
27
+ class AdvanceAnalyticsEngagementView(AnalyticsPaginationMixin, ViewSet):
29
28
  """
29
+ View to handle requests for enterprise engagement data.
30
30
 
31
+ Here is the list of URLs that are handled by this view:
32
+ 1. `enterprise_data_api_v1.enterprise-learner-engagement-list`: Get individual engagement data.
33
+ 2. `enterprise_data_api_v1.enterprise-learner-engagement-stats`: Get engagement stats data.
34
+ """
31
35
  authentication_classes = (JwtAuthentication,)
32
36
  pagination_class = AdvanceAnalyticsPagination
33
- http_method_names = ["get"]
37
+ http_method_names = ('get', )
34
38
 
35
39
  @permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
36
- def get(self, request, enterprise_uuid):
40
+ def list(self, request, enterprise_uuid):
37
41
  """
38
- HTTP GET endpoint to retrieve the enterprise engagements data.
42
+ Get individual engagements data for the enterprise.
39
43
  """
40
- serializer = serializers.AdvanceAnalyticsEngagementStatsSerializer(data=request.GET)
44
+ # Remove hyphens from the UUID
45
+ enterprise_uuid = enterprise_uuid.replace('-', '')
46
+
47
+ serializer = AdvanceAnalyticsQueryParamSerializer(data=request.GET)
41
48
  serializer.is_valid(raise_exception=True)
42
- cache_expiry = fetch_enrollments_cache_expiry_timestamp()
49
+ min_enrollment_date, _ = FactEnrollmentAdminDashTable().get_enrollment_date_range(
50
+ enterprise_uuid,
51
+ )
43
52
 
44
- enrollment_df = fetch_and_cache_enrollments_data(enterprise_uuid, cache_expiry).copy()
45
- engagement_df = fetch_and_cache_engagements_data(enterprise_uuid, cache_expiry).copy()
46
- # Use start and end date if provided by the client, if client has not provided then use
47
- # 1. minimum enrollment date from the data as the start_date
48
- # 2. today's date as the end_date
49
- start_date = serializer.data.get('start_date', enrollment_df.enterprise_enrollment_date.min())
53
+ # get values from query params or use default values
54
+ start_date = serializer.data.get('start_date', min_enrollment_date)
50
55
  end_date = serializer.data.get('end_date', datetime.now())
56
+ page = serializer.data.get('page', 1)
57
+ page_size = serializer.data.get('page_size', 100)
58
+ engagements = FactEngagementAdminDashTable().get_all_engagements(
59
+ enterprise_customer_uuid=enterprise_uuid,
60
+ start_date=start_date,
61
+ end_date=end_date,
62
+ limit=page_size,
63
+ offset=(page - 1) * page_size,
64
+ )
65
+ total_count = FactEngagementAdminDashTable().get_engagement_count(
66
+ enterprise_customer_uuid=enterprise_uuid,
67
+ start_date=start_date,
68
+ end_date=end_date,
69
+ )
51
70
  response_type = request.query_params.get('response_type', ResponseType.JSON.value)
52
- # Date filtering.
53
- engagements = date_filter(
54
- start=start_date, end=end_date, data_frame=engagement_df.copy(), date_column='activity_date'
71
+
72
+ LOGGER.info(
73
+ "Individual engagements data requested for enterprise [%s] from [%s] to [%s]",
74
+ enterprise_uuid,
75
+ start_date,
76
+ end_date,
55
77
  )
56
- engagements["learning_time_hours"] = engagements["learning_time_seconds"] / 60 / 60
57
- engagements = engagements[engagements["learning_time_hours"] > 0]
58
- engagements["learning_time_hours"] = round(engagements["learning_time_hours"].astype(float), 1)
59
78
 
60
- # Select only the columns that will be in the table.
61
- engagements = engagements[
62
- [
63
- "email",
64
- "course_title",
65
- "activity_date",
66
- "course_subject",
67
- "learning_time_hours",
68
- ]
69
- ]
70
- engagements["activity_date"] = engagements["activity_date"].dt.date
71
- engagements = engagements.sort_values(by="activity_date", ascending=False).reset_index(drop=True)
72
79
  if response_type == ResponseType.CSV.value:
73
- response = StreamingHttpResponse(
74
- IndividualEngagementsCSVRenderer().render(self._stream_serialized_data(engagements)),
75
- content_type="text/csv"
80
+ filename = f"""individual_engagements, {start_date} - {end_date}.csv"""
81
+
82
+ return StreamingHttpResponse(
83
+ IndividualEngagementsCSVRenderer().render(self._stream_serialized_data(
84
+ enterprise_uuid, start_date, end_date, total_count
85
+ )),
86
+ content_type="text/csv",
87
+ headers={"Content-Disposition": f'attachment; filename="{filename}"'},
76
88
  )
77
- start_date = start_date.strftime('%Y/%m/%d')
78
- end_date = end_date.strftime('%Y/%m/%d')
79
- filename = f"""Individual Engagements, {start_date} - {end_date}.csv"""
80
- response['Content-Disposition'] = f'attachment; filename="{filename}"'
81
- response['Access-Control-Expose-Headers'] = 'Content-Disposition'
82
- return response
83
89
 
84
- paginator = self.pagination_class()
85
- page = paginator.paginate_queryset(engagements, request)
86
- serialized_data = page.data.to_dict(orient='records')
87
- response = paginator.get_paginated_response(serialized_data)
88
-
89
- return response
90
+ return self.get_paginated_response(
91
+ request=request,
92
+ records=engagements,
93
+ page=page,
94
+ page_size=page_size,
95
+ total_count=total_count,
96
+ )
90
97
 
91
- def _stream_serialized_data(self, engagements, chunk_size=50000):
98
+ @staticmethod
99
+ def _stream_serialized_data(enterprise_uuid, start_date, end_date, total_count, page_size=50000):
92
100
  """
93
101
  Stream the serialized data.
94
102
  """
95
- total_rows = engagements.shape[0]
96
- for start_index in range(0, total_rows, chunk_size):
97
- end_index = min(start_index + chunk_size, total_rows)
98
- chunk = engagements.iloc[start_index:end_index]
99
- yield from chunk.to_dict(orient='records')
100
-
101
-
102
- class AdvanceAnalyticsEngagementStatsView(APIView):
103
- """
104
- API for getting the advance analytics engagements statistics data.
105
- """
106
-
107
- authentication_classes = (JwtAuthentication,)
108
- http_method_names = ["get"]
103
+ offset = 0
104
+ while offset < total_count:
105
+ engagements = FactEngagementAdminDashTable().get_all_engagements(
106
+ enterprise_customer_uuid=enterprise_uuid,
107
+ start_date=start_date,
108
+ end_date=end_date,
109
+ limit=page_size,
110
+ offset=offset,
111
+ )
112
+ yield from engagements
113
+ offset += page_size
109
114
 
110
115
  @permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
111
- def get(self, request, enterprise_uuid):
116
+ @action(detail=False, methods=['get'], name='Enterprise engagements data for charts', url_path='stats')
117
+ def stats(self, request, enterprise_uuid):
112
118
  """
113
- HTTP GET endpoint to retrieve the enterprise engagements statistics data.
119
+ Get data to populate enterprise engagement charts.
120
+
121
+ Here is the list of the charts and their corresponding data:
122
+ 1. `engagement_over_time`: This will show time series data of engagements over time.
123
+ 2. `top_courses_by_engagement`: This will show the top courses by engagements.
124
+ 3. `top_subjects_by_engagement`: This will show the top subjects by engagements.
114
125
  """
115
- serializer = serializers.AdvanceAnalyticsEngagementStatsSerializer(data=request.GET)
116
- serializer.is_valid(raise_exception=True)
126
+ # Remove hyphens from the UUID
127
+ enterprise_uuid = enterprise_uuid.replace('-', '')
117
128
 
118
- cache_expiry = fetch_enrollments_cache_expiry_timestamp()
129
+ serializer = AdvanceAnalyticsQueryParamSerializer(data=request.GET)
130
+ serializer.is_valid(raise_exception=True)
119
131
 
120
- enrollment_df = fetch_and_cache_enrollments_data(enterprise_uuid, cache_expiry).copy()
121
- engagement_df = fetch_and_cache_engagements_data(enterprise_uuid, cache_expiry).copy()
122
- # Use start and end date if provided by the client, if client has not provided then use
123
- # 1. minimum enrollment date from the data as the start_date
124
- # 2. today's date as the end_date
125
- start_date = serializer.data.get('start_date', enrollment_df.enterprise_enrollment_date.min())
132
+ min_enrollment_date, _ = FactEnrollmentAdminDashTable().get_enrollment_date_range(
133
+ enterprise_uuid,
134
+ )
135
+ # get values from query params or use default
136
+ start_date = serializer.data.get('start_date', min_enrollment_date)
126
137
  end_date = serializer.data.get('end_date', datetime.now())
127
- granularity = serializer.data.get('granularity', Granularity.DAILY.value)
128
- calculation = serializer.data.get('calculation', Calculation.TOTAL.value)
129
- chart_type = serializer.data.get('chart_type')
130
-
131
- if chart_type is None:
138
+ with timer('construct_engagement_all_stats'):
132
139
  data = {
133
- "engagements_over_time": self.construct_engagements_over_time(
134
- engagement_df.copy(),
135
- start_date,
136
- end_date,
137
- granularity,
138
- calculation,
140
+ 'engagement_over_time': FactEngagementAdminDashTable().get_engagement_time_series_data(
141
+ enterprise_uuid, start_date, end_date
139
142
  ),
140
- "top_courses_by_engagement": self.construct_top_courses_by_engagements(
141
- engagement_df.copy(),
142
- start_date,
143
- end_date,
143
+ 'top_courses_by_engagement': FactEngagementAdminDashTable().get_top_courses_by_engagement(
144
+ enterprise_uuid, start_date, end_date,
144
145
  ),
145
- "top_subjects_by_engagement": self.construct_top_subjects_by_engagements(
146
- engagement_df.copy(),
147
- start_date,
148
- end_date,
146
+ 'top_subjects_by_engagement': FactEngagementAdminDashTable().get_top_subjects_by_engagement(
147
+ enterprise_uuid, start_date, end_date,
149
148
  ),
150
149
  }
151
- return Response(data)
152
- elif chart_type == EngagementChart.ENGAGEMENTS_OVER_TIME.value:
153
- return self.construct_engagements_over_time_csv(
154
- engagement_df.copy(),
155
- start_date,
156
- end_date,
157
- granularity,
158
- calculation,
159
- )
160
- elif chart_type == EngagementChart.TOP_COURSES_BY_ENGAGEMENTS.value:
161
- return self.construct_top_courses_by_engagements_csv(
162
- engagement_df.copy(),
163
- start_date,
164
- end_date,
165
- )
166
- elif chart_type == EngagementChart.TOP_SUBJECTS_BY_ENGAGEMENTS.value:
167
- return self.construct_top_subjects_by_engagements_csv(
168
- engagement_df.copy(),
169
- start_date,
170
- end_date,
171
- )
172
- return Response(data='Not Found', status=rest_status.HTTP_400_BAD_REQUEST)
173
-
174
- def engagements_over_time_common(self, engagements_df, start_date, end_date, granularity, calculation):
175
- """
176
- Common method for constructing engagements over time data.
177
-
178
- Arguments:
179
- engagements_df {DataFrame} -- DataFrame of engagements
180
- start_date {datetime} -- Engagement start date in the format 'YYYY-MM-DD'
181
- end_date {datetime} -- Engagement end date in the format 'YYYY-MM-DD'
182
- granularity {str} -- Granularity of the data. One of Granularity choices
183
- calculation {str} -- Calculation of the data. One of Calculation choices
184
- """
185
- engagements_df["learning_time_hours"] = engagements_df["learning_time_seconds"] / 60 / 60
186
- engagements_df = engagements_df[engagements_df["learning_time_hours"] > 0]
187
- engagements_df["learning_time_hours"] = round(engagements_df["learning_time_hours"].astype(float), 1)
188
-
189
- engagements_df = engagements_df[["activity_date", "enroll_type", "learning_time_hours"]]
190
-
191
- # Date filtering.
192
- engagements_df = date_filter(
193
- start=start_date, end=end_date, data_frame=engagements_df, date_column="activity_date"
194
- )
195
-
196
- # Date aggregation.
197
- engagements_df = granularity_aggregation(
198
- level=granularity,
199
- group=["activity_date", "enroll_type"],
200
- date="activity_date",
201
- data_frame=engagements_df,
202
- aggregation_type="sum",
203
- )
204
-
205
- # Calculating metric.
206
- engagements = calculation_aggregation(calc=calculation, aggregation_type="sum", data_frame=engagements_df)
207
- return engagements
208
-
209
- def construct_engagements_over_time(self, engagements_df, start_date, end_date, granularity, calculation):
210
- """
211
- Construct engagements over time data.
212
-
213
- Arguments:
214
- engagements_df {DataFrame} -- DataFrame of engagements
215
- start_date {datetime} -- Engagement start date in the format 'YYYY-MM-DD'
216
- end_date {datetime} -- Engagement end date in the format 'YYYY-MM-DD'
217
- granularity {str} -- Granularity of the data. One of Granularity choices
218
- calculation {str} -- Calculation of the data. One of Calculation choices
219
- """
220
- engagements = self.engagements_over_time_common(engagements_df, start_date, end_date, granularity, calculation)
221
- # convert dataframe to a list of records
222
- return engagements.to_dict(orient='records')
223
-
224
- def construct_engagements_over_time_csv(self, engagements_df, start_date, end_date, granularity, calculation):
225
- """
226
- Construct engagements over time CSV.
227
-
228
- Arguments:
229
- engagements_df {DataFrame} -- DataFrame of engagements
230
- start_date {datetime} -- Engagement start date in the format 'YYYY-MM-DD'
231
- end_date {datetime} -- Engagement end date in the format 'YYYY-MM-DD'
232
- granularity {str} -- Granularity of the data. One of Granularity choices
233
- calculation {str} -- Calculation of the data. One of Calculation choices
234
- """
235
- engagements = self.engagements_over_time_common(engagements_df, start_date, end_date, granularity, calculation)
236
-
237
- engagements = engagements.pivot(
238
- index="activity_date", columns="enroll_type", values="sum"
239
- )
240
-
241
- filename = f"Engagement Timeseries, {start_date} - {end_date} ({granularity} {calculation}).csv"
242
- return self.construct_csv_response(engagements, filename)
243
-
244
- def top_courses_by_engagements_common(self, engagements_df, start_date, end_date):
245
- """
246
- Common method for constructing top courses by engagements data.
247
-
248
- Arguments:
249
- engagements_df {DataFrame} -- DataFrame of engagements
250
- start_date {datetime} -- Engagement start date in the format 'YYYY-MM-DD'
251
- end_date {datetime} -- Engagement end date in the format 'YYYY-MM-DD'
252
- group_by_columns {list} -- List of columns to group by
253
- columns {list} -- List of column for the final result
254
- """
255
- engagements_df["learning_time_hours"] = engagements_df["learning_time_seconds"] / 60 / 60
256
- engagements_df["learning_time_hours"] = engagements_df["learning_time_hours"].astype("float")
257
-
258
- # Date filtering.
259
- engagements = date_filter(
260
- start=start_date, end=end_date, data_frame=engagements_df, date_column="activity_date"
261
- )
262
-
263
- courses = list(
264
- engagements.groupby(["course_key"])
265
- .learning_time_hours.sum()
266
- .sort_values(ascending=False)[:10]
267
- .index
268
- )
269
-
270
- engagements = (
271
- engagements_df[engagements_df.course_key.isin(courses)]
272
- .groupby(["course_key", "course_title", "enroll_type"])
273
- .learning_time_hours.sum()
274
- .reset_index()
275
- )
276
-
277
- engagements.columns = ["course_key", "course_title", "enroll_type", "count"]
278
-
279
- return engagements
280
-
281
- def construct_top_courses_by_engagements(self, engagements_df, start_date, end_date):
282
- """
283
- Construct top courses by engagements data.
284
-
285
- Arguments:
286
- engagements_df {DataFrame} -- DataFrame of engagements
287
- start_date {datetime} -- Engagement start date in the format 'YYYY-MM-DD'
288
- end_date {datetime} -- Engagement end date in the format 'YYYY-MM-DD'
289
- """
290
- engagements = self.top_courses_by_engagements_common(
291
- engagements_df,
292
- start_date,
293
- end_date
294
- )
295
-
296
- # convert dataframe to a list of records
297
- return engagements.to_dict(orient='records')
298
-
299
- def construct_top_courses_by_engagements_csv(self, engagements_df, start_date, end_date):
300
- """
301
- Construct top courses by engagements CSV.
302
-
303
- Arguments:
304
- engagements_df {DataFrame} -- DataFrame of engagements
305
- start_date {datetime} -- Engagement start date in the format 'YYYY-MM-DD'
306
- end_date {datetime} -- Engagement end date in the format 'YYYY-MM-DD'
307
- """
308
- engagements = self.top_courses_by_engagements_common(
309
- engagements_df,
310
- start_date,
311
- end_date
312
- )
313
-
314
- engagements = engagements.pivot(
315
- index=["course_key", "course_title"], columns="enroll_type", values="count"
316
- )
317
-
318
- filename = f"Top 10 Courses by Learning Hours, {start_date} - {end_date}.csv"
319
- return self.construct_csv_response(engagements, filename)
320
-
321
- def top_subjects_by_engagements_common(self, engagements_df, start_date, end_date):
322
- """
323
- Common method for constructing top subjects by engagements data.
324
-
325
- Arguments:
326
- engagements_df {DataFrame} -- DataFrame of engagements
327
- start_date {datetime} -- Engagement start date in the format 'YYYY-MM-DD'
328
- end_date {datetime} -- Engagement end date in the format 'YYYY-MM-DD'
329
- """
330
- engagements_df["learning_time_hours"] = engagements_df["learning_time_seconds"] / 60 / 60
331
- engagements_df["learning_time_hours"] = engagements_df["learning_time_hours"].astype("float")
332
-
333
- # Date filtering.
334
- engagements = date_filter(
335
- start=start_date, end=end_date, data_frame=engagements_df, date_column="activity_date"
336
- )
337
-
338
- subjects = list(
339
- engagements.groupby(["course_subject"])
340
- .learning_time_hours.sum()
341
- .sort_values(ascending=False)[:10]
342
- .index
343
- )
344
-
345
- engagements = (
346
- engagements[engagements.course_subject.isin(subjects)]
347
- .groupby(["course_subject", "enroll_type"])
348
- .learning_time_hours.sum()
349
- .reset_index()
350
- )
351
- engagements.columns = ["course_subject", "enroll_type", "count"]
352
-
353
- return engagements
354
-
355
- def construct_top_subjects_by_engagements(self, engagements_df, start_date, end_date):
356
- """
357
- Construct top subjects by engagements data.
358
-
359
- Arguments:
360
- engagements_df {DataFrame} -- DataFrame of engagements
361
- start_date {datetime} -- Engagement start date in the format 'YYYY-MM-DD'
362
- end_date {datetime} -- Engagement end date in the format 'YYYY-MM-DD'
363
- """
364
- engagements = self.top_subjects_by_engagements_common(engagements_df, start_date, end_date)
365
- # convert dataframe to a list of records
366
- return engagements.to_dict(orient='records')
367
-
368
- def construct_top_subjects_by_engagements_csv(self, engagements_df, start_date, end_date):
369
- """
370
- Construct top subjects by engagements CSV.
371
-
372
- Arguments:
373
- engagements_df {DataFrame} -- DataFrame of engagements
374
- start_date {datetime} -- Engagement start date in the format 'YYYY-MM-DD'
375
- end_date {datetime} -- Engagement end date in the format 'YYYY-MM-DD'
376
- """
377
- engagements = self.top_subjects_by_engagements_common(engagements_df, start_date, end_date)
378
- engagements = engagements.pivot(index="course_subject", columns="enroll_type", values="count")
379
- filename = f"Top 10 Subjects by Learning Hours, {start_date} - {end_date}.csv"
380
- return self.construct_csv_response(engagements, filename)
381
-
382
- def construct_csv_response(self, engagements, filename):
383
- """
384
- Construct CSV response.
385
-
386
- Arguments:
387
- engagements {DataFrame} -- DataFrame of engagements
388
- filename {str} -- Filename for the CSV
389
- """
390
- response = HttpResponse(content_type='text/csv')
391
- response['Content-Disposition'] = f'attachment; filename="{filename}"'
392
- response['Access-Control-Expose-Headers'] = 'Content-Disposition'
393
- engagements.to_csv(path_or_buf=response)
394
-
395
- return response
150
+ return Response(data)
@@ -15,10 +15,7 @@ from django.http import StreamingHttpResponse
15
15
  from enterprise_data.admin_analytics.constants import ResponseType
16
16
  from enterprise_data.admin_analytics.database.tables import FactEnrollmentAdminDashTable
17
17
  from enterprise_data.api.v1.paginators import AdvanceAnalyticsPagination
18
- from enterprise_data.api.v1.serializers import (
19
- AdvanceAnalyticsEnrollmentStatsSerializer,
20
- AdvanceAnalyticsQueryParamSerializer,
21
- )
18
+ from enterprise_data.api.v1.serializers import AdvanceAnalyticsQueryParamSerializer
22
19
  from enterprise_data.api.v1.views.base import AnalyticsPaginationMixin
23
20
  from enterprise_data.renderers import IndividualEnrollmentsCSVRenderer
24
21
  from enterprise_data.utils import timer
@@ -115,7 +112,7 @@ class AdvanceAnalyticsEnrollmentsView(AnalyticsPaginationMixin, ViewSet):
115
112
  offset += page_size
116
113
 
117
114
  @permission_required('can_access_enterprise', fn=lambda request, enterprise_uuid: enterprise_uuid)
118
- @action(detail=False, methods=['get'], name='Charts Data', url_path='stats')
115
+ @action(detail=False, methods=['get'], name='Enterprise enrollments data for charts', url_path='stats')
119
116
  def stats(self, request, enterprise_uuid):
120
117
  """
121
118
  Get data to populate enterprise enrollment charts.
@@ -128,7 +125,7 @@ class AdvanceAnalyticsEnrollmentsView(AnalyticsPaginationMixin, ViewSet):
128
125
  # Remove hyphens from the UUID
129
126
  enterprise_uuid = enterprise_uuid.replace('-', '')
130
127
 
131
- serializer = AdvanceAnalyticsEnrollmentStatsSerializer(data=request.GET)
128
+ serializer = AdvanceAnalyticsQueryParamSerializer(data=request.GET)
132
129
  serializer.is_valid(raise_exception=True)
133
130
 
134
131
  min_enrollment_date, _ = FactEnrollmentAdminDashTable().get_enrollment_date_range(
@@ -45,17 +45,17 @@ class IndividualEnrollmentsCSVRenderer(CSVStreamingRenderer):
45
45
  ]
46
46
 
47
47
 
48
- class LeaderboardCSVRenderer(CSVStreamingRenderer):
48
+ class IndividualCompletionsCSVRenderer(CSVStreamingRenderer):
49
49
  """
50
- Custom streaming csv renderer for advance analytics leaderboard data.
50
+ Custom streaming csv renderer for advance analytics individual completions data.
51
51
  """
52
52
 
53
53
  header = [
54
54
  'email',
55
- 'learning_time_hours',
56
- 'daily_sessions',
57
- 'average_session_length',
58
- 'course_completions',
55
+ 'course_title',
56
+ 'course_subject',
57
+ 'enroll_type',
58
+ 'passed_date',
59
59
  ]
60
60
 
61
61
 
@@ -67,7 +67,22 @@ class IndividualEngagementsCSVRenderer(CSVStreamingRenderer):
67
67
  header = [
68
68
  'email',
69
69
  'course_title',
70
- 'activity_date',
71
70
  'course_subject',
71
+ 'enroll_type',
72
+ 'activity_date',
73
+ 'learning_time_hours',
74
+ ]
75
+
76
+
77
+ class LeaderboardCSVRenderer(CSVStreamingRenderer):
78
+ """
79
+ Custom streaming csv renderer for advance analytics leaderboard data.
80
+ """
81
+
82
+ header = [
83
+ 'email',
72
84
  'learning_time_hours',
85
+ 'daily_sessions',
86
+ 'average_session_length',
87
+ 'course_completions',
73
88
  ]