edx-enterprise-data 10.11.1__py3-none-any.whl → 10.12.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: edx-enterprise-data
3
- Version: 10.11.1
3
+ Version: 10.12.0
4
4
  Summary: Enterprise Reporting
5
5
  Home-page: https://github.com/openedx/edx-enterprise-data
6
6
  Author: edX
@@ -1,5 +1,5 @@
1
- edx_enterprise_data-10.11.1.dist-info/licenses/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
2
- enterprise_data/__init__.py,sha256=j6aLs1tTZrhX0hqVukHt4A3LxK06eXBgt0DlKcYKKK4,125
1
+ edx_enterprise_data-10.12.0.dist-info/licenses/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
2
+ enterprise_data/__init__.py,sha256=eCPWfovt7oPmCOdH366B9g4bK-v2sL1oI2SGL8UqKx4,125
3
3
  enterprise_data/apps.py,sha256=aF6hZwDfI2oWj95tUTm_2ikHueQj-jLj-u0GrgzpsQI,414
4
4
  enterprise_data/clients.py,sha256=glw-fXu3V7rKEpiUBF056gZEdNK4KnWcP9tNYELimRQ,6566
5
5
  enterprise_data/constants.py,sha256=uCKjfpdlMYFZJsAj3n9RMw4Cmg5_6s3NuwocO-fch3s,238
@@ -16,12 +16,13 @@ enterprise_data/admin_analytics/constants.py,sha256=-6uLAq5DUeA_rv5eUb9SeqlG3iVW
16
16
  enterprise_data/admin_analytics/data_loaders.py,sha256=b6RjEIxCol8ETQMY7QfwhqN9eEAvrUN_UldIG7rgsSY,736
17
17
  enterprise_data/admin_analytics/database/__init__.py,sha256=vNSWKf2VV5xMegN7htJJtxtQEb0ASLC6frE2w0ZpYpE,104
18
18
  enterprise_data/admin_analytics/database/utils.py,sha256=5u-d6ZQW95mF_r4bH8Xdi7DgpYAuDFOG_q0P-bjKXHU,1712
19
- enterprise_data/admin_analytics/database/filters/__init__.py,sha256=ZteBm55429cn8zaUVYHRFA7InQPWP0b4XiZeq24HQT4,115
19
+ enterprise_data/admin_analytics/database/filters/__init__.py,sha256=FIgcEfVQPU0TrJoFhWJMo6WPFz4WnPAzxxkwTLfwtP8,186
20
20
  enterprise_data/admin_analytics/database/filters/base.py,sha256=nOek6XgHEVBKUTGk-K0PZpy0RSZB3D7kc8OD5nlqXcE,71
21
+ enterprise_data/admin_analytics/database/filters/fact_completion_admin_dash.py,sha256=j50oW-C-N2X3g6RsvnP9PHeiz8Z4qqe-SyjxeCzb6zQ,1208
21
22
  enterprise_data/admin_analytics/database/filters/fact_enrollment_admin_dash.py,sha256=WKRBWRCujiMay2ePdl9vnNN1ux-LO0edyb79W4dm-S4,1808
22
23
  enterprise_data/admin_analytics/database/queries/__init__.py,sha256=IC5TLOr_GnydbrVbl2mWhwO3aUbYeHuDmfPTLmwGhZA,218
23
24
  enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py,sha256=eLy9GT0gFGLbw8yZZdEaeCAw0pTEUsy0-Ud5g2T9T80,10836
24
- enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py,sha256=0YbIQvNL8pgi-apsoYzBSnDeouiqZIUlYzIH4xkbY_w,10942
25
+ enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py,sha256=n_RiqraVzYvCsgLaPmpSBUX3cyNEHjSnkNH7n35gC80,10609
25
26
  enterprise_data/admin_analytics/database/queries/skills_daily_rollup_admin_dash.py,sha256=ZaJPghTrQvBGsFxfpeR8EE45ujtaYI9R_xkoDDqD2So,4269
26
27
  enterprise_data/admin_analytics/database/query_filters/__init__.py,sha256=xW9cf5KGpMs33tTlil5gzKq4RxcZVCJZESsrHo2X0E4,182
27
28
  enterprise_data/admin_analytics/database/query_filters/base.py,sha256=XZUC1AIaDPBq0cwh2Xn5wp_DZfsD3HnMMvqFUt7iM2E,2205
@@ -31,7 +32,7 @@ enterprise_data/admin_analytics/database/query_filters/in_.py,sha256=RsqDVTgzJ0v
31
32
  enterprise_data/admin_analytics/database/tables/__init__.py,sha256=Z-c3P9hqR-dC9uYKe63qHkQG9Nms8cLE2jRN-4jeMM0,289
32
33
  enterprise_data/admin_analytics/database/tables/base.py,sha256=1KyKsC18pW3m-5U-T6pdt5rIwsz6Wp3QFFbD3r6L6YQ,395
33
34
  enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py,sha256=VMUGwfrUsYd_NnxEDNBovAmHwhdTCSImGA6JjHzknhk,12959
34
- enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py,sha256=okRav232A2TD2WzVsOq_HHOm3itp_-uJB6uUxypykLI,15864
35
+ enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py,sha256=yDqrt9SWvvBNb-v-7Sh-m2Vef2bsGRSsP-YKeLCAnSA,18411
35
36
  enterprise_data/admin_analytics/database/tables/skills_daily_rollup_admin_dash.py,sha256=3xNwSi0wfCyBHcXPd6-9Ujs1NUm8kmZRg_gPrZzp9nQ,3233
36
37
  enterprise_data/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
38
  enterprise_data/api/urls.py,sha256=POqc_KATHdnpMf9zHtpO46pKD5KAlAExtx7G6iylLcU,273
@@ -40,15 +41,15 @@ enterprise_data/api/v0/serializers.py,sha256=dngZTk6DhRxApchQKCMp1B_c8aVnQtH0NCq
40
41
  enterprise_data/api/v0/urls.py,sha256=vzJjqIo_S3AXWs9Us8XTaJc3FnxLbYzAkmLyuDQqum0,699
41
42
  enterprise_data/api/v0/views.py,sha256=4RslZ4NZOU-844bnebEQ71ji2utRY7jEijqC45oQQD0,14380
42
43
  enterprise_data/api/v1/__init__.py,sha256=1aAzAYU5hk-RW6cKUxa1645cbZMxn7GIZ7OMjWc9MKI,46
43
- enterprise_data/api/v1/serializers.py,sha256=U3zxiideqwpoJuofI0dkx0AwbN2GUiELxqJIXIVwI9s,13843
44
+ enterprise_data/api/v1/serializers.py,sha256=6yxUe_7UBh80e2ZPZIf1AUzzgtKM8-yS38Q4Katqbj8,13898
44
45
  enterprise_data/api/v1/urls.py,sha256=Wl1xLboPg-Lq1ZvjAWf51JKYkHlmx02Kpq1nwfDyS8s,4372
45
46
  enterprise_data/api/v1/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- enterprise_data/api/v1/views/analytics_completions.py,sha256=esFbJ5q8ssnm2Mfbc3rZXtiGHF-MeM4KQ4Ft3N7wwHU,6260
47
+ enterprise_data/api/v1/views/analytics_completions.py,sha256=ALkoUTgvaqCxpjnf3rkvwBT7-Hv7eM1RnCdRboCQgR8,6540
47
48
  enterprise_data/api/v1/views/analytics_engagements.py,sha256=Yo-bpA-0xOQHUPTFF0jHWxjm6KpSC-l2nGxhYX0ajBk,6298
48
49
  enterprise_data/api/v1/views/analytics_enrollments.py,sha256=_KBo4RhEmfQXcCOgvowD28WCVJwlc4APuCnRDbAYK9E,6563
49
50
  enterprise_data/api/v1/views/analytics_leaderboard.py,sha256=3dyo7_OhyGEEeibemBrRsUOo0jbM4LbDgV5gw3YnVig,4186
50
51
  enterprise_data/api/v1/views/base.py,sha256=Kkmd5zgEBAhvwS_GoGXSK6lgbDNwSPioYn-QbnizI3w,3416
51
- enterprise_data/api/v1/views/enterprise_admin.py,sha256=xxpb48cjuHkFJH-IQG0DwOvdH8RgyYD-aQjyfMMJNFg,9030
52
+ enterprise_data/api/v1/views/enterprise_admin.py,sha256=uaf8sSp_JS1T5Kgi4xNCjhu6A0HKa3Zwp6JMjY3okJk,9103
52
53
  enterprise_data/api/v1/views/enterprise_learner.py,sha256=vRzfaQCEoeq_tEY7KsNZJVzgn7XEWZ1l-n0oUoSVGyU,20093
53
54
  enterprise_data/api/v1/views/enterprise_offers.py,sha256=VifxgqTLFLVw4extYPlHcN1N_yjXcsYsAlYEnAbpb10,1266
54
55
  enterprise_data/cache/__init__.py,sha256=fiBUploll1kmDy2vCmnNpeZVTD4ewsgtRF14vVs0Rb4,1850
@@ -64,7 +65,7 @@ enterprise_data/management/commands/create_enterprise_learner_enrollment_lpr_v1.
64
65
  enterprise_data/management/commands/create_enterprise_learner_lpr_v1.py,sha256=bUYmZHA3yK3ZBPhV0wkpRDgH_Q2b5rVQnwSp2hRmh28,1799
65
66
  enterprise_data/management/commands/create_enterprise_offer.py,sha256=0R1eEKWTCGjod4I8qBH2UBD-erqj5mFtM_DG5Vxet_0,1150
66
67
  enterprise_data/management/commands/create_enterprise_user.py,sha256=V_kvOSPZ1DXfAdF1W3AwAtavEYjdYaHBjjfzndZP8lk,1498
67
- enterprise_data/management/commands/pre_warm_analytics_cache.py,sha256=i64hcbvqGW0UWYbx046iEETzCOcva1cdYumdb5RByGU,8522
68
+ enterprise_data/management/commands/pre_warm_analytics_cache.py,sha256=bwqocYMc72AlgXy_SbT0EL5Aa4sLtVLtYkWSLTZmio0,8696
68
69
  enterprise_data/management/commands/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
70
  enterprise_data/management/commands/tests/test_create_dummy_data_lpr_v1.py,sha256=wt9fqAFKQVTqllpZ42dch-n31JavUifUIB9CKNYcnYM,1086
70
71
  enterprise_data/management/commands/tests/test_create_enterprise_enrollment.py,sha256=5CABLk8qAx8RP8mrFtnbJ4-xVkf9-5Mq6iQcx8jBfFc,1742
@@ -144,7 +145,7 @@ enterprise_data/tests/api/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
144
145
  enterprise_data/tests/api/v1/test_serializers.py,sha256=DwgEHcyOP3oqNUPB2O-NkJGeO_cYs9XJiq7791vJLZE,3682
145
146
  enterprise_data/tests/api/v1/test_views.py,sha256=rLqUHfar0HdBNtz33hQxd_0qUUgr7Ku3KwQSQ1B4Ypg,15213
146
147
  enterprise_data/tests/api/v1/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
147
- enterprise_data/tests/api/v1/views/test_enterprise_admin.py,sha256=OxeCL_-wotTOoe7cQxJTFDNataBRn5y8ei6GM5-EklA,11052
148
+ enterprise_data/tests/api/v1/views/test_enterprise_admin.py,sha256=6SXw7LcMaE-M2VSX8PHaWmOHDGXlqbI2ahNHqyBuPOk,11161
148
149
  enterprise_data_roles/__init__.py,sha256=toCpbypm2uDoWVw29_em9gPFNly8vNUS__C0b4TCqEg,112
149
150
  enterprise_data_roles/admin.py,sha256=QNP0VeWE092vZzpyxOA5UJK1nNGl5e71B1J0RCwo_nU,998
150
151
  enterprise_data_roles/apps.py,sha256=nKi8TyuQ5Q6WGtKs5QeXvUTc3N-YQjKhyBnm2EM3Bng,260
@@ -178,7 +179,7 @@ enterprise_reporting/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5N
178
179
  enterprise_reporting/fixtures/enterprise_customer_reporting.json,sha256=nS6E9KHW0Iqk7ZHtTyyVyrztIXxjn9OtBvMJkn7owxc,3959
179
180
  enterprise_reporting/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
180
181
  enterprise_reporting/tests/test_clients.py,sha256=h-h7xBJ6wIBKP-QqRxcJJGiQxLfTOLYByCuWfcCeCy0,8474
181
- enterprise_reporting/tests/test_delivery_method.py,sha256=_nvpxPjYb59ix0nsgX4Ani0gJ0mgcBCgfHpw0rS0scc,4561
182
+ enterprise_reporting/tests/test_delivery_method.py,sha256=p7OWkHKzl-iiA8jHXoo7TIRQees-AUryKzIq-t2MtwI,4561
182
183
  enterprise_reporting/tests/test_enterprise_client.py,sha256=lpWm0muvA3alRjmlRAezE5901C9DU3WiySH4D5-U3qE,1058
183
184
  enterprise_reporting/tests/test_external_link_report.py,sha256=zdnVOD1qtAp9c5EbIPnD9jcoLtW4iKs7gSVklgBK328,7029
184
185
  enterprise_reporting/tests/test_reporter.py,sha256=PTmkGvPjGEjxiyizL88LAKnaWdvZDgOBjL4QStfOdyw,4057
@@ -186,7 +187,7 @@ enterprise_reporting/tests/test_send_enterprise_reports.py,sha256=zBj7sDvRLJQbRs
186
187
  enterprise_reporting/tests/test_utils.py,sha256=y4t6w9aKra-ftqtUeHvPwOhje-1npz7auV5o74ya8fE,9523
187
188
  enterprise_reporting/tests/test_vertica_client.py,sha256=-R2yNCGUjRtoXwLMBloVFQkFYrJoo613VCr61gwI3kQ,140
188
189
  enterprise_reporting/tests/utils.py,sha256=xms2LM7DV3wczXEfctOK1ddel1EE0J_YSr17UzbCDy4,1401
189
- edx_enterprise_data-10.11.1.dist-info/METADATA,sha256=qGem3YioOvmd2BpaHm7xLHVjzDkgWIHl-eW5jn3r_o0,1707
190
- edx_enterprise_data-10.11.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
191
- edx_enterprise_data-10.11.1.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
192
- edx_enterprise_data-10.11.1.dist-info/RECORD,,
190
+ edx_enterprise_data-10.12.0.dist-info/METADATA,sha256=ug0VdbHtFCW6mtrxcaDxw6lYa72hnc6B_gMYHfAg8xA,1707
191
+ edx_enterprise_data-10.12.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
192
+ edx_enterprise_data-10.12.0.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
193
+ edx_enterprise_data-10.12.0.dist-info/RECORD,,
@@ -2,4 +2,4 @@
2
2
  Enterprise data api application. This Django app exposes API endpoints used by enterprises.
3
3
  """
4
4
 
5
- __version__ = "10.11.1"
5
+ __version__ = "10.12.0"
@@ -2,4 +2,5 @@
2
2
  Query Filters for database tables.
3
3
  """
4
4
 
5
+ from .fact_completion_admin_dash import FactCompletionAdminDashFilters
5
6
  from .fact_enrollment_admin_dash import FactEnrollmentAdminDashFilters
@@ -0,0 +1,36 @@
1
+ """
2
+ Query filters for enrollments table.
3
+ """
4
+ from enterprise_data.admin_analytics.database.filters.base import BaseFilter
5
+ from enterprise_data.admin_analytics.database.query_filters import BetweenQueryFilter, EqualQueryFilter
6
+
7
+
8
+ class FactCompletionAdminDashFilters(BaseFilter):
9
+ """
10
+ Query filters for completions data in enrollments table.
11
+ """
12
+ @staticmethod
13
+ def passed_date_range_filter(
14
+ start_date_params_key: str, end_date_params_key: str
15
+ ) -> BetweenQueryFilter:
16
+ """
17
+ Filter by passed date to be in the given range.
18
+
19
+ Arguments:
20
+ start_date_params_key (str): The start date key against which value will be passed in the query.
21
+ end_date_params_key (str): The end date key against which value will be passed in the query.
22
+ """
23
+ return BetweenQueryFilter(
24
+ column='passed_date',
25
+ range_placeholders=(start_date_params_key, end_date_params_key),
26
+ )
27
+
28
+ @staticmethod
29
+ def has_passed_filter() -> EqualQueryFilter:
30
+ """
31
+ Filter by has passed with fixed value 1.
32
+ """
33
+ return EqualQueryFilter(
34
+ column='has_passed',
35
+ value=1
36
+ )
@@ -70,17 +70,15 @@ class FactEnrollmentAdminDashQueries:
70
70
  """
71
71
 
72
72
  @staticmethod
73
- def get_completion_count_query():
73
+ def get_completion_count_query(query_filters: QueryFilters) -> str:
74
74
  """
75
75
  Get the query to fetch the completion count.
76
76
  """
77
- return """
77
+ return f"""
78
78
  SELECT
79
79
  SUM(has_passed) as completions
80
80
  FROM fact_enrollment_admin_dash
81
- WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
82
- has_passed=1 AND
83
- passed_date BETWEEN %(start_date)s AND %(end_date)s;
81
+ WHERE {query_filters.to_sql()};
84
82
  """
85
83
 
86
84
  @staticmethod
@@ -94,7 +92,7 @@ class FactEnrollmentAdminDashQueries:
94
92
  FROM fact_engagement_admin_dash
95
93
  WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
96
94
  activity_date BETWEEN %(start_date)s AND %(end_date)s;
97
- """
95
+ """
98
96
 
99
97
  @staticmethod
100
98
  def get_top_courses_by_enrollments_query(query_filters: QueryFilters, record_count: int = 10) -> str:
@@ -183,27 +181,28 @@ class FactEnrollmentAdminDashQueries:
183
181
  """
184
182
 
185
183
  @staticmethod
186
- def get_all_completions_query():
184
+ def get_all_completions_query(
185
+ query_filters: QueryFilters,
186
+ ) -> str:
187
187
  """
188
188
  Get the query to fetch all completions.
189
189
  """
190
- return """
190
+ return f"""
191
191
  SELECT email, course_title, course_subject, enroll_type, passed_date
192
192
  FROM fact_enrollment_admin_dash
193
- WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
194
- has_passed=1 AND
195
- passed_date BETWEEN %(start_date)s AND %(end_date)s
193
+ WHERE {query_filters.to_sql()}
196
194
  ORDER BY passed_date DESC LIMIT %(limit)s OFFSET %(offset)s
197
195
  """
198
196
 
199
197
  @staticmethod
200
- def get_top_courses_by_completions_query(record_count=10):
198
+ def get_top_courses_by_completions_query(query_filters: QueryFilters, record_count=10) -> str:
201
199
  """
202
200
  Get the query to fetch the completion count by courses.
203
201
 
204
202
  Query should fetch the top N courses by completion count. Where N is the value of record_count.
205
203
 
206
204
  Arguments:
205
+ query_filters (QueryFilters): List of query filters.
207
206
  record_count (int): Number of records to fetch.
208
207
 
209
208
  Returns:
@@ -217,9 +216,7 @@ class FactEnrollmentAdminDashQueries:
217
216
  enroll_type,
218
217
  passed_date
219
218
  FROM fact_enrollment_admin_dash
220
- WHERE has_passed = 1 AND
221
- enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
222
- passed_date BETWEEN %(start_date)s AND %(end_date)s
219
+ WHERE {query_filters.to_sql()}
223
220
  ),
224
221
  top_10_courses AS (
225
222
  SELECT
@@ -243,13 +240,14 @@ class FactEnrollmentAdminDashQueries:
243
240
  """
244
241
 
245
242
  @staticmethod
246
- def get_top_subjects_by_completions_query(record_count=10):
243
+ def get_top_subjects_by_completions_query(query_filters: QueryFilters, record_count=10) -> str:
247
244
  """
248
245
  Get the query to fetch the completion count by subjects.
249
246
 
250
247
  Query should fetch the top N subjects by completion count. Where N is the value of record_count.
251
248
 
252
249
  Arguments:
250
+ query_filters (QueryFilters): List of query filters.
253
251
  record_count (int): Number of records to fetch.
254
252
 
255
253
  Returns:
@@ -262,9 +260,7 @@ class FactEnrollmentAdminDashQueries:
262
260
  enroll_type,
263
261
  passed_date
264
262
  FROM fact_enrollment_admin_dash
265
- WHERE has_passed = 1 AND
266
- enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
267
- passed_date BETWEEN %(start_date)s AND %(end_date)s
263
+ WHERE {query_filters.to_sql()}
268
264
  ),
269
265
  top_10_subjects AS (
270
266
  SELECT
@@ -287,7 +283,9 @@ class FactEnrollmentAdminDashQueries:
287
283
  """
288
284
 
289
285
  @staticmethod
290
- def get_completions_time_series_data_query():
286
+ def get_completions_time_series_data_query(
287
+ query_filters: QueryFilters,
288
+ ) -> str:
291
289
  """
292
290
  Get the query to fetch the completion time series data.
293
291
 
@@ -296,12 +294,10 @@ class FactEnrollmentAdminDashQueries:
296
294
  Returns:
297
295
  (str): Query to fetch the completion time series data.
298
296
  """
299
- return """
297
+ return f"""
300
298
  SELECT passed_date, enroll_type, count(course_key) as completion_count
301
299
  FROM fact_enrollment_admin_dash
302
- WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
303
- has_passed=1 AND
304
- passed_date BETWEEN %(start_date)s AND %(end_date)s
300
+ WHERE {query_filters.to_sql()}
305
301
  GROUP BY passed_date, enroll_type
306
302
  ORDER BY passed_date;
307
303
  """
@@ -10,7 +10,7 @@ from enterprise_data.cache.decorators import cache_it
10
10
  from enterprise_data.clients import EnterpriseApiClient
11
11
  from enterprise_data.exceptions import EnterpriseApiClientException
12
12
 
13
- from ..filters import FactEnrollmentAdminDashFilters
13
+ from ..filters import FactCompletionAdminDashFilters, FactEnrollmentAdminDashFilters
14
14
  from ..queries import FactEnrollmentAdminDashQueries
15
15
  from ..query_filters import INQueryFilter, QueryFilters
16
16
  from ..utils import run_query
@@ -25,6 +25,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
25
25
  """
26
26
  queries = FactEnrollmentAdminDashQueries()
27
27
  filters = FactEnrollmentAdminDashFilters()
28
+ completion_filters = FactCompletionAdminDashFilters()
28
29
 
29
30
  def __get_enterprise_user_query_filter( # pylint: disable=inconsistent-return-statements
30
31
  self, group_uuid: Optional[UUID],
@@ -92,6 +93,45 @@ class FactEnrollmentAdminDashTable(BaseTable):
92
93
 
93
94
  return query_filters, params
94
95
 
96
+ def __get_common_query_filters_for_completion(
97
+ self, enterprise_customer_uuid: UUID,
98
+ group_uuid: Optional[UUID],
99
+ start_date: date,
100
+ end_date: date,
101
+ ) -> Tuple[QueryFilters, dict]:
102
+ """
103
+ Utility method to get query filters common in most usages below.
104
+
105
+ This will return a tuple containing the query filters list and the dictionary of query parameters that
106
+ will be used in the query.
107
+
108
+ It will contain the following query filters.
109
+ 1. enterprise_customer_uuid filter to filter records for an enterprise customer.
110
+ 2. enrollment_date range filter to filter records by enrollment date.
111
+ 3. group_uuid filter to filter records for learners who belong to the given group.
112
+ 4. has_passed filter to filter records for successful completions.
113
+ """
114
+ query_filters = QueryFilters([
115
+ self.filters.enterprise_customer_uuid_filter('enterprise_customer_uuid'),
116
+ self.completion_filters.passed_date_range_filter('start_date', 'end_date'),
117
+ self.completion_filters.has_passed_filter(),
118
+ ])
119
+ params = {
120
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
121
+ 'start_date': start_date,
122
+ 'end_date': end_date,
123
+ }
124
+
125
+ response = self.__get_enterprise_user_query_filter(
126
+ group_uuid, enterprise_customer_uuid
127
+ )
128
+ if response is not None:
129
+ enterprise_user_id_in_filter, enterprise_user_id_params = response
130
+ query_filters.append(enterprise_user_id_in_filter)
131
+ params.update(enterprise_user_id_params)
132
+
133
+ return query_filters, params
134
+
95
135
  def get_top_enterprises(self, count=10):
96
136
  """
97
137
  Get the top enterprises by enrollments.
@@ -232,27 +272,31 @@ class FactEnrollmentAdminDashTable(BaseTable):
232
272
  return tuple(results[0])
233
273
 
234
274
  @cache_it()
235
- def get_completion_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
275
+ def get_completion_count(
276
+ self, enterprise_customer_uuid: UUID, group_uuid: Optional[UUID], start_date: date, end_date: date
277
+ ):
236
278
  """
237
279
  Get the completion count for the given enterprise customer.
238
280
 
239
281
  Arguments:
240
282
  enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
283
+ group_uuid (UUID): The UUID of the group.
241
284
  start_date (date): The start date.
242
285
  end_date (date): The end date.
243
286
 
244
287
  Returns:
245
288
  int: The completion count.
246
289
  """
290
+ query_filters, query_filter_params = self.__get_common_query_filters_for_completion(
291
+ enterprise_customer_uuid, group_uuid, start_date, end_date
292
+ )
247
293
  results = run_query(
248
- query=self.queries.get_completion_count_query(),
249
- params={
250
- 'enterprise_customer_uuid': enterprise_customer_uuid,
251
- 'start_date': start_date,
252
- 'end_date': end_date,
253
- }
294
+ query=self.queries.get_completion_count_query(query_filters),
295
+ params=query_filter_params,
296
+ as_dict=False,
254
297
  )
255
- if not results:
298
+ # Handle empty results defensively
299
+ if not results or not results[0] or len(results[0]) == 0:
256
300
  return 0
257
301
 
258
302
  return int(results[0][0] or 0)
@@ -347,7 +391,13 @@ class FactEnrollmentAdminDashTable(BaseTable):
347
391
 
348
392
  @cache_it()
349
393
  def get_all_completions(
350
- self, enterprise_customer_uuid: UUID, start_date: date, end_date: date, limit: int, offset: int
394
+ self,
395
+ enterprise_customer_uuid: UUID,
396
+ group_uuid: Optional[UUID],
397
+ start_date: date,
398
+ end_date: date,
399
+ limit: int,
400
+ offset: int,
351
401
  ):
352
402
  """
353
403
  Get all completions for the given enterprise customer.
@@ -355,6 +405,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
355
405
  Arguments:
356
406
  enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
357
407
  start_date (date): The start date.
408
+ group_uuid (UUID): The UUID of the group.
358
409
  end_date (date): The end date.
359
410
  limit (int): The maximum number of records to return.
360
411
  offset (int): The number of records to skip.
@@ -362,12 +413,13 @@ class FactEnrollmentAdminDashTable(BaseTable):
362
413
  Returns:
363
414
  list<dict>: A list of dictionaries containing the completions data.
364
415
  """
416
+ query_filters, query_filter_params = self.__get_common_query_filters_for_completion(
417
+ enterprise_customer_uuid, group_uuid, start_date, end_date
418
+ )
365
419
  return run_query(
366
- query=self.queries.get_all_completions_query(),
420
+ query=self.queries.get_all_completions_query(query_filters),
367
421
  params={
368
- 'enterprise_customer_uuid': enterprise_customer_uuid,
369
- 'start_date': start_date,
370
- 'end_date': end_date,
422
+ **query_filter_params,
371
423
  'limit': limit,
372
424
  'offset': offset,
373
425
  },
@@ -375,7 +427,9 @@ class FactEnrollmentAdminDashTable(BaseTable):
375
427
  )
376
428
 
377
429
  @cache_it()
378
- def get_top_courses_by_completions(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
430
+ def get_top_courses_by_completions(
431
+ self, enterprise_customer_uuid: UUID, group_uuid: Optional[UUID], start_date: date, end_date: date
432
+ ):
379
433
  """
380
434
  Get the top courses by completion for the given enterprise customer.
381
435
 
@@ -387,18 +441,19 @@ class FactEnrollmentAdminDashTable(BaseTable):
387
441
  Returns:
388
442
  list<dict>: A list of dictionaries containing the course key, course_title and completion count.
389
443
  """
444
+ query_filters, query_filter_params = self.__get_common_query_filters_for_completion(
445
+ enterprise_customer_uuid, group_uuid, start_date, end_date
446
+ )
390
447
  return run_query(
391
- query=self.queries.get_top_courses_by_completions_query(),
392
- params={
393
- 'enterprise_customer_uuid': enterprise_customer_uuid,
394
- 'start_date': start_date,
395
- 'end_date': end_date,
396
- },
448
+ query=self.queries.get_top_courses_by_completions_query(query_filters),
449
+ params=query_filter_params,
397
450
  as_dict=True,
398
451
  )
399
452
 
400
453
  @cache_it()
401
- def get_top_subjects_by_completions(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
454
+ def get_top_subjects_by_completions(
455
+ self, enterprise_customer_uuid: UUID, group_uuid: Optional[UUID], start_date: date, end_date: date
456
+ ):
402
457
  """
403
458
  Get the top subjects by completions for the given enterprise customer.
404
459
 
@@ -410,35 +465,36 @@ class FactEnrollmentAdminDashTable(BaseTable):
410
465
  Returns:
411
466
  list<dict>: A list of dictionaries containing the subject and completion count.
412
467
  """
468
+ query_filters, query_filter_params = self.__get_common_query_filters_for_completion(
469
+ enterprise_customer_uuid, group_uuid, start_date, end_date
470
+ )
413
471
  return run_query(
414
- query=self.queries.get_top_subjects_by_completions_query(),
415
- params={
416
- 'enterprise_customer_uuid': enterprise_customer_uuid,
417
- 'start_date': start_date,
418
- 'end_date': end_date,
419
- },
472
+ query=self.queries.get_top_subjects_by_completions_query(query_filters),
473
+ params=query_filter_params,
420
474
  as_dict=True,
421
475
  )
422
476
 
423
477
  @cache_it()
424
- def get_completions_time_series_data(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
478
+ def get_completions_time_series_data(
479
+ self, enterprise_customer_uuid: UUID, group_uuid: Optional[UUID], start_date: date, end_date: date
480
+ ):
425
481
  """
426
482
  Get the completions time series data for the given enterprise customer.
427
483
 
428
484
  Arguments:
429
485
  enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
486
+ group_uuid (UUID): The UUID of the group.
430
487
  start_date (date): The start date.
431
488
  end_date (date): The end date.
432
489
 
433
490
  Returns:
434
491
  list<dict>: A list of dictionaries containing the date and completion count.
435
492
  """
493
+ query_filters, query_filter_params = self.__get_common_query_filters_for_completion(
494
+ enterprise_customer_uuid, group_uuid, start_date, end_date
495
+ )
436
496
  return run_query(
437
- query=self.queries.get_completions_time_series_data_query(),
438
- params={
439
- 'enterprise_customer_uuid': enterprise_customer_uuid,
440
- 'start_date': start_date,
441
- 'end_date': end_date,
442
- },
497
+ query=self.queries.get_completions_time_series_data_query(query_filters),
498
+ params=query_filter_params,
443
499
  as_dict=True,
444
500
  )
@@ -262,6 +262,7 @@ class AdminAnalyticsAggregatesQueryParamsSerializer(serializers.Serializer): #
262
262
  response_type = serializers.CharField(required=False)
263
263
  page = serializers.IntegerField(required=False)
264
264
  chart_type = serializers.CharField(required=False)
265
+ group_uuid = serializers.UUIDField(required=False)
265
266
 
266
267
  def validate(self, attrs):
267
268
  """
@@ -51,10 +51,12 @@ class AdvanceAnalyticsCompletionsView(AnalyticsPaginationMixin, ViewSet):
51
51
  # get values from query params or use default values
52
52
  start_date = serializer.data.get('start_date', min_enrollment_date)
53
53
  end_date = serializer.data.get('end_date', date.today())
54
+ group_uuid = serializer.data.get('group_uuid')
54
55
  page = serializer.data.get('page', 1)
55
56
  page_size = serializer.data.get('page_size', 100)
56
57
  completions = FactEnrollmentAdminDashTable().get_all_completions(
57
58
  enterprise_customer_uuid=enterprise_uuid,
59
+ group_uuid=group_uuid,
58
60
  start_date=start_date,
59
61
  end_date=end_date,
60
62
  limit=page_size,
@@ -62,6 +64,7 @@ class AdvanceAnalyticsCompletionsView(AnalyticsPaginationMixin, ViewSet):
62
64
  )
63
65
  total_count = FactEnrollmentAdminDashTable().get_completion_count(
64
66
  enterprise_customer_uuid=enterprise_uuid,
67
+ group_uuid=group_uuid,
65
68
  start_date=start_date,
66
69
  end_date=end_date,
67
70
  )
@@ -79,7 +82,7 @@ class AdvanceAnalyticsCompletionsView(AnalyticsPaginationMixin, ViewSet):
79
82
 
80
83
  return StreamingHttpResponse(
81
84
  IndividualCompletionsCSVRenderer().render(self._stream_serialized_data(
82
- enterprise_uuid, start_date, end_date, total_count
85
+ enterprise_uuid, group_uuid, start_date, end_date, total_count
83
86
  )),
84
87
  content_type="text/csv",
85
88
  headers={"Content-Disposition": f'attachment; filename="{filename}"'},
@@ -94,7 +97,7 @@ class AdvanceAnalyticsCompletionsView(AnalyticsPaginationMixin, ViewSet):
94
97
  )
95
98
 
96
99
  @staticmethod
97
- def _stream_serialized_data(enterprise_uuid, start_date, end_date, total_count, page_size=50000):
100
+ def _stream_serialized_data(enterprise_uuid, group_uuid, start_date, end_date, total_count, page_size=50000):
98
101
  """
99
102
  Stream the serialized data.
100
103
  """
@@ -102,6 +105,7 @@ class AdvanceAnalyticsCompletionsView(AnalyticsPaginationMixin, ViewSet):
102
105
  while offset < total_count:
103
106
  completions = FactEnrollmentAdminDashTable().get_all_completions(
104
107
  enterprise_customer_uuid=enterprise_uuid,
108
+ group_uuid=group_uuid,
105
109
  start_date=start_date,
106
110
  end_date=end_date,
107
111
  limit=page_size,
@@ -133,16 +137,18 @@ class AdvanceAnalyticsCompletionsView(AnalyticsPaginationMixin, ViewSet):
133
137
  # get values from query params or use default
134
138
  start_date = serializer.data.get('start_date', min_enrollment_date)
135
139
  end_date = serializer.data.get('end_date', date.today())
140
+ group_uuid = serializer.data.get('group_uuid')
141
+
136
142
  with timer('construct_completion_all_stats'):
137
143
  data = {
138
144
  'completions_over_time': FactEnrollmentAdminDashTable().get_completions_time_series_data(
139
- enterprise_uuid, start_date, end_date
145
+ enterprise_uuid, group_uuid, start_date, end_date
140
146
  ),
141
147
  'top_courses_by_completions': FactEnrollmentAdminDashTable().get_top_courses_by_completions(
142
- enterprise_uuid, start_date, end_date,
148
+ enterprise_uuid, group_uuid, start_date, end_date,
143
149
  ),
144
150
  'top_subjects_by_completions': FactEnrollmentAdminDashTable().get_top_subjects_by_completions(
145
- enterprise_uuid, start_date, end_date,
151
+ enterprise_uuid, group_uuid, start_date, end_date,
146
152
  ),
147
153
  }
148
154
  return Response(data)
@@ -108,12 +108,13 @@ class EnterpriseAdminAnalyticsAggregatesView(APIView):
108
108
  'start_date', min_enrollment_date
109
109
  )
110
110
  end_date = serializer.data.get('end_date', datetime.today())
111
+ group_uuid = serializer.data.get('group_uuid', None)
111
112
 
112
113
  enrolls, courses = FactEnrollmentAdminDashTable().get_enrollment_and_course_count(
113
114
  enterprise_id, start_date, end_date,
114
115
  )
115
116
  completions = FactEnrollmentAdminDashTable().get_completion_count(
116
- enterprise_id, start_date, end_date,
117
+ enterprise_id, group_uuid, start_date, end_date,
117
118
  )
118
119
  hours, sessions = FactEngagementAdminDashTable().get_learning_hours_and_daily_sessions(
119
120
  enterprise_id, start_date, end_date,
@@ -56,6 +56,7 @@ class Command(BaseCommand):
56
56
  )
57
57
  enterprise_enrollment_table.get_completion_count(
58
58
  enterprise_customer_uuid=enterprise_customer_uuid,
59
+ group_uuid=None,
59
60
  start_date=start_date,
60
61
  end_date=end_date,
61
62
  )
@@ -95,6 +96,7 @@ class Command(BaseCommand):
95
96
  page_size = 100
96
97
  enterprise_enrollment_table.get_all_completions(
97
98
  enterprise_customer_uuid=enterprise_customer_uuid,
99
+ group_uuid=None,
98
100
  start_date=start_date,
99
101
  end_date=end_date,
100
102
  limit=page_size,
@@ -102,21 +104,25 @@ class Command(BaseCommand):
102
104
  )
103
105
  enterprise_enrollment_table.get_completion_count(
104
106
  enterprise_customer_uuid=enterprise_customer_uuid,
107
+ group_uuid=None,
105
108
  start_date=start_date,
106
109
  end_date=end_date,
107
110
  )
108
111
  enterprise_enrollment_table.get_top_courses_by_completions(
109
112
  enterprise_customer_uuid=enterprise_customer_uuid,
113
+ group_uuid=None,
110
114
  start_date=start_date,
111
115
  end_date=end_date,
112
116
  )
113
117
  enterprise_enrollment_table.get_top_subjects_by_completions(
114
118
  enterprise_customer_uuid=enterprise_customer_uuid,
119
+ group_uuid=None,
115
120
  start_date=start_date,
116
121
  end_date=end_date,
117
122
  )
118
123
  enterprise_enrollment_table.get_completions_time_series_data(
119
124
  enterprise_customer_uuid=enterprise_customer_uuid,
125
+ group_uuid=None,
120
126
  start_date=start_date,
121
127
  end_date=end_date,
122
128
  )
@@ -62,6 +62,10 @@ class TestEnterpriseAdminAnalyticsAggregatesView(JWTTestMixin, APITransactionTes
62
62
  """
63
63
  mock implementation of run_query.
64
64
  """
65
+ # Check if this is the completion_count_query being called with query_filters
66
+ if hasattr(query, '__name__') and query.__name__ == 'get_completion_count_query':
67
+ return [[50]]
68
+
65
69
  mock_responses = {
66
70
  self.enrollment_queries.get_enrollment_date_range_query(): [[
67
71
  datetime.strptime('2021-01-01', "%Y-%m-%d"),
@@ -70,15 +74,12 @@ class TestEnterpriseAdminAnalyticsAggregatesView(JWTTestMixin, APITransactionTes
70
74
  self.enrollment_queries.get_enrollment_and_course_count_query(): [[
71
75
  100, 10
72
76
  ]],
73
- self.enrollment_queries.get_completion_count_query(): [[
74
- 50
75
- ]],
76
77
  self.engagement_queries.get_learning_hours_and_daily_sessions_query(): [[
77
78
  100, 10
78
79
  ]],
79
80
  'SELECT MAX(created) FROM enterprise_learner_enrollment': [[datetime.strptime('2021-01-01', "%Y-%m-%d")]]
80
81
  }
81
- return mock_responses[query]
82
+ return mock_responses.get(query, [[]])
82
83
 
83
84
  def test_get_admin_analytics_aggregates(self):
84
85
  """
@@ -5,8 +5,8 @@ Test delivery methods.
5
5
  import unittest
6
6
 
7
7
  import ddt
8
+ from mock import MagicMock, patch
8
9
 
9
- from mock import patch, MagicMock
10
10
  from enterprise_reporting.delivery_method import SFTPDeliveryMethod, SMTPDeliveryMethod
11
11
  from enterprise_reporting.utils import encrypt_string
12
12