feat: extend program dash endpoint to b2c (#37387)

This commit is contained in:
Maxwell Frank
2025-10-01 11:38:05 -04:00
committed by GitHub
parent 8d95c32021
commit 7fc88d5a25
3 changed files with 106 additions and 6 deletions

View File

@@ -10,6 +10,7 @@ from django.test.utils import override_settings
from django.urls import reverse_lazy
from enterprise.models import EnterpriseCourseEnrollment
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import (
CourseEnrollmentFactory,
UserFactory,
@@ -144,7 +145,7 @@ class TestProgramProgressDetailView(ProgramsApiConfigMixin, SharedModuleStoreTes
@skip_unless_lms
class TestProgramsView(SharedModuleStoreTestCase, ProgramCacheMixin):
class TestProgramsEnterpriseView(SharedModuleStoreTestCase, ProgramCacheMixin):
"""Unit tests for the program details page."""
enterprise_uuid = str(uuid4())
@@ -196,7 +197,7 @@ class TestProgramsView(SharedModuleStoreTestCase, ProgramCacheMixin):
@with_site_configuration(configuration={"COURSE_CATALOG_API_URL": "foo"})
@override_settings(FEATURES=dict(ENABLE_ENTERPRISE_INTEGRATION=True))
@enterprise_is_enabled()
def test_program_list(self):
def test_program_list_enterprise(self):
"""
Verify API returns proper response.
"""
@@ -243,3 +244,92 @@ class TestProgramsView(SharedModuleStoreTestCase, ProgramCacheMixin):
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data, [])
@skip_unless_lms
class TestProgramsB2CView(SharedModuleStoreTestCase, ProgramCacheMixin):
"""Unit tests for the program details page."""
program_uuid = str(uuid4())
url = reverse_lazy("openedx.core.djangoapps.programs:v0:program_list_b2c")
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.user = UserFactory()
modulestore_course = ModuleStoreCourseFactory()
course_run = CourseRunFactory(key=str(modulestore_course.id))
course = CourseFactory(course_runs=[course_run])
CourseEnrollmentFactory(is_active=True, course_id=modulestore_course.id, user=cls.user)
cls.program = ProgramFactory(
uuid=cls.program_uuid,
courses=[course],
title="Journey to cooking",
type="MicroMasters",
authoring_organizations=[
{
"key": "MAX",
"logo_image_url": "http://test.org/media/organization/logos/test-logo.png",
}
],
)
cls.site = SiteFactory(domain="test.localhost")
def setUp(self):
super().setUp()
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)
self.set_program_in_catalog_cache(self.program_uuid, self.program)
ProgramEnrollmentFactory.create(
user=self.user,
program_uuid=self.program_uuid,
external_user_key="0001",
)
@with_site_configuration(configuration={"COURSE_CATALOG_API_URL": "foo"})
def test_program_list_b2c(self):
"""
Verify API returns proper response.
"""
cache.set(
SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site.domain),
[self.program_uuid],
None,
)
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
program = response.data[0]
assert len(program)
assert program["uuid"] == self.program["uuid"]
assert program["title"] == self.program["title"]
assert program["type"] == self.program["type"]
assert program["authoring_organizations"] == self.program["authoring_organizations"]
assert program["banner_image"] == self.program["banner_image"]
assert program["progress"] == {
"uuid": self.program["uuid"],
"completed": 0,
"in_progress": 0,
"not_started": 1,
"all_unenrolled": False,
}
@with_site_configuration(configuration={"COURSE_CATALOG_API_URL": "foo"})
def test_program_empty_list_if_no_enrollments(self):
"""
Verify API returns empty response if no enrollments exists for a learner.
"""
CourseEnrollment.objects.filter(user=self.user).delete()
cache.set(
SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site.domain),
[self.program_uuid],
None,
)
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data, [])

View File

@@ -18,6 +18,11 @@ urlpatterns = [
Programs.as_view(),
name="program_list",
),
re_path(
"^programs/$",
Programs.as_view(),
name="program_list_b2c",
),
re_path(
rf"^programs/(?P<program_uuid>{PROGRAM_UUID_PATTERN})/progress_details/$",
ProgramProgressDetailView.as_view(),

View File

@@ -33,8 +33,9 @@ class Programs(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request: "HttpRequest", enterprise_uuid: str) -> "HttpResponse":
"""For an enterprise learner, get list of enrolled programs with progress.
def get(self, request: "HttpRequest", enterprise_uuid: str = "") -> "HttpResponse":
"""For a learner, get list of enrolled programs with progress.
If an enterprise UUID ias provided, filter out all non-enterprise enrollments for the learner.
**Example Request**
@@ -89,8 +90,12 @@ class Programs(APIView):
"""
user: "AnonymousUser | User" = request.user
enrollments = list(self._get_enterprise_course_enrollments(enterprise_uuid, user))
# return empty reponse if no enterprise enrollments exists for a user
if enterprise_uuid:
enrollments = list(self._get_enterprise_course_enrollments(enterprise_uuid, user))
else:
enrollments = list(get_course_enrollments(user))
# return empty reponse if no enrollments exists for a user
if not enrollments:
return Response([])