From b8cd931391c6127f180ea0502dafecb80890a7a4 Mon Sep 17 00:00:00 2001 From: Kira Miller <31229189+kiram15@users.noreply.github.com> Date: Tue, 26 May 2026 19:52:07 +0000 Subject: [PATCH] feat: replace enterprise_support import with GradeEventContextRequested filter --- lms/djangoapps/grades/events.py | 23 ++++-- lms/djangoapps/grades/tests/test_events.py | 82 +++++++++++++++++++++- requirements/constraints.txt | 2 +- requirements/edx/base.txt | 2 +- requirements/edx/development.txt | 2 +- requirements/edx/doc.txt | 2 +- requirements/edx/testing.txt | 2 +- 7 files changed, 105 insertions(+), 10 deletions(-) diff --git a/lms/djangoapps/grades/events.py b/lms/djangoapps/grades/events.py index e386d0bc6326..b47cad3277af 100644 --- a/lms/djangoapps/grades/events.py +++ b/lms/djangoapps/grades/events.py @@ -15,6 +15,7 @@ UserPersonalData, ) from openedx_events.learning.signals import CCX_COURSE_PASSING_STATUS_UPDATED, COURSE_PASSING_STATUS_UPDATED +from openedx_filters.learning.filters import GradeEventContextRequested from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.student.models import CourseEnrollment @@ -27,7 +28,6 @@ ) from lms.djangoapps.grades.signals.signals import SCHEDULE_FOLLOW_UP_SEGMENT_EVENT_FOR_COURSE_PASSED_FIRST_TIME from openedx.core.djangoapps.content.course_overviews.models import CourseOverview -from openedx.features.enterprise_support.context import get_enterprise_event_context log = getLogger(__name__) @@ -166,10 +166,25 @@ def course_grade_passed_first_time(user_id, course_id): """ event_name = COURSE_GRADE_PASSED_FIRST_TIME_EVENT_TYPE context = contexts.course_context_from_course_id(course_id) - context_enterprise = get_enterprise_event_context(user_id, course_id) - context.update(context_enterprise) + try: + filtered_context, _, _ = GradeEventContextRequested.run_filter( + context=context, user_id=user_id, course_id=course_id + ) + except Exception: # pylint: disable=broad-except + log.exception( + 'GradeEventContextRequested failed for user_id=%s course_id=%s', + user_id, + course_id, + ) + raise + log.debug( + 'GradeEventContextRequested succeeded for user_id=%s course_id=%s', + user_id, + course_id, + ) + # TODO (AN-6134): remove this context manager - with tracker.get_tracker().context(event_name, context): + with tracker.get_tracker().context(event_name, filtered_context): tracker.emit( event_name, { diff --git a/lms/djangoapps/grades/tests/test_events.py b/lms/djangoapps/grades/tests/test_events.py index 01b214ce95e1..baf6f7be378b 100644 --- a/lms/djangoapps/grades/tests/test_events.py +++ b/lms/djangoapps/grades/tests/test_events.py @@ -3,7 +3,9 @@ """ from unittest import mock +from unittest.mock import patch +import pytest from ccx_keys.locator import CCXLocator from django.utils.timezone import now from openedx_events.learning.data import ( @@ -34,7 +36,7 @@ class PersistentGradeEventsTest(OpenEdxEventsTestMixin, SharedModuleStoreTestCase): """ - Tests for the Open edX Events associated with the persistant grade process through the update_or_create method. + Tests for the Open edX Events associated with the persistent grade process through the update_or_create method. This class guarantees that the following events are sent during the user updates their grade, with the exact Data Attributes as the event definition stated: @@ -229,3 +231,81 @@ def test_ccx_course_passing_status_updated_emitted(self): }, event_receiver.call_args.kwargs, ) + + +class GradeEventContextFilterTest(SharedModuleStoreTestCase): + """ + Tests that course_grade_passed_first_time invokes the GradeEventContextRequested + filter and uses the returned context directly. + """ + + def setUp(self): + super().setUp() + self.user = UserFactory.create() + self.course = CourseFactory.create() + + @patch('lms.djangoapps.grades.events.tracker') + @patch('lms.djangoapps.grades.events.contexts.course_context_from_course_id') + @patch('lms.djangoapps.grades.events.GradeEventContextRequested.run_filter') + def test_filter_called_with_context( + self, + mock_run_filter, + mock_course_context_from_course_id, + mock_tracker, + ): + """ + course_grade_passed_first_time should call + GradeEventContextRequested.run_filter and use the returned context. + """ + original_context = {"course_id": str(self.course.id)} + filtered_context = { + "course_id": str(self.course.id), + "org": "test_org", + "enterprise_uuid": "abc-123", + } + mock_course_context_from_course_id.return_value = original_context + mock_run_filter.return_value = (filtered_context, self.user.id, self.course.id) + + from lms.djangoapps.grades.events import course_grade_passed_first_time + + course_grade_passed_first_time(self.user.id, self.course.id) + + mock_run_filter.assert_called_once_with( + context=original_context, + user_id=self.user.id, + course_id=self.course.id, + ) + mock_tracker.get_tracker.return_value.context.assert_called_once_with( + 'edx.course.grade.passed.first_time', + filtered_context, + ) + + @patch('lms.djangoapps.grades.events.log') + @patch('lms.djangoapps.grades.events.tracker') + @patch('lms.djangoapps.grades.events.contexts.course_context_from_course_id') + @patch('lms.djangoapps.grades.events.GradeEventContextRequested.run_filter') + def test_filter_exception_is_logged_and_raised( + self, + mock_run_filter, + mock_course_context_from_course_id, + mock_tracker, + mock_log, + ): + """ + If run_filter raises an exception, it should be logged and re-raised. + """ + original_context = {"course_id": str(self.course.id)} + mock_course_context_from_course_id.return_value = original_context + mock_run_filter.side_effect = Exception("boom") + + from lms.djangoapps.grades.events import course_grade_passed_first_time + + with self.assertRaisesRegex(Exception, "boom"): + course_grade_passed_first_time(self.user.id, self.course.id) + + mock_log.exception.assert_called_once_with( + 'GradeEventContextRequested failed for user_id=%s course_id=%s', + self.user.id, + self.course.id, + ) + mock_tracker.get_tracker.return_value.context.assert_not_called() diff --git a/requirements/constraints.txt b/requirements/constraints.txt index 9e5331e24a25..a622294e90d9 100644 --- a/requirements/constraints.txt +++ b/requirements/constraints.txt @@ -44,7 +44,7 @@ django-stubs<6 # The team that owns this package will manually bump this package rather than having it pulled in automatically. # This is to allow them to better control its deployment and to do it in a process that works better # for them. -edx-enterprise==8.0.14 +edx-enterprise==8.0.15 # Date: 2023-07-26 # Our legacy Sass code is incompatible with anything except this ancient libsass version. diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index 7bd90612d22b..0f081a3102d2 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -483,7 +483,7 @@ edx-drf-extensions==10.6.0 # enterprise-integrated-channels # openedx-authz # openedx-core -edx-enterprise==8.0.14 +edx-enterprise==8.0.15 # via # -c requirements/constraints.txt # -r requirements/edx/kernel.in diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index 24305b48d8b5..245008c6242e 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -756,7 +756,7 @@ edx-drf-extensions==10.6.0 # enterprise-integrated-channels # openedx-authz # openedx-core -edx-enterprise==8.0.14 +edx-enterprise==8.0.15 # via # -c requirements/constraints.txt # -r requirements/edx/doc.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index f09d2947f481..afc9418b1ab0 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -573,7 +573,7 @@ edx-drf-extensions==10.6.0 # enterprise-integrated-channels # openedx-authz # openedx-core -edx-enterprise==8.0.14 +edx-enterprise==8.0.15 # via # -c requirements/constraints.txt # -r requirements/edx/base.txt diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index b1e08b4f4fad..d2701636a298 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -589,7 +589,7 @@ edx-drf-extensions==10.6.0 # enterprise-integrated-channels # openedx-authz # openedx-core -edx-enterprise==8.0.14 +edx-enterprise==8.0.15 # via # -c requirements/constraints.txt # -r requirements/edx/base.txt