diff --git a/lms/djangoapps/support/tests/test_views.py b/lms/djangoapps/support/tests/test_views.py index 86bee237172e..ea8208c32bda 100644 --- a/lms/djangoapps/support/tests/test_views.py +++ b/lms/djangoapps/support/tests/test_views.py @@ -60,11 +60,6 @@ from openedx.core.djangoapps.oauth_dispatch.tests import factories from openedx.features.content_type_gating.models import ContentTypeGatingConfig from openedx.features.course_duration_limits.models import CourseDurationLimitConfig -from openedx.features.enterprise_support.api import enterprise_is_enabled -from openedx.features.enterprise_support.tests.factories import ( - EnterpriseCourseEnrollmentFactory, - EnterpriseCustomerUserFactory, -) from xmodule.modulestore.tests.django_utils import ( TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase, @@ -72,11 +67,6 @@ ) from xmodule.modulestore.tests.factories import CourseFactory -try: - from consent.models import DataSharingConsent -except ImportError: # pragma: no cover - pass - class SupportViewTestCase(ModuleStoreTestCase): """ @@ -404,7 +394,6 @@ def test_get_enrollments(self, search_string_type): ) assert {CourseMode.VERIFIED, CourseMode.AUDIT, CourseMode.HONOR, CourseMode.NO_ID_PROFESSIONAL_MODE, CourseMode.PROFESSIONAL, CourseMode.CREDIT_MODE} == {mode['slug'] for mode in data[0]['course_modes']} - assert 'enterprise_course_enrollments' not in data[0] assert data[0]['order_number'] == '' assert data[0]['source_system'] == '' @@ -445,52 +434,35 @@ def test_order_source_system_information(self): assert len(data) == 1 assert data[0]['source_system'] == 'commercetools' - @override_settings(ENABLE_ENTERPRISE_INTEGRATION=True) - @enterprise_is_enabled() - def test_get_enrollments_enterprise_enabled(self): + @patch('lms.djangoapps.support.views.enrollments.SupportEnrollmentDataRequested.run_filter') + def test_get_enrollments_augmented_by_pipeline_step(self, mock_run_filter): + """ + Data a configured pipeline step adds to an enrollment dict is passed through in the + response. This view only needs to verify it wires the filter's return value through + correctly — pipeline step behavior itself is covered by that step's own test suite. + """ + course_id = str(self.course.id) + + def fake_run_filter(enrollments_data, user): + for enrollment in enrollments_data: + enrollment['foo_extra_data'] = "I'm a little teapot." + return enrollments_data, user + + mock_run_filter.side_effect = fake_run_filter + url = reverse( 'support:enrollment_list', kwargs={'username_or_email': self.student.username} ) - - enterprise_customer_user = EnterpriseCustomerUserFactory( - user_id=self.student.id - ) - enterprise_course_enrollment = EnterpriseCourseEnrollmentFactory( - course_id=self.course.id, - enterprise_customer_user=enterprise_customer_user - ) - data_sharing_consent = DataSharingConsent( - course_id=self.course.id, - enterprise_customer=enterprise_customer_user.enterprise_customer, - username=self.student.username, - granted=True - ) - data_sharing_consent.save() - response = self.client.get(url) assert response.status_code == 200 data = json.loads(response.content.decode('utf-8')) assert len(data) == 1 - enterprise_course_enrollments_data = data[0]['enterprise_course_enrollments'] - assert len(enterprise_course_enrollments_data) == 1 - expected = { - 'course_id': str(enterprise_course_enrollment.course_id), - 'enterprise_customer_name': enterprise_customer_user.enterprise_customer.name, - 'enterprise_customer_user_id': enterprise_customer_user.id, - 'license': None, - 'saved_for_later': enterprise_course_enrollment.saved_for_later, - 'data_sharing_consent': { - 'username': self.student.username, - 'enterprise_customer_uuid': str(enterprise_customer_user.enterprise_customer_id), - 'exists': data_sharing_consent.exists, - 'consent_provided': data_sharing_consent.granted, - 'consent_required': data_sharing_consent.consent_required(), - 'course_id': str(enterprise_course_enrollment.course_id), - } - } - assert enterprise_course_enrollments_data[0] == expected + _, call_kwargs = mock_run_filter.call_args + assert call_kwargs['user'] == self.student + assert call_kwargs['enrollments_data'][0]['course_id'] == course_id + assert data[0]['foo_extra_data'] == "I'm a little teapot." @ddt.data( (True, 'Self Paced'), diff --git a/lms/djangoapps/support/views/enrollments.py b/lms/djangoapps/support/views/enrollments.py index d2fde62ca3a7..134185fe9939 100644 --- a/lms/djangoapps/support/views/enrollments.py +++ b/lms/djangoapps/support/views/enrollments.py @@ -2,7 +2,6 @@ Support tool for changing course enrollments. """ import logging -from collections import defaultdict import markupsafe from django.contrib.auth.models import User # pylint: disable=imported-auth-user @@ -14,6 +13,7 @@ from django.views.generic import View from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey +from openedx_filters.learning.filters import SupportEnrollmentDataRequested from rest_framework.generics import GenericAPIView from common.djangoapps.course_modes.models import CourseMode @@ -34,12 +34,6 @@ from openedx.core.djangoapps.enrollments.api import get_enrollment_attributes, get_enrollments, update_enrollment from openedx.core.djangoapps.enrollments.errors import CourseModeNotFoundError from openedx.core.djangoapps.enrollments.serializers import ModeSerializer -from openedx.features.enterprise_support.api import ( - enterprise_enabled, - get_data_sharing_consents, - get_enterprise_course_enrollments, -) -from openedx.features.enterprise_support.serializers import EnterpriseCourseEnrollmentSerializer logger = logging.getLogger(__name__) @@ -70,35 +64,6 @@ class EnrollmentSupportListView(GenericAPIView): # does not specify a serializer class. exclude_from_schema = True - def _enterprise_course_enrollments_by_course_id(self, user): - """ - Returns a dict containing enterprise course enrollments data with - course ids as keys. - """ - enterprise_course_enrollments = get_enterprise_course_enrollments(user) - data_sharing_consents_for_user = get_data_sharing_consents(user) - - enterprise_enrollments_by_course_id = defaultdict(list) - consent_by_course_and_enterprise_customer_id = {} - - # Get data sharing consent for each enterprise enrollment - for consent in data_sharing_consents_for_user: - key = f'{consent.course_id}-{consent.enterprise_customer_id}' - consent_by_course_and_enterprise_customer_id[key] = consent.serialize() - - for enterprise_course_enrollment in enterprise_course_enrollments: - serialized_enterprise_course_enrollment = EnterpriseCourseEnrollmentSerializer( - enterprise_course_enrollment - ).data - course_id = enterprise_course_enrollment.course_id - enterprise_customer_id = enterprise_course_enrollment.enterprise_customer_user.enterprise_customer_id - key = f'{course_id}-{enterprise_customer_id}' - consent = consent_by_course_and_enterprise_customer_id.get(key) - serialized_enterprise_course_enrollment['data_sharing_consent'] = consent - enterprise_enrollments_by_course_id[course_id].append(serialized_enterprise_course_enrollment) - - return enterprise_enrollments_by_course_id - @method_decorator(require_support_permission) def get(self, request, username_or_email): """ @@ -126,11 +91,7 @@ def get(self, request, username_or_email): # Add manual enrollment history, if it exists enrollment['manual_enrollment'] = self.manual_enrollment_data(enrollment, course_key) - if enterprise_enabled(): - enterprise_enrollments_by_course_id = self._enterprise_course_enrollments_by_course_id(user) - for enrollment in enrollments: - enterprise_course_enrollments = enterprise_enrollments_by_course_id.get(enrollment['course_id'], []) - enrollment['enterprise_course_enrollments'] = enterprise_course_enrollments + enrollments, _ = SupportEnrollmentDataRequested.run_filter(enrollments_data=enrollments, user=user) return JsonResponse(enrollments) diff --git a/pyproject.toml b/pyproject.toml index 43d8bcf43350..5a1b10dab7dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -290,7 +290,7 @@ constraint-dependencies = [ "sphinx-autoapi<3.6.1", "setuptools<82", "astroid==4.0.4", - "edx-enterprise==8.11.0", + "edx-enterprise==8.13.0", "djangorestframework<3.18", ] [tool.edx_lint] @@ -407,7 +407,7 @@ uv_constraints = [ # 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.11.0", + "edx-enterprise==8.13.0", # Date: 2026-08-31 # DRF 3.18.0 changes many=True validation errors from a list to a dict keyed by # item index, which breaks the error response shape of several write endpoints. diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index a84b5d2f0e04..75e1643266b0 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -480,7 +480,7 @@ edx-drf-extensions==10.9.0 # openedx-authz # openedx-core # openedx-platform -edx-enterprise==8.11.0 +edx-enterprise==8.13.0 # via openedx-platform edx-event-bus-kafka==6.1.0 # via openedx-platform @@ -845,7 +845,7 @@ openedx-events==11.2.0 # openedx-core # openedx-platform # ora2 -openedx-filters==3.11.0 +openedx-filters==3.13.0 # via # edx-enterprise # lti-consumer-xblock diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index df19b9b2bedf..0429441fbfef 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -533,7 +533,7 @@ edx-drf-extensions==10.9.0 # openedx-authz # openedx-core # openedx-platform -edx-enterprise==8.11.0 +edx-enterprise==8.13.0 # via openedx-platform edx-event-bus-kafka==6.1.0 # via openedx-platform @@ -947,7 +947,7 @@ openedx-events==11.2.0 # openedx-core # openedx-platform # ora2 -openedx-filters==3.11.0 +openedx-filters==3.13.0 # via # edx-enterprise # lti-consumer-xblock diff --git a/uv.lock b/uv.lock index dc7157edb779..97b7f4cdad36 100644 --- a/uv.lock +++ b/uv.lock @@ -20,7 +20,7 @@ constraints = [ { name = "django-oauth-toolkit", specifier = "==1.7.1" }, { name = "django-stubs", specifier = "<6" }, { name = "djangorestframework", specifier = "<3.18" }, - { name = "edx-enterprise", specifier = "==8.11.0" }, + { name = "edx-enterprise", specifier = "==8.13.0" }, { name = "elasticsearch", specifier = "==7.9.1" }, { name = "libsass", specifier = "==0.10.0" }, { name = "lxml", specifier = "==5.3.2" }, @@ -2044,7 +2044,7 @@ wheels = [ [[package]] name = "edx-enterprise" -version = "8.11.0" +version = "8.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bleach" }, @@ -2098,9 +2098,9 @@ dependencies = [ { name = "tincan" }, { name = "unicodecsv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/32/25/659c1ce1ee742721ddb8063da436bffed1a5b3891fbdf5e8bea5b289c601/edx_enterprise-8.11.0.tar.gz", hash = "sha256:e059a570463f01214555d431d467191e261a9f8d3ac5bce15161b4beefd48e9e", size = 5166614, upload-time = "2026-09-10T18:04:36.466Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/07/2221f3da7cd3b738782c951ce8304f9712745a34fe79c31f4b2e4182fce0/edx_enterprise-8.13.0.tar.gz", hash = "sha256:a846af2215d282f757c10a58e059678fd9d91a52960a86e9c64fc778f15efe84", size = 5167027, upload-time = "2026-09-16T17:50:40.292Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/d9/c867215a28e0ce0335710a574ec3da5632c9e299dba7cc82f1a646aac69d/edx_enterprise-8.11.0-py3-none-any.whl", hash = "sha256:49138decfadf5d5ee84854bc4e19840c74fde635ba8cc33b0d8eb57fb876c5f9", size = 5560621, upload-time = "2026-09-10T18:04:33.04Z" }, + { url = "https://files.pythonhosted.org/packages/65/2e/11e9d8e4d3faad3b9e181c6b9291d0f2665b3699483942c325b11d8a0095/edx_enterprise-8.13.0-py3-none-any.whl", hash = "sha256:3d6df911be5873def84396d94a5cf0ad2525f2a801780fa0f4409067d569e923", size = 5561253, upload-time = "2026-09-16T17:50:36.868Z" }, ] [[package]] @@ -4372,7 +4372,7 @@ wheels = [ [[package]] name = "openedx-filters" -version = "3.11.0" +version = "3.13.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "django", version = "4.2.30", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-16-openedx-platform-django42'" }, @@ -4380,9 +4380,9 @@ dependencies = [ { name = "edx-opaque-keys" }, { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/c7/91104a834a10a8dddb6cf5f60b93e55b0f749adb187f3322a00a2f460f72/openedx_filters-3.11.0.tar.gz", hash = "sha256:4fe0815a8e7edf45e34f3edeb2d8653446b1c3136e57d06109f5303964898f79", size = 51483, upload-time = "2026-09-10T18:50:26.047Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/d5/ff6693eac81f446f41a789fcc61b2e6d4feac12f4bf8008e9968bf09e67a/openedx_filters-3.13.0.tar.gz", hash = "sha256:4b41bc61fa789fe7cdd6095dc61f12f15492881e9023ccf6606262b7cc56e57d", size = 52411, upload-time = "2026-09-16T17:47:19.519Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/89/9c/71128665dfd5860caafa3977493ab61b2f08aab80a453d951053799c58f8/openedx_filters-3.11.0-py2.py3-none-any.whl", hash = "sha256:e2b86d324b3d16b728af64fe1e6f8796f97d21b9de9e2356806545efda28e08b", size = 50461, upload-time = "2026-09-10T18:50:24.901Z" }, + { url = "https://files.pythonhosted.org/packages/41/00/41dd3650ca52251844c3f7a48699ca66150e4a436b27fac03e6e7cf7b200/openedx_filters-3.13.0-py2.py3-none-any.whl", hash = "sha256:2992f607fb714d8d313b399a8b244d0510deb157717a74446e2cff01429f68af", size = 51236, upload-time = "2026-09-16T17:47:18.452Z" }, ] [[package]]