Skip to content

Commit 00f77bd

Browse files
Merge pull request #583 from openedx/ammar/filter-engagement-data-by-group-uuid
feat: filter engagement data by group uuid
2 parents f0b889e + 0d01673 commit 00f77bd

12 files changed

+245
-158
lines changed

CHANGELOG.rst

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ Unreleased
1515
----------
1616

1717
=========================
18+
[10.13.0] - 2025-04-11
19+
---------------------
20+
* feat: Support filtering of engagement API by group_uuid
21+
1822
[10.12.0] - 2025-04-09
1923
---------------------
2024
* feat: Added the ability to filter enrollments by group_uuid in the enterprise completions API

enterprise_data/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Enterprise data api application. This Django app exposes API endpoints used by enterprises.
33
"""
44

5-
__version__ = "10.12.0"
5+
__version__ = "10.13.0"

enterprise_data/admin_analytics/database/filters/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
"""
44

55
from .fact_completion_admin_dash import FactCompletionAdminDashFilters
6+
from .fact_engagement_admin_dash import FactEngagementAdminDashFilters
67
from .fact_enrollment_admin_dash import FactEnrollmentAdminDashFilters

enterprise_data/admin_analytics/database/filters/fact_completion_admin_dash.py

+3-17
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,14 @@
22
Query filters for enrollments table.
33
"""
44
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
67

78

8-
class FactCompletionAdminDashFilters(BaseFilter):
9+
class FactCompletionAdminDashFilters(CommonFiltersMixin, BaseFilter):
910
"""
1011
Query filters for completions data in enrollments table.
1112
"""
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-
)
2713

2814
@staticmethod
2915
def has_passed_filter() -> EqualQueryFilter:
Original file line numberDiff line numberDiff line change
@@ -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+
"""

enterprise_data/admin_analytics/database/filters/fact_enrollment_admin_dash.py

+2-42
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,10 @@
22
Query filters for enrollments table.
33
"""
44
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
66

77

8-
class FactEnrollmentAdminDashFilters(BaseFilter):
8+
class FactEnrollmentAdminDashFilters(CommonFiltersMixin, BaseFilter):
99
"""
1010
Query filters for enrollments table.
1111
"""
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-
)
Original file line numberDiff line numberDiff line change
@@ -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

enterprise_data/admin_analytics/database/queries/fact_engagement_admin_dash.py

+13-18
Original file line numberDiff line numberDiff line change
@@ -21,34 +21,32 @@ def get_learning_hours_and_daily_sessions_query():
2121
"""
2222

2323
@staticmethod
24-
def get_engagement_count_query():
24+
def get_engagement_count_query(query_filters):
2525
"""
2626
Get the query to fetch the total number of engagements for an enterprise customer.
2727
"""
28-
return """
28+
return f"""
2929
SELECT count(*)
3030
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()};
3332
"""
3433

3534
@staticmethod
36-
def get_all_engagement_query():
35+
def get_all_engagement_query(query_filters):
3736
"""
3837
Get the query to fetch all engagement data.
3938
"""
40-
return """
39+
return f"""
4140
SELECT
4241
email, course_title, course_subject, enroll_type, activity_date,
4342
learning_time_seconds/3600 as learning_time_hours
4443
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()}
4745
ORDER BY activity_date DESC LIMIT %(limit)s OFFSET %(offset)s;
4846
"""
4947

5048
@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):
5250
"""
5351
Get the query to fetch the learning time in hours by courses.
5452
@@ -69,8 +67,7 @@ def get_top_courses_by_engagement_query(record_count=10):
6967
(learning_time_seconds / 60.0 / 60.0) AS learning_time_hours,
7068
activity_date
7169
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()}
7471
),
7572
top_10_courses AS (
7673
SELECT
@@ -94,7 +91,7 @@ def get_top_courses_by_engagement_query(record_count=10):
9491
"""
9592

9693
@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):
9895
"""
9996
Get the query to fetch the learning time in hours by subjects.
10097
@@ -114,8 +111,7 @@ def get_top_subjects_by_engagement_query(record_count=10):
114111
(learning_time_seconds / 60.0 / 60.0) AS learning_time_hours,
115112
activity_date
116113
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()}
119115
),
120116
top_10_subjects AS (
121117
SELECT
@@ -138,7 +134,7 @@ def get_top_subjects_by_engagement_query(record_count=10):
138134
"""
139135

140136
@staticmethod
141-
def get_engagement_time_series_data_query():
137+
def get_engagement_time_series_data_query(query_filters):
142138
"""
143139
Get the query to fetch the completion time series data.
144140
@@ -147,11 +143,10 @@ def get_engagement_time_series_data_query():
147143
Returns:
148144
(str): Query to fetch the completion time series data.
149145
"""
150-
return """
146+
return f"""
151147
SELECT activity_date, enroll_type, SUM(learning_time_seconds)/3600 as learning_time_hours
152148
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()}
155150
GROUP BY activity_date, enroll_type
156151
ORDER BY activity_date;
157152
"""

0 commit comments

Comments
 (0)