edx-enterprise-data 10.12.0__py3-none-any.whl → 10.14.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.12.0
3
+ Version: 10.14.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.12.0.dist-info/licenses/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
2
- enterprise_data/__init__.py,sha256=eCPWfovt7oPmCOdH366B9g4bK-v2sL1oI2SGL8UqKx4,125
1
+ edx_enterprise_data-10.14.0.dist-info/licenses/LICENSE,sha256=dql8h4yceoMhuzlcK0TT_i-NgTFNIZsgE47Q4t3dUYI,34520
2
+ enterprise_data/__init__.py,sha256=y4Uerdix66ZKv0ma98I9YNBuEs_a4mbrItRkMxGHljY,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,14 @@ 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=FIgcEfVQPU0TrJoFhWJMo6WPFz4WnPAzxxkwTLfwtP8,186
19
+ enterprise_data/admin_analytics/database/filters/__init__.py,sha256=qzRLCsLTocvaFKpHIB3juyVsxaQ3ByO3YNeSAYkUsHc,257
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
22
- enterprise_data/admin_analytics/database/filters/fact_enrollment_admin_dash.py,sha256=WKRBWRCujiMay2ePdl9vnNN1ux-LO0edyb79W4dm-S4,1808
21
+ enterprise_data/admin_analytics/database/filters/fact_completion_admin_dash.py,sha256=pklKo12Vxm3rfNGE4qUiFmb9mZ526MhONPDzoJ-o0DM,679
22
+ enterprise_data/admin_analytics/database/filters/fact_engagement_admin_dash.py,sha256=ljdCbRbPS26qJaG6RnqepqwFgiE9GIQnR1uyQRlU5RM,334
23
+ enterprise_data/admin_analytics/database/filters/fact_enrollment_admin_dash.py,sha256=X7aVTii8hS49rSRf8EtZZKxsSZR64tX3TsEsLiHfVUg,338
24
+ enterprise_data/admin_analytics/database/filters/mixins.py,sha256=FOtOKvcugQC8eofli8Pf2vi8EKKXoWHem1CVaLw7moA,3064
23
25
  enterprise_data/admin_analytics/database/queries/__init__.py,sha256=IC5TLOr_GnydbrVbl2mWhwO3aUbYeHuDmfPTLmwGhZA,218
24
- enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py,sha256=eLy9GT0gFGLbw8yZZdEaeCAw0pTEUsy0-Ud5g2T9T80,10836
26
+ enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py,sha256=RejAfiNGr0i3wyNe5bsqFOcdRKCGp-tbqS04ibgLi5I,10393
25
27
  enterprise_data/admin_analytics/database/queries/fact_enrollment_admin_dash.py,sha256=n_RiqraVzYvCsgLaPmpSBUX3cyNEHjSnkNH7n35gC80,10609
26
28
  enterprise_data/admin_analytics/database/queries/skills_daily_rollup_admin_dash.py,sha256=ZaJPghTrQvBGsFxfpeR8EE45ujtaYI9R_xkoDDqD2So,4269
27
29
  enterprise_data/admin_analytics/database/query_filters/__init__.py,sha256=xW9cf5KGpMs33tTlil5gzKq4RxcZVCJZESsrHo2X0E4,182
@@ -31,8 +33,8 @@ enterprise_data/admin_analytics/database/query_filters/equal.py,sha256=Z4OPVSZmo
31
33
  enterprise_data/admin_analytics/database/query_filters/in_.py,sha256=RsqDVTgzJ0vvEMmtyefPNZh8U3eShLp7ZqY_J5oC45E,802
32
34
  enterprise_data/admin_analytics/database/tables/__init__.py,sha256=Z-c3P9hqR-dC9uYKe63qHkQG9Nms8cLE2jRN-4jeMM0,289
33
35
  enterprise_data/admin_analytics/database/tables/base.py,sha256=1KyKsC18pW3m-5U-T6pdt5rIwsz6Wp3QFFbD3r6L6YQ,395
34
- enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py,sha256=VMUGwfrUsYd_NnxEDNBovAmHwhdTCSImGA6JjHzknhk,12959
35
- enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py,sha256=yDqrt9SWvvBNb-v-7Sh-m2Vef2bsGRSsP-YKeLCAnSA,18411
36
+ enterprise_data/admin_analytics/database/tables/fact_engagement_admin_dash.py,sha256=wAnpNgh1USAQKi86JdEy4KJ_d_IL2fbKKu3ypqi5BCs,15434
37
+ enterprise_data/admin_analytics/database/tables/fact_enrollment_admin_dash.py,sha256=BwchyH1VJy8Ie-m_ma3bXiKKkNtJhb8A0u37hjocyiU,17049
36
38
  enterprise_data/admin_analytics/database/tables/skills_daily_rollup_admin_dash.py,sha256=3xNwSi0wfCyBHcXPd6-9Ujs1NUm8kmZRg_gPrZzp9nQ,3233
37
39
  enterprise_data/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
40
  enterprise_data/api/urls.py,sha256=POqc_KATHdnpMf9zHtpO46pKD5KAlAExtx7G6iylLcU,273
@@ -45,11 +47,11 @@ enterprise_data/api/v1/serializers.py,sha256=6yxUe_7UBh80e2ZPZIf1AUzzgtKM8-yS38Q
45
47
  enterprise_data/api/v1/urls.py,sha256=Wl1xLboPg-Lq1ZvjAWf51JKYkHlmx02Kpq1nwfDyS8s,4372
46
48
  enterprise_data/api/v1/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
49
  enterprise_data/api/v1/views/analytics_completions.py,sha256=ALkoUTgvaqCxpjnf3rkvwBT7-Hv7eM1RnCdRboCQgR8,6540
48
- enterprise_data/api/v1/views/analytics_engagements.py,sha256=Yo-bpA-0xOQHUPTFF0jHWxjm6KpSC-l2nGxhYX0ajBk,6298
50
+ enterprise_data/api/v1/views/analytics_engagements.py,sha256=XHJ_sfGyeamCW4krEazHzOMDGjK5qwIDvZ1IJRUgLaE,6577
49
51
  enterprise_data/api/v1/views/analytics_enrollments.py,sha256=_KBo4RhEmfQXcCOgvowD28WCVJwlc4APuCnRDbAYK9E,6563
50
52
  enterprise_data/api/v1/views/analytics_leaderboard.py,sha256=3dyo7_OhyGEEeibemBrRsUOo0jbM4LbDgV5gw3YnVig,4186
51
53
  enterprise_data/api/v1/views/base.py,sha256=Kkmd5zgEBAhvwS_GoGXSK6lgbDNwSPioYn-QbnizI3w,3416
52
- enterprise_data/api/v1/views/enterprise_admin.py,sha256=uaf8sSp_JS1T5Kgi4xNCjhu6A0HKa3Zwp6JMjY3okJk,9103
54
+ enterprise_data/api/v1/views/enterprise_admin.py,sha256=3rmFoRxnnQFoOsHDCt8excTJT84NLlhg4QhpdDJLxOM,9129
53
55
  enterprise_data/api/v1/views/enterprise_learner.py,sha256=vRzfaQCEoeq_tEY7KsNZJVzgn7XEWZ1l-n0oUoSVGyU,20093
54
56
  enterprise_data/api/v1/views/enterprise_offers.py,sha256=VifxgqTLFLVw4extYPlHcN1N_yjXcsYsAlYEnAbpb10,1266
55
57
  enterprise_data/cache/__init__.py,sha256=fiBUploll1kmDy2vCmnNpeZVTD4ewsgtRF14vVs0Rb4,1850
@@ -65,7 +67,7 @@ enterprise_data/management/commands/create_enterprise_learner_enrollment_lpr_v1.
65
67
  enterprise_data/management/commands/create_enterprise_learner_lpr_v1.py,sha256=bUYmZHA3yK3ZBPhV0wkpRDgH_Q2b5rVQnwSp2hRmh28,1799
66
68
  enterprise_data/management/commands/create_enterprise_offer.py,sha256=0R1eEKWTCGjod4I8qBH2UBD-erqj5mFtM_DG5Vxet_0,1150
67
69
  enterprise_data/management/commands/create_enterprise_user.py,sha256=V_kvOSPZ1DXfAdF1W3AwAtavEYjdYaHBjjfzndZP8lk,1498
68
- enterprise_data/management/commands/pre_warm_analytics_cache.py,sha256=bwqocYMc72AlgXy_SbT0EL5Aa4sLtVLtYkWSLTZmio0,8696
70
+ enterprise_data/management/commands/pre_warm_analytics_cache.py,sha256=MdvwIZdxYei5V6pTOe_-ohCXHlrMRk29fGe9rfijJzk,8841
69
71
  enterprise_data/management/commands/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
72
  enterprise_data/management/commands/tests/test_create_dummy_data_lpr_v1.py,sha256=wt9fqAFKQVTqllpZ42dch-n31JavUifUIB9CKNYcnYM,1086
71
73
  enterprise_data/management/commands/tests/test_create_enterprise_enrollment.py,sha256=5CABLk8qAx8RP8mrFtnbJ4-xVkf9-5Mq6iQcx8jBfFc,1742
@@ -187,7 +189,7 @@ enterprise_reporting/tests/test_send_enterprise_reports.py,sha256=zBj7sDvRLJQbRs
187
189
  enterprise_reporting/tests/test_utils.py,sha256=y4t6w9aKra-ftqtUeHvPwOhje-1npz7auV5o74ya8fE,9523
188
190
  enterprise_reporting/tests/test_vertica_client.py,sha256=-R2yNCGUjRtoXwLMBloVFQkFYrJoo613VCr61gwI3kQ,140
189
191
  enterprise_reporting/tests/utils.py,sha256=xms2LM7DV3wczXEfctOK1ddel1EE0J_YSr17UzbCDy4,1401
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,,
192
+ edx_enterprise_data-10.14.0.dist-info/METADATA,sha256=4L64abOc1Q5PpDbJ0vhoc0RL7yps1sCMXHD8UotGDK0,1707
193
+ edx_enterprise_data-10.14.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
194
+ edx_enterprise_data-10.14.0.dist-info/top_level.txt,sha256=f5F2kU-dob6MqiHJpgZkFzoCD5VMhsdpkTV5n9Tvq3I,59
195
+ edx_enterprise_data-10.14.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.12.0"
5
+ __version__ = "10.14.0"
@@ -3,4 +3,5 @@ Query Filters for database tables.
3
3
  """
4
4
 
5
5
  from .fact_completion_admin_dash import FactCompletionAdminDashFilters
6
+ from .fact_engagement_admin_dash import FactEngagementAdminDashFilters
6
7
  from .fact_enrollment_admin_dash import FactEnrollmentAdminDashFilters
@@ -2,28 +2,14 @@
2
2
  Query filters for enrollments table.
3
3
  """
4
4
  from enterprise_data.admin_analytics.database.filters.base import BaseFilter
5
- from enterprise_data.admin_analytics.database.query_filters import BetweenQueryFilter, EqualQueryFilter
5
+ from enterprise_data.admin_analytics.database.filters.mixins import CommonFiltersMixin
6
+ from enterprise_data.admin_analytics.database.query_filters import EqualQueryFilter
6
7
 
7
8
 
8
- class FactCompletionAdminDashFilters(BaseFilter):
9
+ class FactCompletionAdminDashFilters(CommonFiltersMixin, BaseFilter):
9
10
  """
10
11
  Query filters for completions data in enrollments table.
11
12
  """
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
13
 
28
14
  @staticmethod
29
15
  def has_passed_filter() -> EqualQueryFilter:
@@ -0,0 +1,11 @@
1
+ """
2
+ Query filters for engagments data.
3
+ """
4
+ from enterprise_data.admin_analytics.database.filters.base import BaseFilter
5
+ from enterprise_data.admin_analytics.database.filters.mixins import CommonFiltersMixin
6
+
7
+
8
+ class FactEngagementAdminDashFilters(CommonFiltersMixin, BaseFilter):
9
+ """
10
+ Query filters for engagments data.
11
+ """
@@ -2,50 +2,10 @@
2
2
  Query filters for enrollments table.
3
3
  """
4
4
  from enterprise_data.admin_analytics.database.filters.base import BaseFilter
5
- from enterprise_data.admin_analytics.database.query_filters import BetweenQueryFilter, EqualQueryFilter, INQueryFilter
5
+ from enterprise_data.admin_analytics.database.filters.mixins import CommonFiltersMixin
6
6
 
7
7
 
8
- class FactEnrollmentAdminDashFilters(BaseFilter):
8
+ class FactEnrollmentAdminDashFilters(CommonFiltersMixin, BaseFilter):
9
9
  """
10
10
  Query filters for enrollments table.
11
11
  """
12
- @staticmethod
13
- def enterprise_customer_uuid_filter(enterprise_customer_uuid_params_key: str) -> EqualQueryFilter:
14
- """
15
- Filter by enterprise customer uuid.
16
-
17
- Arguments:
18
- enterprise_customer_uuid_params_key: The key against which value will be passed in the query.
19
- """
20
- return EqualQueryFilter(
21
- column='enterprise_customer_uuid',
22
- value_placeholder=enterprise_customer_uuid_params_key,
23
- )
24
-
25
- @staticmethod
26
- def enterprise_enrollment_date_range_filter(
27
- start_date_params_key: str, end_date_params_key: str
28
- ) -> BetweenQueryFilter:
29
- """
30
- Filter by enrollment date to be in the given range.
31
-
32
- Arguments:
33
- start_date_params_key (str): The start date key against which value will be passed in the query.
34
- end_date_params_key (str): The end date key against which value will be passed in the query.
35
- """
36
- return BetweenQueryFilter(
37
- column='enterprise_enrollment_date',
38
- range_placeholders=(start_date_params_key, end_date_params_key),
39
- )
40
-
41
- @staticmethod
42
- def enterprise_user_id_in_filter(
43
- enterprise_user_id_param_keys: list,
44
- ) -> INQueryFilter:
45
- """
46
- Filter by enterprise user id.
47
- """
48
- return INQueryFilter(
49
- column='enterprise_user_id',
50
- values_placeholders=enterprise_user_id_param_keys
51
- )
@@ -0,0 +1,84 @@
1
+ """
2
+ Common query filters for all tables.
3
+ """
4
+ from logging import getLogger
5
+ from typing import Optional, Tuple
6
+ from uuid import UUID
7
+
8
+ from enterprise_data.admin_analytics.database.filters.base import BaseFilter
9
+ from enterprise_data.admin_analytics.database.query_filters import BetweenQueryFilter, EqualQueryFilter, INQueryFilter
10
+ from enterprise_data.clients import EnterpriseApiClient
11
+ from enterprise_data.exceptions import EnterpriseApiClientException
12
+
13
+ LOGGER = getLogger(__name__)
14
+
15
+
16
+ class CommonFiltersMixin(BaseFilter):
17
+ """
18
+ Common filters.
19
+ """
20
+ @staticmethod
21
+ def enterprise_customer_uuid_filter(enterprise_customer_uuid_params_key: str) -> EqualQueryFilter:
22
+ """
23
+ Filter by enterprise customer uuid.
24
+
25
+ Arguments:
26
+ enterprise_customer_uuid_params_key: The key against which value will be passed in the query.
27
+ """
28
+ return EqualQueryFilter(
29
+ column='enterprise_customer_uuid',
30
+ value_placeholder=enterprise_customer_uuid_params_key,
31
+ )
32
+
33
+ @staticmethod
34
+ def date_range_filter(
35
+ column: str,
36
+ start_date_params_key: str,
37
+ end_date_params_key: str
38
+ ) -> BetweenQueryFilter:
39
+ """
40
+ Filter by a table column to be in the given date range.
41
+
42
+ Arguments:
43
+ column (str): The table column name to filter on.
44
+ start_date_params_key (str): The start date key against which value will be passed in the query.
45
+ end_date_params_key (str): The end date key against which value will be passed in the query.
46
+ """
47
+ return BetweenQueryFilter(
48
+ column=column,
49
+ range_placeholders=(start_date_params_key, end_date_params_key),
50
+ )
51
+
52
+ def enterprise_user_query_filter( # pylint: disable=inconsistent-return-statements
53
+ self,
54
+ group_uuid: Optional[UUID],
55
+ enterprise_customer_uuid: UUID
56
+ ) -> Optional[Tuple[INQueryFilter, dict]]:
57
+ """
58
+ Get the query filter to filter enrollments for enterprise users in the given group.
59
+
60
+ Arguments:
61
+ group_uuid (UUID): The UUID of the group.
62
+ enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
63
+
64
+ Returns:
65
+ (INQueryFilter | None): The query filter to filter enrollments for enterprise users in the given group.
66
+ """
67
+ if not group_uuid:
68
+ return None
69
+
70
+ try:
71
+ learners_in_group = EnterpriseApiClient.get_enterprise_user_ids_in_group(group_uuid)
72
+ except EnterpriseApiClientException:
73
+ LOGGER.exception(
74
+ "Failed to get learners in group [%s] for enterprise [%s]",
75
+ group_uuid,
76
+ enterprise_customer_uuid,
77
+ )
78
+ else:
79
+ params = {f'eu_{i}': i for i in learners_in_group}
80
+ enterprise_user_id_in_filter = INQueryFilter(
81
+ column='enterprise_user_id',
82
+ values_placeholders=list(params.keys()),
83
+ )
84
+ return enterprise_user_id_in_filter, params
@@ -21,34 +21,32 @@ class FactEngagementAdminDashQueries:
21
21
  """
22
22
 
23
23
  @staticmethod
24
- def get_engagement_count_query():
24
+ def get_engagement_count_query(query_filters):
25
25
  """
26
26
  Get the query to fetch the total number of engagements for an enterprise customer.
27
27
  """
28
- return """
28
+ return f"""
29
29
  SELECT count(*)
30
30
  FROM fact_enrollment_engagement_day_admin_dash
31
- WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
32
- activity_date BETWEEN %(start_date)s AND %(end_date)s;
31
+ WHERE {query_filters.to_sql()};
33
32
  """
34
33
 
35
34
  @staticmethod
36
- def get_all_engagement_query():
35
+ def get_all_engagement_query(query_filters):
37
36
  """
38
37
  Get the query to fetch all engagement data.
39
38
  """
40
- return """
39
+ return f"""
41
40
  SELECT
42
41
  email, course_title, course_subject, enroll_type, activity_date,
43
42
  learning_time_seconds/3600 as learning_time_hours
44
43
  FROM fact_enrollment_engagement_day_admin_dash
45
- WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
46
- activity_date BETWEEN %(start_date)s AND %(end_date)s
44
+ WHERE {query_filters.to_sql()}
47
45
  ORDER BY activity_date DESC LIMIT %(limit)s OFFSET %(offset)s;
48
46
  """
49
47
 
50
48
  @staticmethod
51
- def get_top_courses_by_engagement_query(record_count=10):
49
+ def get_top_courses_by_engagement_query(query_filters, record_count=10):
52
50
  """
53
51
  Get the query to fetch the learning time in hours by courses.
54
52
 
@@ -69,8 +67,7 @@ class FactEngagementAdminDashQueries:
69
67
  (learning_time_seconds / 60.0 / 60.0) AS learning_time_hours,
70
68
  activity_date
71
69
  FROM fact_enrollment_engagement_day_admin_dash
72
- WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
73
- activity_date BETWEEN %(start_date)s AND %(end_date)s
70
+ WHERE {query_filters.to_sql()}
74
71
  ),
75
72
  top_10_courses AS (
76
73
  SELECT
@@ -94,7 +91,7 @@ class FactEngagementAdminDashQueries:
94
91
  """
95
92
 
96
93
  @staticmethod
97
- def get_top_subjects_by_engagement_query(record_count=10):
94
+ def get_top_subjects_by_engagement_query(query_filters, record_count=10):
98
95
  """
99
96
  Get the query to fetch the learning time in hours by subjects.
100
97
 
@@ -114,8 +111,7 @@ class FactEngagementAdminDashQueries:
114
111
  (learning_time_seconds / 60.0 / 60.0) AS learning_time_hours,
115
112
  activity_date
116
113
  FROM fact_enrollment_engagement_day_admin_dash
117
- WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
118
- activity_date BETWEEN %(start_date)s AND %(end_date)s
114
+ WHERE {query_filters.to_sql()}
119
115
  ),
120
116
  top_10_subjects AS (
121
117
  SELECT
@@ -138,7 +134,7 @@ class FactEngagementAdminDashQueries:
138
134
  """
139
135
 
140
136
  @staticmethod
141
- def get_engagement_time_series_data_query():
137
+ def get_engagement_time_series_data_query(query_filters):
142
138
  """
143
139
  Get the query to fetch the completion time series data.
144
140
 
@@ -147,11 +143,10 @@ class FactEngagementAdminDashQueries:
147
143
  Returns:
148
144
  (str): Query to fetch the completion time series data.
149
145
  """
150
- return """
146
+ return f"""
151
147
  SELECT activity_date, enroll_type, SUM(learning_time_seconds)/3600 as learning_time_hours
152
148
  FROM fact_enrollment_engagement_day_admin_dash
153
- WHERE enterprise_customer_uuid=%(enterprise_customer_uuid)s AND
154
- activity_date BETWEEN %(start_date)s AND %(end_date)s
149
+ WHERE {query_filters.to_sql()}
155
150
  GROUP BY activity_date, enroll_type
156
151
  ORDER BY activity_date;
157
152
  """
@@ -2,12 +2,15 @@
2
2
  Module for interacting with the fact_enrollment_engagement_day_admin_dash table.
3
3
  """
4
4
  from datetime import date
5
+ from typing import Optional, Tuple
5
6
  from uuid import UUID
6
7
 
7
8
  from enterprise_data.cache.decorators import cache_it
8
9
  from enterprise_data.utils import find_first
9
10
 
11
+ from ..filters import FactEngagementAdminDashFilters
10
12
  from ..queries import FactEngagementAdminDashQueries
13
+ from ..query_filters import QueryFilters
11
14
  from ..utils import run_query
12
15
  from .base import BaseTable
13
16
 
@@ -19,6 +22,43 @@ class FactEngagementAdminDashTable(BaseTable):
19
22
  Class for communicating with the fact_enrollment_engagement_day_admin_dash table.
20
23
  """
21
24
  queries = FactEngagementAdminDashQueries()
25
+ engagement_filters = FactEngagementAdminDashFilters()
26
+
27
+ def __get_common_query_filters_for_engagement(
28
+ self, enterprise_customer_uuid: UUID,
29
+ group_uuid: Optional[UUID],
30
+ start_date: date,
31
+ end_date: date,
32
+ ) -> Tuple[QueryFilters, dict]:
33
+ """
34
+ Utility method to get query filters common in most usages below.
35
+ This will return a tuple containing the query filters list and the dictionary of query parameters that
36
+ will be used in the query.
37
+ It will contain the following query filters.
38
+ 1. enterprise_customer_uuid filter to filter records for an enterprise customer.
39
+ 2. enrollment_date range filter to filter records by enrollment date.
40
+ 3. group_uuid filter to filter records for learners who belong to the given group.
41
+ """
42
+ query_filters = QueryFilters([
43
+ self.engagement_filters.enterprise_customer_uuid_filter('enterprise_customer_uuid'),
44
+ self.engagement_filters.date_range_filter('activity_date', 'start_date', 'end_date'),
45
+ ])
46
+ params = {
47
+ 'enterprise_customer_uuid': enterprise_customer_uuid,
48
+ 'start_date': start_date,
49
+ 'end_date': end_date,
50
+ }
51
+
52
+ response = self.engagement_filters.enterprise_user_query_filter(
53
+ group_uuid,
54
+ enterprise_customer_uuid
55
+ )
56
+ if response is not None:
57
+ enterprise_user_id_in_filter, enterprise_user_id_params = response
58
+ query_filters.append(enterprise_user_id_in_filter)
59
+ params.update(enterprise_user_id_params)
60
+
61
+ return query_filters, params
22
62
 
23
63
  @cache_it()
24
64
  def get_learning_hours_and_daily_sessions(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
@@ -47,7 +87,13 @@ class FactEngagementAdminDashTable(BaseTable):
47
87
  return tuple(results[0])
48
88
 
49
89
  @cache_it()
50
- def get_engagement_count(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
90
+ def get_engagement_count(
91
+ self,
92
+ enterprise_customer_uuid: UUID,
93
+ group_uuid: Optional[UUID],
94
+ start_date: date,
95
+ end_date: date
96
+ ):
51
97
  """
52
98
  Get the total number of engagements for the given enterprise customer.
53
99
 
@@ -59,13 +105,13 @@ class FactEngagementAdminDashTable(BaseTable):
59
105
  Returns:
60
106
  int: The total number of engagements.
61
107
  """
108
+ query_filters, query_filter_params = self.__get_common_query_filters_for_engagement(
109
+ enterprise_customer_uuid, group_uuid, start_date, end_date
110
+ )
111
+
62
112
  results = run_query(
63
- query=self.queries.get_engagement_count_query(),
64
- params={
65
- 'enterprise_customer_uuid': enterprise_customer_uuid,
66
- 'start_date': start_date,
67
- 'end_date': end_date,
68
- }
113
+ query=self.queries.get_engagement_count_query(query_filters),
114
+ params=query_filter_params,
69
115
  )
70
116
  if not results:
71
117
  return 0
@@ -73,13 +119,20 @@ class FactEngagementAdminDashTable(BaseTable):
73
119
 
74
120
  @cache_it()
75
121
  def get_all_engagements(
76
- self, enterprise_customer_uuid: UUID, start_date: date, end_date: date, limit: int, offset: int
122
+ self,
123
+ enterprise_customer_uuid: UUID,
124
+ group_uuid: Optional[UUID],
125
+ start_date: date,
126
+ end_date: date,
127
+ limit: int,
128
+ offset: int
77
129
  ):
78
130
  """
79
131
  Get all engagement data for the given enterprise customer.
80
132
 
81
133
  Arguments:
82
134
  enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
135
+ group_uuid (UUID): The UUID of a group.
83
136
  start_date (date): The start date.
84
137
  end_date (date): The end date.
85
138
  limit (int): The maximum number of records to return.
@@ -88,12 +141,14 @@ class FactEngagementAdminDashTable(BaseTable):
88
141
  Returns:
89
142
  list<dict>: A list of dictionaries containing the engagement data.
90
143
  """
144
+ query_filters, query_filter_params = self.__get_common_query_filters_for_engagement(
145
+ enterprise_customer_uuid, group_uuid, start_date, end_date
146
+ )
147
+
91
148
  return run_query(
92
- query=self.queries.get_all_engagement_query(),
149
+ query=self.queries.get_all_engagement_query(query_filters),
93
150
  params={
94
- 'enterprise_customer_uuid': enterprise_customer_uuid,
95
- 'start_date': start_date,
96
- 'end_date': end_date,
151
+ **query_filter_params,
97
152
  'limit': limit,
98
153
  'offset': offset,
99
154
  },
@@ -101,7 +156,13 @@ class FactEngagementAdminDashTable(BaseTable):
101
156
  )
102
157
 
103
158
  @cache_it()
104
- def get_top_courses_by_engagement(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
159
+ def get_top_courses_by_engagement(
160
+ self,
161
+ enterprise_customer_uuid: UUID,
162
+ group_uuid: Optional[UUID],
163
+ start_date: date,
164
+ end_date: date
165
+ ):
105
166
  """
106
167
  Get the top courses by user engagement for the given enterprise customer.
107
168
 
@@ -113,18 +174,23 @@ class FactEngagementAdminDashTable(BaseTable):
113
174
  Returns:
114
175
  list<dict>: A list of dictionaries containing the course data.
115
176
  """
177
+ query_filters, query_filter_params = self.__get_common_query_filters_for_engagement(
178
+ enterprise_customer_uuid, group_uuid, start_date, end_date
179
+ )
116
180
  return run_query(
117
- query=self.queries.get_top_courses_by_engagement_query(),
118
- params={
119
- 'enterprise_customer_uuid': enterprise_customer_uuid,
120
- 'start_date': start_date,
121
- 'end_date': end_date,
122
- },
181
+ query=self.queries.get_top_courses_by_engagement_query(query_filters),
182
+ params=query_filter_params,
123
183
  as_dict=True,
124
184
  )
125
185
 
126
186
  @cache_it()
127
- def get_top_subjects_by_engagement(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
187
+ def get_top_subjects_by_engagement(
188
+ self,
189
+ enterprise_customer_uuid: UUID,
190
+ group_uuid: Optional[UUID],
191
+ start_date: date,
192
+ end_date: date
193
+ ):
128
194
  """
129
195
  Get the top subjects by user engagement for the given enterprise customer.
130
196
 
@@ -136,18 +202,23 @@ class FactEngagementAdminDashTable(BaseTable):
136
202
  Returns:
137
203
  list<dict>: A list of dictionaries containing the subject data.
138
204
  """
205
+ query_filters, query_filter_params = self.__get_common_query_filters_for_engagement(
206
+ enterprise_customer_uuid, group_uuid, start_date, end_date
207
+ )
139
208
  return run_query(
140
- query=self.queries.get_top_subjects_by_engagement_query(),
141
- params={
142
- 'enterprise_customer_uuid': enterprise_customer_uuid,
143
- 'start_date': start_date,
144
- 'end_date': end_date,
145
- },
209
+ query=self.queries.get_top_subjects_by_engagement_query(query_filters),
210
+ params=query_filter_params,
146
211
  as_dict=True,
147
212
  )
148
213
 
149
214
  @cache_it()
150
- def get_engagement_time_series_data(self, enterprise_customer_uuid: UUID, start_date: date, end_date: date):
215
+ def get_engagement_time_series_data(
216
+ self,
217
+ enterprise_customer_uuid: UUID,
218
+ group_uuid: Optional[UUID],
219
+ start_date: date,
220
+ end_date: date
221
+ ):
151
222
  """
152
223
  Get the engagement time series data.
153
224
 
@@ -159,13 +230,12 @@ class FactEngagementAdminDashTable(BaseTable):
159
230
  Returns:
160
231
  list<dict>: A list of dictionaries containing the engagement time series data.
161
232
  """
233
+ query_filters, query_filter_params = self.__get_common_query_filters_for_engagement(
234
+ enterprise_customer_uuid, group_uuid, start_date, end_date
235
+ )
162
236
  return run_query(
163
- query=self.queries.get_engagement_time_series_data_query(),
164
- params={
165
- 'enterprise_customer_uuid': enterprise_customer_uuid,
166
- 'start_date': start_date,
167
- 'end_date': end_date,
168
- },
237
+ query=self.queries.get_engagement_time_series_data_query(query_filters),
238
+ params=query_filter_params,
169
239
  as_dict=True,
170
240
  )
171
241
 
@@ -2,60 +2,26 @@
2
2
  Module for interacting with the fact_enrollment_admin_dash table.
3
3
  """
4
4
  from datetime import date, datetime
5
- from logging import getLogger
6
5
  from typing import Optional, Tuple
7
6
  from uuid import UUID
8
7
 
9
8
  from enterprise_data.cache.decorators import cache_it
10
- from enterprise_data.clients import EnterpriseApiClient
11
- from enterprise_data.exceptions import EnterpriseApiClientException
12
9
 
13
10
  from ..filters import FactCompletionAdminDashFilters, FactEnrollmentAdminDashFilters
14
11
  from ..queries import FactEnrollmentAdminDashQueries
15
- from ..query_filters import INQueryFilter, QueryFilters
12
+ from ..query_filters import QueryFilters
16
13
  from ..utils import run_query
17
14
  from .base import BaseTable
18
15
 
19
- LOGGER = getLogger(__name__)
20
-
21
16
 
22
17
  class FactEnrollmentAdminDashTable(BaseTable):
23
18
  """
24
19
  Class for communicating with the fact_enrollment_admin_dash table.
25
20
  """
26
21
  queries = FactEnrollmentAdminDashQueries()
27
- filters = FactEnrollmentAdminDashFilters()
22
+ enrollment_filters = FactEnrollmentAdminDashFilters()
28
23
  completion_filters = FactCompletionAdminDashFilters()
29
24
 
30
- def __get_enterprise_user_query_filter( # pylint: disable=inconsistent-return-statements
31
- self, group_uuid: Optional[UUID],
32
- enterprise_customer_uuid: UUID
33
- ) -> Optional[Tuple[INQueryFilter, dict]]:
34
- """
35
- Get the query filter to filter enrollments for enterprise users in the given group.
36
-
37
- Arguments:
38
- group_uuid (UUID): The UUID of the group.
39
- enterprise_customer_uuid (UUID): The UUID of the enterprise customer.
40
-
41
- Returns:
42
- (INQueryFilter | None): The query filter to filter enrollments for enterprise users in the given group.
43
- """
44
- if not group_uuid:
45
- return None
46
-
47
- try:
48
- learners_in_group = EnterpriseApiClient.get_enterprise_user_ids_in_group(group_uuid)
49
- except EnterpriseApiClientException:
50
- LOGGER.exception(
51
- "Failed to get learners in group [%s] for enterprise [%s]",
52
- group_uuid,
53
- enterprise_customer_uuid,
54
- )
55
- else:
56
- params = {f'eu_{i}': enterprise_user_id for i, enterprise_user_id in enumerate(learners_in_group)}
57
- return self.filters.enterprise_user_id_in_filter(list(params.keys())), params
58
-
59
25
  def __get_common_query_filters(
60
26
  self, enterprise_customer_uuid: UUID,
61
27
  group_uuid: Optional[UUID],
@@ -74,8 +40,8 @@ class FactEnrollmentAdminDashTable(BaseTable):
74
40
  3. group_uuid filter to filter records for learners who belong to the given group.
75
41
  """
76
42
  query_filters = QueryFilters([
77
- self.filters.enterprise_customer_uuid_filter('enterprise_customer_uuid'),
78
- self.filters.enterprise_enrollment_date_range_filter('start_date', 'end_date'),
43
+ self.enrollment_filters.enterprise_customer_uuid_filter('enterprise_customer_uuid'),
44
+ self.enrollment_filters.date_range_filter('enterprise_enrollment_date', 'start_date', 'end_date'),
79
45
  ])
80
46
  params = {
81
47
  'enterprise_customer_uuid': enterprise_customer_uuid,
@@ -83,7 +49,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
83
49
  'end_date': end_date,
84
50
  }
85
51
 
86
- response = self.__get_enterprise_user_query_filter(
52
+ response = self.enrollment_filters.enterprise_user_query_filter(
87
53
  group_uuid, enterprise_customer_uuid
88
54
  )
89
55
  if response is not None:
@@ -112,8 +78,8 @@ class FactEnrollmentAdminDashTable(BaseTable):
112
78
  4. has_passed filter to filter records for successful completions.
113
79
  """
114
80
  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'),
81
+ self.completion_filters.enterprise_customer_uuid_filter('enterprise_customer_uuid'),
82
+ self.completion_filters.date_range_filter('passed_date', 'start_date', 'end_date'),
117
83
  self.completion_filters.has_passed_filter(),
118
84
  ])
119
85
  params = {
@@ -122,7 +88,7 @@ class FactEnrollmentAdminDashTable(BaseTable):
122
88
  'end_date': end_date,
123
89
  }
124
90
 
125
- response = self.__get_enterprise_user_query_filter(
91
+ response = self.completion_filters.enterprise_user_query_filter(
126
92
  group_uuid, enterprise_customer_uuid
127
93
  )
128
94
  if response is not None:
@@ -51,10 +51,12 @@ class AdvanceAnalyticsEngagementView(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
  engagements = FactEngagementAdminDashTable().get_all_engagements(
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 AdvanceAnalyticsEngagementView(AnalyticsPaginationMixin, ViewSet):
62
64
  )
63
65
  total_count = FactEngagementAdminDashTable().get_engagement_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 AdvanceAnalyticsEngagementView(AnalyticsPaginationMixin, ViewSet):
79
82
 
80
83
  return StreamingHttpResponse(
81
84
  IndividualEngagementsCSVRenderer().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 AdvanceAnalyticsEngagementView(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 AdvanceAnalyticsEngagementView(AnalyticsPaginationMixin, ViewSet):
102
105
  while offset < total_count:
103
106
  engagements = FactEngagementAdminDashTable().get_all_engagements(
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,17 @@ class AdvanceAnalyticsEngagementView(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')
136
141
  with timer('construct_engagement_all_stats'):
137
142
  data = {
138
143
  'engagement_over_time': FactEngagementAdminDashTable().get_engagement_time_series_data(
139
- enterprise_uuid, start_date, end_date
144
+ enterprise_uuid, group_uuid, start_date, end_date
140
145
  ),
141
146
  'top_courses_by_engagement': FactEngagementAdminDashTable().get_top_courses_by_engagement(
142
- enterprise_uuid, start_date, end_date,
147
+ enterprise_uuid, group_uuid, start_date, end_date,
143
148
  ),
144
149
  'top_subjects_by_engagement': FactEngagementAdminDashTable().get_top_subjects_by_engagement(
145
- enterprise_uuid, start_date, end_date,
150
+ enterprise_uuid, group_uuid, start_date, end_date,
146
151
  ),
147
152
  }
148
153
  return Response(data)
@@ -196,7 +196,7 @@ class EnterpriseExecEdLCModulePerformanceViewSet(EnterpriseViewSetMixin, viewset
196
196
  serializer_class = serializers.EnterpriseExecEdLCModulePerformanceSerializer
197
197
  filter_backends = (filters.OrderingFilter, filters.SearchFilter)
198
198
  ordering_fields = '__all__'
199
- ordering = ('last_access',)
199
+ ordering = ['username', 'module_name', 'last_access']
200
200
  search_fields = (
201
201
  'username',
202
202
  'course_name'
@@ -147,12 +147,14 @@ class Command(BaseCommand):
147
147
  )
148
148
  enterprise_engagement_table.get_engagement_count(
149
149
  enterprise_customer_uuid=enterprise_customer_uuid,
150
+ group_uuid=None,
150
151
  start_date=start_date,
151
152
  end_date=end_date,
152
153
  )
153
154
  page_size = 100
154
155
  enterprise_engagement_table.get_all_engagements(
155
156
  enterprise_customer_uuid=enterprise_customer_uuid,
157
+ group_uuid=None,
156
158
  start_date=start_date,
157
159
  end_date=end_date,
158
160
  limit=page_size,
@@ -160,16 +162,19 @@ class Command(BaseCommand):
160
162
  )
161
163
  enterprise_engagement_table.get_top_courses_by_engagement(
162
164
  enterprise_customer_uuid=enterprise_customer_uuid,
165
+ group_uuid=None,
163
166
  start_date=start_date,
164
167
  end_date=end_date,
165
168
  )
166
169
  enterprise_engagement_table.get_top_subjects_by_engagement(
167
170
  enterprise_customer_uuid=enterprise_customer_uuid,
171
+ group_uuid=None,
168
172
  start_date=start_date,
169
173
  end_date=end_date,
170
174
  )
171
175
  enterprise_engagement_table.get_engagement_time_series_data(
172
176
  enterprise_customer_uuid=enterprise_customer_uuid,
177
+ group_uuid=None,
173
178
  start_date=start_date,
174
179
  end_date=end_date,
175
180
  )