edx-enterprise-data 9.0.1__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.
- {edx_enterprise_data-9.0.1.dist-info → edx_enterprise_data-9.1.0.dist-info}/METADATA +1 -1
- {edx_enterprise_data-9.0.1.dist-info → edx_enterprise_data-9.1.0.dist-info}/RECORD +22 -23
- enterprise_data/__init__.py +1 -1
- enterprise_data/admin_analytics/constants.py +0 -16
- enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py +90 -0
- enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py +83 -4
- enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py +116 -0
- enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py +97 -3
- enterprise_data/api/v1/serializers.py +1 -55
- enterprise_data/api/v1/urls.py +14 -17
- enterprise_data/api/v1/views/analytics_completions.py +150 -0
- enterprise_data/api/v1/views/analytics_engagements.py +106 -351
- enterprise_data/api/v1/views/analytics_enrollments.py +3 -6
- enterprise_data/renderers.py +22 -7
- enterprise_data/tests/admin_analytics/mock_analytics_data.py +12 -90
- enterprise_data/tests/admin_analytics/mock_enrollments.py +0 -66
- enterprise_data/tests/admin_analytics/test_analytics_engagements.py +120 -240
- enterprise_data/tests/admin_analytics/test_analytics_enrollments.py +12 -81
- enterprise_data/tests/admin_analytics/test_enterprise_completions.py +105 -120
- enterprise_data/admin_analytics/completions_utils.py +0 -261
- enterprise_data/api/v1/views/enterprise_completions.py +0 -200
- {edx_enterprise_data-9.0.1.dist-info → edx_enterprise_data-9.1.0.dist-info}/LICENSE +0 -0
- {edx_enterprise_data-9.0.1.dist-info → edx_enterprise_data-9.1.0.dist-info}/WHEEL +0 -0
- {edx_enterprise_data-9.0.1.dist-info → edx_enterprise_data-9.1.0.dist-info}/top_level.txt +0 -0
@@ -1,200 +0,0 @@
|
|
1
|
-
"""Views for enterprise admin completions analytics."""
|
2
|
-
import datetime
|
3
|
-
from datetime import datetime, timedelta
|
4
|
-
from logging import getLogger
|
5
|
-
|
6
|
-
from edx_rbac.decorators import permission_required
|
7
|
-
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
|
8
|
-
from rest_framework.response import Response
|
9
|
-
from rest_framework.status import HTTP_200_OK
|
10
|
-
from rest_framework.views import APIView
|
11
|
-
|
12
|
-
from django.http import HttpResponse
|
13
|
-
|
14
|
-
from enterprise_data.admin_analytics.completions_utils import (
|
15
|
-
get_completions_over_time,
|
16
|
-
get_csv_data_for_completions_over_time,
|
17
|
-
get_csv_data_for_top_courses_by_completions,
|
18
|
-
get_csv_data_for_top_subjects_by_completions,
|
19
|
-
get_top_courses_by_completions,
|
20
|
-
get_top_subjects_by_completions,
|
21
|
-
)
|
22
|
-
from enterprise_data.admin_analytics.constants import Calculation, Granularity
|
23
|
-
from enterprise_data.admin_analytics.data_loaders import fetch_max_enrollment_datetime
|
24
|
-
from enterprise_data.admin_analytics.utils import ChartType, fetch_and_cache_enrollments_data
|
25
|
-
from enterprise_data.api.v1 import serializers
|
26
|
-
from enterprise_data.api.v1.paginators import AdvanceAnalyticsPagination
|
27
|
-
from enterprise_data.utils import date_filter, timer
|
28
|
-
|
29
|
-
LOGGER = getLogger(__name__)
|
30
|
-
|
31
|
-
|
32
|
-
class EnterrpiseAdminCompletionsStatsView(APIView):
|
33
|
-
"""
|
34
|
-
API for getting the enterprise admin completions.
|
35
|
-
"""
|
36
|
-
authentication_classes = (JwtAuthentication,)
|
37
|
-
http_method_names = ['get']
|
38
|
-
|
39
|
-
@permission_required(
|
40
|
-
"can_access_enterprise", fn=lambda request, enterprise_id: enterprise_id
|
41
|
-
)
|
42
|
-
def get(self, request, enterprise_id):
|
43
|
-
"""
|
44
|
-
HTTP GET endpoint to retrieve the enterprise admin completions
|
45
|
-
"""
|
46
|
-
serializer = serializers.AdminAnalyticsAggregatesQueryParamsSerializer(
|
47
|
-
data=request.GET
|
48
|
-
)
|
49
|
-
serializer.is_valid(raise_exception=True)
|
50
|
-
|
51
|
-
last_updated_at = fetch_max_enrollment_datetime()
|
52
|
-
cache_expiry = (
|
53
|
-
last_updated_at + timedelta(days=1) if last_updated_at else datetime.now()
|
54
|
-
)
|
55
|
-
|
56
|
-
enrollments = fetch_and_cache_enrollments_data(
|
57
|
-
enterprise_id, cache_expiry
|
58
|
-
).copy()
|
59
|
-
# Use start and end date if provided by the client, if client has not provided then use
|
60
|
-
# 1. minimum enrollment date from the data as the start_date
|
61
|
-
# 2. today's date as the end_date
|
62
|
-
start_date = serializer.data.get(
|
63
|
-
"start_date", enrollments.enterprise_enrollment_date.min()
|
64
|
-
)
|
65
|
-
end_date = serializer.data.get("end_date", datetime.now())
|
66
|
-
|
67
|
-
if serializer.data.get('response_type') == 'csv':
|
68
|
-
chart_type = serializer.data.get('chart_type')
|
69
|
-
response = HttpResponse(content_type='text/csv')
|
70
|
-
csv_data = {}
|
71
|
-
|
72
|
-
if chart_type == ChartType.COMPLETIONS_OVER_TIME.value:
|
73
|
-
with timer('completions_over_time_csv_data'):
|
74
|
-
csv_data = get_csv_data_for_completions_over_time(
|
75
|
-
start_date=start_date,
|
76
|
-
end_date=end_date,
|
77
|
-
enrollments=enrollments.copy(),
|
78
|
-
date_agg=serializer.data.get('granularity', Granularity.DAILY.value),
|
79
|
-
calc=serializer.data.get('calculation', Calculation.TOTAL.value),
|
80
|
-
)
|
81
|
-
elif chart_type == ChartType.TOP_COURSES_BY_COMPLETIONS.value:
|
82
|
-
with timer('top_courses_by_completions_csv_data'):
|
83
|
-
csv_data = get_csv_data_for_top_courses_by_completions(
|
84
|
-
start_date=start_date, end_date=end_date, enrollments=enrollments.copy()
|
85
|
-
)
|
86
|
-
elif chart_type == ChartType.TOP_SUBJECTS_BY_COMPLETIONS.value:
|
87
|
-
with timer('top_subjects_by_completions_csv_data'):
|
88
|
-
csv_data = get_csv_data_for_top_subjects_by_completions(
|
89
|
-
start_date=start_date, end_date=end_date, enrollments=enrollments.copy()
|
90
|
-
)
|
91
|
-
filename = csv_data['filename']
|
92
|
-
response['Content-Disposition'] = f'attachment; filename="{filename}"'
|
93
|
-
response['Access-Control-Expose-Headers'] = 'Content-Disposition'
|
94
|
-
csv_data['data'].to_csv(path_or_buf=response)
|
95
|
-
return response
|
96
|
-
|
97
|
-
with timer('completions_all_charts_data'):
|
98
|
-
completions_over_time = get_completions_over_time(
|
99
|
-
start_date=start_date,
|
100
|
-
end_date=end_date,
|
101
|
-
dff=enrollments.copy(),
|
102
|
-
date_agg=serializer.data.get('granularity', Granularity.DAILY.value),
|
103
|
-
calc=serializer.data.get('calculation', Calculation.TOTAL.value),
|
104
|
-
)
|
105
|
-
top_courses_by_completions = get_top_courses_by_completions(
|
106
|
-
start_date=start_date, end_date=end_date, dff=enrollments.copy()
|
107
|
-
)
|
108
|
-
top_subjects_by_completions = get_top_subjects_by_completions(
|
109
|
-
start_date=start_date, end_date=end_date, dff=enrollments.copy()
|
110
|
-
)
|
111
|
-
|
112
|
-
return Response(
|
113
|
-
data={
|
114
|
-
'completions_over_time': completions_over_time.to_dict(
|
115
|
-
orient="records"
|
116
|
-
),
|
117
|
-
'top_courses_by_completions': top_courses_by_completions.to_dict(
|
118
|
-
orient="records"
|
119
|
-
),
|
120
|
-
'top_subjects_by_completions': top_subjects_by_completions.to_dict(
|
121
|
-
orient="records"
|
122
|
-
),
|
123
|
-
},
|
124
|
-
status=HTTP_200_OK,
|
125
|
-
)
|
126
|
-
|
127
|
-
|
128
|
-
class EnterrpiseAdminCompletionsView(APIView):
|
129
|
-
"""
|
130
|
-
API for getting the enterprise admin completions.
|
131
|
-
"""
|
132
|
-
authentication_classes = (JwtAuthentication,)
|
133
|
-
http_method_names = ['get']
|
134
|
-
pagination_class = AdvanceAnalyticsPagination
|
135
|
-
|
136
|
-
@permission_required(
|
137
|
-
"can_access_enterprise", fn=lambda request, enterprise_id: enterprise_id
|
138
|
-
)
|
139
|
-
def get(self, request, enterprise_id):
|
140
|
-
"""
|
141
|
-
HTTP GET endpoint to retrieve the enterprise admin completions
|
142
|
-
"""
|
143
|
-
serializer = serializers.AdminAnalyticsAggregatesQueryParamsSerializer(
|
144
|
-
data=request.GET
|
145
|
-
)
|
146
|
-
serializer.is_valid(raise_exception=True)
|
147
|
-
|
148
|
-
last_updated_at = fetch_max_enrollment_datetime()
|
149
|
-
cache_expiry = (
|
150
|
-
last_updated_at + timedelta(days=1) if last_updated_at else datetime.now()
|
151
|
-
)
|
152
|
-
|
153
|
-
enrollments = fetch_and_cache_enrollments_data(
|
154
|
-
enterprise_id, cache_expiry
|
155
|
-
).copy()
|
156
|
-
# Use start and end date if provided by the client, if client has not provided then use
|
157
|
-
# 1. minimum enrollment date from the data as the start_date
|
158
|
-
# 2. today's date as the end_date
|
159
|
-
start_date = serializer.data.get(
|
160
|
-
'start_date', enrollments.enterprise_enrollment_date.min()
|
161
|
-
)
|
162
|
-
end_date = serializer.data.get('end_date', datetime.now())
|
163
|
-
|
164
|
-
LOGGER.info(
|
165
|
-
"Completions data requested for enterprise [%s] from [%s] to [%s]",
|
166
|
-
enterprise_id,
|
167
|
-
start_date,
|
168
|
-
end_date,
|
169
|
-
)
|
170
|
-
|
171
|
-
dff = enrollments[enrollments['has_passed'] == 1]
|
172
|
-
|
173
|
-
# Date filtering
|
174
|
-
dff = date_filter(start=start_date, end=end_date, data_frame=dff, date_column='passed_date')
|
175
|
-
|
176
|
-
dff = dff[['email', 'course_title', 'course_subject', 'passed_date']]
|
177
|
-
dff['passed_date'] = dff['passed_date'].dt.date
|
178
|
-
dff = dff.sort_values(by="passed_date", ascending=False).reset_index(drop=True)
|
179
|
-
|
180
|
-
LOGGER.info(
|
181
|
-
"Completions data prepared for enterprise [%s] from [%s] to [%s]",
|
182
|
-
enterprise_id,
|
183
|
-
start_date,
|
184
|
-
end_date,
|
185
|
-
)
|
186
|
-
|
187
|
-
if serializer.data.get('response_type') == 'csv':
|
188
|
-
response = HttpResponse(content_type='text/csv')
|
189
|
-
filename = f"Individual Completions, {start_date} - {end_date}.csv"
|
190
|
-
response['Content-Disposition'] = f'attachment; filename="{filename}"'
|
191
|
-
response['Access-Control-Expose-Headers'] = 'Content-Disposition'
|
192
|
-
dff.to_csv(path_or_buf=response, index=False)
|
193
|
-
return response
|
194
|
-
|
195
|
-
paginator = self.pagination_class()
|
196
|
-
page = paginator.paginate_queryset(dff, request)
|
197
|
-
serialized_data = page.data.to_dict(orient='records')
|
198
|
-
response = paginator.get_paginated_response(serialized_data)
|
199
|
-
|
200
|
-
return response
|
File without changes
|
File without changes
|
File without changes
|