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,4 +1,6 @@
|
|
1
|
-
"""
|
1
|
+
"""
|
2
|
+
Tests for enterprise completions analytics.
|
3
|
+
"""
|
2
4
|
from datetime import datetime
|
3
5
|
|
4
6
|
import ddt
|
@@ -7,12 +9,8 @@ from rest_framework import status
|
|
7
9
|
from rest_framework.reverse import reverse
|
8
10
|
from rest_framework.test import APITransactionTestCase
|
9
11
|
|
10
|
-
from enterprise_data.admin_analytics.
|
11
|
-
from enterprise_data.tests.admin_analytics.mock_analytics_data import
|
12
|
-
COMPLETIONS_STATS_CSVS,
|
13
|
-
ENROLLMENTS,
|
14
|
-
enrollments_dataframe,
|
15
|
-
)
|
12
|
+
from enterprise_data.admin_analytics.constants import ResponseType
|
13
|
+
from enterprise_data.tests.admin_analytics.mock_analytics_data import ENROLLMENTS
|
16
14
|
from enterprise_data.tests.mixins import JWTTestMixin
|
17
15
|
from enterprise_data.tests.test_utils import UserFactory
|
18
16
|
from enterprise_data_roles.constants import ENTERPRISE_DATA_ADMIN_ROLE
|
@@ -20,8 +18,10 @@ from enterprise_data_roles.models import EnterpriseDataFeatureRole, EnterpriseDa
|
|
20
18
|
|
21
19
|
|
22
20
|
@ddt.ddt
|
23
|
-
class
|
24
|
-
"""
|
21
|
+
class TestCompletionsStatsAPI(JWTTestMixin, APITransactionTestCase):
|
22
|
+
"""
|
23
|
+
Tests for validating enterprise completions stats endpoint.
|
24
|
+
"""
|
25
25
|
|
26
26
|
def setUp(self):
|
27
27
|
"""
|
@@ -37,99 +37,63 @@ class TestCompletionstStatsAPI(JWTTestMixin, APITransactionTestCase):
|
|
37
37
|
)
|
38
38
|
self.client.force_authenticate(user=self.user)
|
39
39
|
|
40
|
-
self.enterprise_id =
|
40
|
+
self.enterprise_id = 'ee5e6b3a-069a-4947-bb8d-d2dbc323396c'
|
41
41
|
self.set_jwt_cookie()
|
42
42
|
|
43
43
|
self.url = reverse(
|
44
|
-
|
45
|
-
kwargs={
|
44
|
+
'v1:enterprise-admin-analytics-completions-stats',
|
45
|
+
kwargs={'enterprise_uuid': self.enterprise_id},
|
46
46
|
)
|
47
47
|
|
48
|
-
|
49
|
-
'enterprise_data.api.v1.views.
|
50
|
-
return_value=datetime.now()
|
48
|
+
get_enrollment_date_range_patcher = patch(
|
49
|
+
'enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_enrollment_date_range',
|
50
|
+
return_value=(datetime.now(), datetime.now())
|
51
51
|
)
|
52
52
|
|
53
|
-
|
54
|
-
self.addCleanup(
|
53
|
+
get_enrollment_date_range_patcher.start()
|
54
|
+
self.addCleanup(get_enrollment_date_range_patcher.stop)
|
55
55
|
|
56
56
|
@patch(
|
57
|
-
|
57
|
+
'enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.'
|
58
|
+
'get_top_subjects_by_completions'
|
58
59
|
)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
params = {
|
66
|
-
"start_date": "2020-01-01",
|
67
|
-
"end_date": "2025-08-09",
|
68
|
-
"calculation": "Running Total",
|
69
|
-
"granularity": "Daily",
|
70
|
-
}
|
71
|
-
response = self.client.get(self.url, params)
|
72
|
-
assert response.status_code == status.HTTP_200_OK
|
73
|
-
data = response.json()
|
74
|
-
assert data == {
|
75
|
-
"completions_over_time": [
|
76
|
-
{
|
77
|
-
"passed_date": "2021-08-25T00:00:00",
|
78
|
-
"enroll_type": "certificate",
|
79
|
-
"count": 1,
|
80
|
-
},
|
81
|
-
{
|
82
|
-
"passed_date": "2021-09-01T00:00:00",
|
83
|
-
"enroll_type": "certificate",
|
84
|
-
"count": 2,
|
85
|
-
},
|
86
|
-
],
|
87
|
-
"top_courses_by_completions": [
|
88
|
-
{
|
89
|
-
"course_key": "hEmW+tvk03",
|
90
|
-
"course_title": "Re-engineered tangible approach",
|
91
|
-
"enroll_type": "certificate",
|
92
|
-
"count": 2,
|
93
|
-
}
|
94
|
-
],
|
95
|
-
"top_subjects_by_completions": [
|
96
|
-
{
|
97
|
-
"course_subject": "business-management",
|
98
|
-
"enroll_type": "certificate",
|
99
|
-
"count": 2,
|
100
|
-
}
|
101
|
-
],
|
102
|
-
}
|
103
|
-
|
104
|
-
@patch("enterprise_data.api.v1.views.enterprise_completions.fetch_and_cache_enrollments_data")
|
105
|
-
@ddt.data(
|
106
|
-
ChartType.COMPLETIONS_OVER_TIME.value,
|
107
|
-
ChartType.TOP_COURSES_BY_COMPLETIONS.value,
|
108
|
-
ChartType.TOP_SUBJECTS_BY_COMPLETIONS.value,
|
60
|
+
@patch(
|
61
|
+
'enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_top_courses_by_completions'
|
62
|
+
)
|
63
|
+
@patch(
|
64
|
+
'enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.'
|
65
|
+
'get_completions_time_series_data'
|
109
66
|
)
|
110
|
-
def
|
67
|
+
def test_get(
|
68
|
+
self,
|
69
|
+
mock_get_completions_time_series_data,
|
70
|
+
mock_get_top_courses_by_completions,
|
71
|
+
mock_get_top_subjects_by_completions
|
72
|
+
):
|
111
73
|
"""
|
112
|
-
Test
|
74
|
+
Test the GET method to fetch charts data for enterprise completion works correctly.
|
113
75
|
"""
|
114
|
-
|
76
|
+
mock_get_completions_time_series_data.return_value = []
|
77
|
+
mock_get_top_courses_by_completions.return_value = []
|
78
|
+
mock_get_top_subjects_by_completions.return_value = []
|
79
|
+
|
115
80
|
params = {
|
116
81
|
'start_date': '2020-01-01',
|
117
82
|
'end_date': '2025-08-09',
|
118
|
-
'calculation': 'Running Total',
|
119
|
-
'granularity': 'Daily',
|
120
|
-
'response_type': 'csv',
|
121
|
-
'chart_type': chart_type,
|
122
83
|
}
|
123
84
|
response = self.client.get(self.url, params)
|
124
85
|
assert response.status_code == status.HTTP_200_OK
|
125
|
-
|
126
|
-
|
127
|
-
assert
|
86
|
+
data = response.json()
|
87
|
+
assert 'completions_over_time' in data
|
88
|
+
assert 'top_courses_by_completions' in data
|
89
|
+
assert 'top_subjects_by_completions' in data
|
128
90
|
|
129
91
|
|
130
92
|
@ddt.ddt
|
131
|
-
class
|
132
|
-
"""
|
93
|
+
class TestCompletionsAPI(JWTTestMixin, APITransactionTestCase):
|
94
|
+
"""
|
95
|
+
Tests for validating list endpoint of enterprise completions.
|
96
|
+
"""
|
133
97
|
|
134
98
|
def setUp(self):
|
135
99
|
"""
|
@@ -145,58 +109,79 @@ class TestCompletionstAPI(JWTTestMixin, APITransactionTestCase):
|
|
145
109
|
)
|
146
110
|
self.client.force_authenticate(user=self.user)
|
147
111
|
|
148
|
-
self.enterprise_id =
|
112
|
+
self.enterprise_id = 'ee5e6b3a-069a-4947-bb8d-d2dbc323396c'
|
149
113
|
self.set_jwt_cookie()
|
150
114
|
|
151
115
|
self.url = reverse(
|
152
|
-
|
153
|
-
kwargs={
|
116
|
+
'v1:enterprise-admin-analytics-completions',
|
117
|
+
kwargs={'enterprise_uuid': self.enterprise_id},
|
154
118
|
)
|
155
119
|
|
156
|
-
|
157
|
-
'enterprise_data.api.v1.views.
|
158
|
-
return_value=datetime.now()
|
120
|
+
get_enrollment_date_range_patcher = patch(
|
121
|
+
'enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_enrollment_date_range',
|
122
|
+
return_value=(datetime.now(), datetime.now())
|
159
123
|
)
|
160
124
|
|
161
|
-
|
162
|
-
self.addCleanup(
|
163
|
-
|
164
|
-
def verify_enrollment_data(self, results, results_count):
|
165
|
-
"""Verify the received enrollment data."""
|
166
|
-
attrs = [
|
167
|
-
"email",
|
168
|
-
"course_title",
|
169
|
-
"course_subject",
|
170
|
-
"passed_date",
|
171
|
-
]
|
172
|
-
|
173
|
-
assert len(results) == results_count
|
174
|
-
|
175
|
-
filtered_data = []
|
176
|
-
for enrollment in ENROLLMENTS:
|
177
|
-
for result in results:
|
178
|
-
if enrollment["email"] == result["email"]:
|
179
|
-
filtered_data.append({attr: enrollment[attr] for attr in attrs})
|
180
|
-
break
|
181
|
-
received_data = sorted(results, key=lambda x: x["email"])
|
182
|
-
expected_data = sorted(filtered_data, key=lambda x: x["email"])
|
183
|
-
assert received_data == expected_data
|
125
|
+
get_enrollment_date_range_patcher.start()
|
126
|
+
self.addCleanup(get_enrollment_date_range_patcher.stop)
|
184
127
|
|
185
|
-
@patch(
|
186
|
-
|
187
|
-
)
|
188
|
-
def test_get(self, mock_fetch_and_cache_enrollments_data):
|
128
|
+
@patch('enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_completion_count')
|
129
|
+
@patch('enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_all_completions')
|
130
|
+
def test_get(self, mock_get_all_completions, mock_get_completion_count):
|
189
131
|
"""
|
190
|
-
Test the GET method for
|
132
|
+
Test the GET method for fetching enterprise completions works correctly.
|
191
133
|
"""
|
192
|
-
|
134
|
+
mock_get_all_completions.return_value = ENROLLMENTS
|
135
|
+
mock_get_completion_count.return_value = len(ENROLLMENTS)
|
193
136
|
|
194
|
-
response = self.client.get(self.url
|
137
|
+
response = self.client.get(self.url + '?page=1&page_size=2')
|
195
138
|
assert response.status_code == status.HTTP_200_OK
|
196
139
|
data = response.json()
|
197
|
-
assert data["next"] == f"http://testserver{self.url}?page=2&page_size=
|
140
|
+
assert data["next"] == f"http://testserver{self.url}?page=2&page_size=2"
|
198
141
|
assert data["previous"] is None
|
199
142
|
assert data["current_page"] == 1
|
200
|
-
assert data["num_pages"] ==
|
201
|
-
assert data["count"] ==
|
202
|
-
|
143
|
+
assert data["num_pages"] == 3
|
144
|
+
assert data["count"] == 5
|
145
|
+
|
146
|
+
@patch('enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_completion_count')
|
147
|
+
@patch('enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.get_all_completions')
|
148
|
+
def test_get_csv(self, mock_get_all_completions, mock_get_completion_count):
|
149
|
+
"""
|
150
|
+
Test the GET method for the AdvanceAnalyticsIndividualEnrollmentsView return correct CSV data.
|
151
|
+
"""
|
152
|
+
mock_get_all_completions.return_value = ENROLLMENTS
|
153
|
+
mock_get_completion_count.return_value = len(ENROLLMENTS)
|
154
|
+
response = self.client.get(self.url, {"response_type": ResponseType.CSV.value})
|
155
|
+
assert response.status_code == status.HTTP_200_OK
|
156
|
+
|
157
|
+
# verify the response headers
|
158
|
+
assert response["Content-Type"] == "text/csv"
|
159
|
+
|
160
|
+
# verify the response content
|
161
|
+
content = b"".join(response.streaming_content).decode().splitlines()
|
162
|
+
assert len(content) == 6
|
163
|
+
|
164
|
+
# Verify CSV header.
|
165
|
+
assert 'email,course_title,course_subject,enroll_type,passed_date' == content[0]
|
166
|
+
|
167
|
+
# verify the content
|
168
|
+
assert (
|
169
|
+
'rebeccanelson@example.com,Re-engineered tangible approach,business-management,certificate,2021-08-25'
|
170
|
+
in content
|
171
|
+
)
|
172
|
+
assert (
|
173
|
+
'taylorjames@example.com,Re-engineered tangible approach,business-management,certificate,2021-09-01'
|
174
|
+
in content
|
175
|
+
)
|
176
|
+
assert (
|
177
|
+
'ssmith@example.com,Secured static capability,medicine,certificate,'
|
178
|
+
in content
|
179
|
+
)
|
180
|
+
assert (
|
181
|
+
'kathleenmartin@example.com,Horizontal solution-oriented hub,social-sciences,certificate,'
|
182
|
+
in content
|
183
|
+
)
|
184
|
+
assert (
|
185
|
+
'amber79@example.com,Streamlined zero-defect attitude,communication,certificate,'
|
186
|
+
in content
|
187
|
+
)
|
@@ -1,261 +0,0 @@
|
|
1
|
-
"""This module contains utility functions for completions analytics."""
|
2
|
-
from enterprise_data.utils import date_filter
|
3
|
-
|
4
|
-
|
5
|
-
def date_aggregation(level, group, date, df, type_="count"):
|
6
|
-
"""Perform date aggregation on a DataFrame.
|
7
|
-
|
8
|
-
This function aggregates data based on the specified level of aggregation (e.g., daily, weekly, monthly, quarterly)
|
9
|
-
and returns the aggregated data.
|
10
|
-
|
11
|
-
Args:
|
12
|
-
level (str): The level of aggregation. Possible values are "Daily", "Weekly", "Monthly", and "Quarterly".
|
13
|
-
group (list): A list of column names to group the data by.
|
14
|
-
date (str): The name of the date column in the DataFrame.
|
15
|
-
df (pandas.DataFrame): The DataFrame containing the data to be aggregated.
|
16
|
-
type_ (str, optional): The type of aggregation to perform. Possible values
|
17
|
-
are "count" and "sum". Defaults to "count".
|
18
|
-
|
19
|
-
Returns:
|
20
|
-
pandas.DataFrame: The aggregated data.
|
21
|
-
|
22
|
-
"""
|
23
|
-
if type_ == "count":
|
24
|
-
if level == "Daily":
|
25
|
-
df = df.groupby(group).size().reset_index()
|
26
|
-
group.append("count")
|
27
|
-
df.columns = group
|
28
|
-
elif level == "Weekly":
|
29
|
-
df[date] = df[date].dt.to_period("W").dt.start_time
|
30
|
-
df = df.groupby(group).size().reset_index()
|
31
|
-
group.append("count")
|
32
|
-
df.columns = group
|
33
|
-
elif level == "Monthly":
|
34
|
-
df[date] = df[date].dt.to_period("M").dt.start_time
|
35
|
-
df = df.groupby(group).size().reset_index()
|
36
|
-
group.append("count")
|
37
|
-
df.columns = group
|
38
|
-
elif level == "Quarterly":
|
39
|
-
df[date] = df[date].dt.to_period("Q").dt.start_time
|
40
|
-
df = df.groupby(group).size().reset_index()
|
41
|
-
group.append("count")
|
42
|
-
df.columns = group
|
43
|
-
elif type_ == "sum":
|
44
|
-
if level == "Daily":
|
45
|
-
df = df.groupby(group).sum().reset_index()
|
46
|
-
group.append("sum")
|
47
|
-
df.columns = group
|
48
|
-
elif level == "Weekly":
|
49
|
-
df[date] = df[date].dt.to_period("W").dt.start_time
|
50
|
-
df = df.groupby(group).sum().reset_index()
|
51
|
-
group.append("sum")
|
52
|
-
df.columns = group
|
53
|
-
elif level == "Monthly":
|
54
|
-
df[date] = df[date].dt.to_period("M").dt.start_time
|
55
|
-
df = df.groupby(group).sum().reset_index()
|
56
|
-
group.append("sum")
|
57
|
-
df.columns = group
|
58
|
-
elif level == "Quarterly":
|
59
|
-
df[date] = df[date].dt.to_period("Q").dt.start_time
|
60
|
-
df = df.groupby(group).sum().reset_index()
|
61
|
-
group.append("sum")
|
62
|
-
df.columns = group
|
63
|
-
|
64
|
-
return df
|
65
|
-
|
66
|
-
|
67
|
-
def calculation(calc, df, type_="count"):
|
68
|
-
"""Perform a calculation on the given DataFrame based on the specified calculation type.
|
69
|
-
|
70
|
-
Args:
|
71
|
-
calc (str): The calculation type. Possible values are "Total", "Running Total",
|
72
|
-
"Moving Average (3 Period)", and "Moving Average (7 Period)".
|
73
|
-
df (pandas.DataFrame): The filtered enrollments data.
|
74
|
-
type_ (str, optional): The type of calculation to perform. Default is "count".
|
75
|
-
|
76
|
-
Returns:
|
77
|
-
pandas.DataFrame: The aggregated data after performing the calculation.
|
78
|
-
"""
|
79
|
-
if type_ == "count":
|
80
|
-
if calc == "Total":
|
81
|
-
pass
|
82
|
-
elif calc == "Running Total":
|
83
|
-
df["count"] = df.groupby("enroll_type")["count"].cumsum()
|
84
|
-
elif calc == "Moving Average (3 Period)":
|
85
|
-
df["count"] = (
|
86
|
-
df.groupby("enroll_type")["count"]
|
87
|
-
.rolling(3)
|
88
|
-
.mean()
|
89
|
-
.droplevel(level=[0])
|
90
|
-
)
|
91
|
-
elif calc == "Moving Average (7 Period)":
|
92
|
-
df["count"] = (
|
93
|
-
df.groupby("enroll_type")["count"]
|
94
|
-
.rolling(7)
|
95
|
-
.mean()
|
96
|
-
.droplevel(level=[0])
|
97
|
-
)
|
98
|
-
elif type_ == "sum":
|
99
|
-
if calc == "Total":
|
100
|
-
pass
|
101
|
-
elif calc == "Running Total":
|
102
|
-
df["sum"] = df.groupby("enroll_type")["sum"].cumsum()
|
103
|
-
elif calc == "Moving Average (3 Period)":
|
104
|
-
df["sum"] = (
|
105
|
-
df.groupby("enroll_type")["sum"].rolling(3).mean().droplevel(level=[0])
|
106
|
-
)
|
107
|
-
elif calc == "Moving Average (7 Period)":
|
108
|
-
df["sum"] = (
|
109
|
-
df.groupby("enroll_type")["sum"].rolling(7).mean().droplevel(level=[0])
|
110
|
-
)
|
111
|
-
|
112
|
-
return df
|
113
|
-
|
114
|
-
|
115
|
-
def get_completions_over_time(start_date, end_date, dff, date_agg, calc):
|
116
|
-
"""Get agreggated data for completions over time graph.
|
117
|
-
|
118
|
-
Args:
|
119
|
-
start_date (datetime): The start date for the date filter.
|
120
|
-
end_date (datetime): The end date for the date filter.
|
121
|
-
dff (pandas.DataFrame): enrollments data
|
122
|
-
date_agg (str): It denotes the granularity of the aggregated date which can be Daily, Weekly, Monthly, Quarterly
|
123
|
-
calc (str): Calculations denoiated the period for the running averages. It can be Total, Running Total, Moving
|
124
|
-
Average (3 Period), Moving Average (7 Period)
|
125
|
-
"""
|
126
|
-
|
127
|
-
dff = dff[dff["has_passed"] == 1]
|
128
|
-
|
129
|
-
# Date filtering.
|
130
|
-
dff = date_filter(start=start_date, end=end_date, data_frame=dff, date_column="passed_date")
|
131
|
-
|
132
|
-
# Date aggregation.
|
133
|
-
dff = date_aggregation(
|
134
|
-
level=date_agg, group=["passed_date", "enroll_type"], date="passed_date", df=dff
|
135
|
-
)
|
136
|
-
|
137
|
-
# Calculating metric.
|
138
|
-
dff = calculation(calc=calc, df=dff)
|
139
|
-
|
140
|
-
return dff
|
141
|
-
|
142
|
-
|
143
|
-
def get_top_courses_by_completions(start_date, end_date, dff):
|
144
|
-
"""Get top 10 courses by completions.
|
145
|
-
|
146
|
-
Args:
|
147
|
-
start_date (datetime): The start date for the date filter.
|
148
|
-
end_date (datetime): The end date for the date filter.
|
149
|
-
dff (pandas.DataFrame): Enrollments data
|
150
|
-
"""
|
151
|
-
|
152
|
-
dff = dff[dff["has_passed"] == 1]
|
153
|
-
|
154
|
-
# Date filtering.
|
155
|
-
dff = date_filter(start=start_date, end=end_date, data_frame=dff, date_column="passed_date")
|
156
|
-
|
157
|
-
courses = list(
|
158
|
-
dff.groupby(["course_key"]).size().sort_values(ascending=False)[:10].index
|
159
|
-
)
|
160
|
-
|
161
|
-
dff = (
|
162
|
-
dff[dff.course_key.isin(courses)]
|
163
|
-
.groupby(["course_key", "course_title", "enroll_type"])
|
164
|
-
.size()
|
165
|
-
.reset_index()
|
166
|
-
)
|
167
|
-
dff.columns = ["course_key", "course_title", "enroll_type", "count"]
|
168
|
-
|
169
|
-
return dff
|
170
|
-
|
171
|
-
|
172
|
-
def get_top_subjects_by_completions(start_date, end_date, dff):
|
173
|
-
"""Get top 10 subjects by completions.
|
174
|
-
|
175
|
-
Args:
|
176
|
-
start_date (datetime): The start date for the date filter.
|
177
|
-
end_date (datetime): The end date for the date filter.
|
178
|
-
dff (pandas.DataFrame): Enrollments data
|
179
|
-
"""
|
180
|
-
|
181
|
-
dff = dff[dff["has_passed"] == 1]
|
182
|
-
|
183
|
-
# Date filtering.
|
184
|
-
dff = date_filter(start=start_date, end=end_date, data_frame=dff, date_column="passed_date")
|
185
|
-
|
186
|
-
subjects = list(
|
187
|
-
dff.groupby(["course_subject"]).size().sort_values(ascending=False)[:10].index
|
188
|
-
)
|
189
|
-
|
190
|
-
dff = (
|
191
|
-
dff[dff.course_subject.isin(subjects)]
|
192
|
-
.groupby(["course_subject", "enroll_type"])
|
193
|
-
.size()
|
194
|
-
.reset_index()
|
195
|
-
)
|
196
|
-
dff.columns = ["course_subject", "enroll_type", "count"]
|
197
|
-
|
198
|
-
return dff
|
199
|
-
|
200
|
-
|
201
|
-
def get_csv_data_for_completions_over_time(
|
202
|
-
start_date, end_date, enrollments, date_agg, calc
|
203
|
-
):
|
204
|
-
"""Get csv data for completions over time graph.
|
205
|
-
|
206
|
-
Args:
|
207
|
-
start_date (datetime): The start date for the date filter.
|
208
|
-
end_date (datetime): The end date for the date filter.
|
209
|
-
enrollments (pandas.DataFrame): Filtered enrollments data
|
210
|
-
date_agg (str): it denotes the granularity of the aggregated date which can be Daily, Weekly, Monthly, Quarterly
|
211
|
-
calc (str): calculations denoiated the period for the running averages. It can be Total, Running Total, Moving
|
212
|
-
Average (3 Period), Moving Average (7 Period)
|
213
|
-
|
214
|
-
Returns:
|
215
|
-
dict: csv data
|
216
|
-
"""
|
217
|
-
|
218
|
-
dff = get_completions_over_time(start_date, end_date, enrollments, date_agg, calc)
|
219
|
-
dff = dff.pivot(index="passed_date", columns="enroll_type", values="count")
|
220
|
-
filename = (
|
221
|
-
f"Completions Timeseries, {start_date} - {end_date} ({date_agg} {calc}).csv"
|
222
|
-
)
|
223
|
-
return {"filename": filename, "data": dff}
|
224
|
-
|
225
|
-
|
226
|
-
def get_csv_data_for_top_courses_by_completions(start_date, end_date, enrollments):
|
227
|
-
"""Get csv data for top 10 courses by completions.
|
228
|
-
|
229
|
-
Args:
|
230
|
-
start_date (datetime): The start date for the date filter.
|
231
|
-
end_date (datetime): The end date for the date filter.
|
232
|
-
enrollments (pandas.DataFrame): Filtered enrollments data
|
233
|
-
|
234
|
-
Returns:
|
235
|
-
dict: csv data
|
236
|
-
"""
|
237
|
-
|
238
|
-
dff = get_top_courses_by_completions(start_date, end_date, enrollments)
|
239
|
-
dff = dff.pivot(
|
240
|
-
index=["course_key", "course_title"], columns="enroll_type", values="count"
|
241
|
-
)
|
242
|
-
filename = f"Top 10 Courses by Completions, {start_date} - {end_date}.csv"
|
243
|
-
return {"filename": filename, "data": dff}
|
244
|
-
|
245
|
-
|
246
|
-
def get_csv_data_for_top_subjects_by_completions(start_date, end_date, enrollments):
|
247
|
-
"""Get csv data for top 10 subjects by completions.
|
248
|
-
|
249
|
-
Args:
|
250
|
-
start_date (datetime): The start date for the date filter.
|
251
|
-
end_date (datetime): The end date for the date filter.
|
252
|
-
enrollments (pandas.DataFrame): Filtered enrollments data
|
253
|
-
|
254
|
-
Returns:
|
255
|
-
dict: csv data
|
256
|
-
"""
|
257
|
-
|
258
|
-
dff = get_top_subjects_by_completions(start_date, end_date, enrollments)
|
259
|
-
dff = dff.pivot(index="course_subject", columns="enroll_type", values="count")
|
260
|
-
filename = f"Top 10 Subjects by Completions, {start_date} - {end_date}.csv"
|
261
|
-
return {"filename": filename, "data": dff}
|