Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 20 additions & 48 deletions lms/djangoapps/support/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,13 @@
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,
SharedModuleStoreTestCase,
)
from xmodule.modulestore.tests.factories import CourseFactory

try:
from consent.models import DataSharingConsent
except ImportError: # pragma: no cover
pass


class SupportViewTestCase(ModuleStoreTestCase):
"""
Expand Down Expand Up @@ -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'] == ''

Expand Down Expand Up @@ -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'),
Expand Down
43 changes: 2 additions & 41 deletions lms/djangoapps/support/views/enrollments.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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__)

Expand Down Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions requirements/edx/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions requirements/edx/development.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
14 changes: 7 additions & 7 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading