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,4 +1,6 @@
1
- """Unitest for EnterpriseAdminCompletionsStatsView."""
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.utils import ChartType
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 TestCompletionstStatsAPI(JWTTestMixin, APITransactionTestCase):
24
- """Tests for EnterrpiseAdminCompletionsStatsView."""
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 = "ee5e6b3a-069a-4947-bb8d-d2dbc323396c"
40
+ self.enterprise_id = 'ee5e6b3a-069a-4947-bb8d-d2dbc323396c'
41
41
  self.set_jwt_cookie()
42
42
 
43
43
  self.url = reverse(
44
- "v1:enterprise-admin-analytics-completions-stats",
45
- kwargs={"enterprise_id": self.enterprise_id},
44
+ 'v1:enterprise-admin-analytics-completions-stats',
45
+ kwargs={'enterprise_uuid': self.enterprise_id},
46
46
  )
47
47
 
48
- fetch_max_enrollment_datetime_patcher = patch(
49
- 'enterprise_data.api.v1.views.enterprise_completions.fetch_max_enrollment_datetime',
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
- fetch_max_enrollment_datetime_patcher.start()
54
- self.addCleanup(fetch_max_enrollment_datetime_patcher.stop)
53
+ get_enrollment_date_range_patcher.start()
54
+ self.addCleanup(get_enrollment_date_range_patcher.stop)
55
55
 
56
56
  @patch(
57
- "enterprise_data.api.v1.views.enterprise_completions.fetch_and_cache_enrollments_data"
57
+ 'enterprise_data.api.v1.views.analytics_enrollments.FactEnrollmentAdminDashTable.'
58
+ 'get_top_subjects_by_completions'
58
59
  )
59
- def test_get(self, mock_fetch_and_cache_enrollments_data):
60
- """
61
- Test the GET method for the EnterrpiseAdminCompletionsStatsView works.
62
- """
63
- mock_fetch_and_cache_enrollments_data.return_value = enrollments_dataframe()
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 test_get_csv(self, chart_type, mock_fetch_and_cache_enrollments_data):
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 that EnterrpiseAdminCompletionsStatsView return correct CSV data.
74
+ Test the GET method to fetch charts data for enterprise completion works correctly.
113
75
  """
114
- mock_fetch_and_cache_enrollments_data.return_value = enrollments_dataframe()
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
- assert response["Content-Type"] == "text/csv"
126
- # verify the response content
127
- assert response.content == COMPLETIONS_STATS_CSVS[chart_type]
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 TestCompletionstAPI(JWTTestMixin, APITransactionTestCase):
132
- """Tests for EnterrpiseAdminCompletionsView."""
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 = "ee5e6b3a-069a-4947-bb8d-d2dbc323396c"
112
+ self.enterprise_id = 'ee5e6b3a-069a-4947-bb8d-d2dbc323396c'
149
113
  self.set_jwt_cookie()
150
114
 
151
115
  self.url = reverse(
152
- "v1:enterprise-admin-analytics-completions",
153
- kwargs={"enterprise_id": self.enterprise_id},
116
+ 'v1:enterprise-admin-analytics-completions',
117
+ kwargs={'enterprise_uuid': self.enterprise_id},
154
118
  )
155
119
 
156
- fetch_max_enrollment_datetime_patcher = patch(
157
- 'enterprise_data.api.v1.views.enterprise_completions.fetch_max_enrollment_datetime',
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
- fetch_max_enrollment_datetime_patcher.start()
162
- self.addCleanup(fetch_max_enrollment_datetime_patcher.stop)
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
- "enterprise_data.api.v1.views.enterprise_completions.fetch_and_cache_enrollments_data"
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 the EnterrpiseAdminCompletionsView works.
132
+ Test the GET method for fetching enterprise completions works correctly.
191
133
  """
192
- mock_fetch_and_cache_enrollments_data.return_value = enrollments_dataframe()
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, {"page": 1, "page_size": 1})
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=1"
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"] == 2
201
- assert data["count"] == 2
202
- self.verify_enrollment_data(data["results"], 1)
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}