From 9c110c6a1dfe801e2ad22b5a30d1af24efe10336 Mon Sep 17 00:00:00 2001 From: Daniel Wong Date: Thu, 30 Apr 2026 13:36:29 -0600 Subject: [PATCH] fix: adjust gradebook URL to correct value (#38479) --- lms/djangoapps/instructor/tests/test_api_v2.py | 18 ++++++++++++++---- .../instructor/views/serializers_v2.py | 14 +++++++++----- lms/envs/devstack.py | 2 +- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/lms/djangoapps/instructor/tests/test_api_v2.py b/lms/djangoapps/instructor/tests/test_api_v2.py index f2565b6bad5f..2cb125e04dfc 100644 --- a/lms/djangoapps/instructor/tests/test_api_v2.py +++ b/lms/djangoapps/instructor/tests/test_api_v2.py @@ -119,14 +119,22 @@ def _get_url(self, course_id=None): course_id = str(self.course_key) return reverse('instructor_api_v2:course_metadata', kwargs={'course_id': course_id}) - @override_settings(COURSE_AUTHORING_MICROFRONTEND_URL='http://localhost:2001/authoring') - @override_settings(ADMIN_CONSOLE_MICROFRONTEND_URL='http://localhost:2025/admin-console') + @override_settings( + COURSE_AUTHORING_MICROFRONTEND_URL='http://localhost:2001/authoring', + ADMIN_CONSOLE_MICROFRONTEND_URL='http://localhost:2025/admin-console', + # intentionally include trailing slash to test URL joining logic + WRITABLE_GRADEBOOK_URL='http://localhost:1994/gradebook/', + ) def test_get_course_metadata_as_instructor(self): """ Test that an instructor can retrieve comprehensive course metadata. """ - self.client.force_authenticate(user=self.instructor) - response = self.client.get(self._get_url()) + with patch( + 'lms.djangoapps.instructor.views.serializers_v2.is_writable_gradebook_enabled', + return_value=True, + ): + self.client.force_authenticate(user=self.instructor) + response = self.client.get(self._get_url()) assert response.status_code == status.HTTP_200_OK data = response.data @@ -176,9 +184,11 @@ def test_get_course_metadata_as_instructor(self): assert 'analytics_dashboard_message' in data assert 'studio_grading_url' in data assert 'admin_console_url' in data + assert 'gradebook_url' in data assert data['studio_grading_url'] == f'http://localhost:2001/authoring/course/{self.course.id}/settings/grading' assert data['admin_console_url'] == 'http://localhost:2025/admin-console/authz' + assert data['gradebook_url'] == f'http://localhost:1994/gradebook/{self.course.id}' @override_settings(ADMIN_CONSOLE_MICROFRONTEND_URL='http://localhost:2025/admin-console') def test_admin_console_url_requires_instructor_access(self): diff --git a/lms/djangoapps/instructor/views/serializers_v2.py b/lms/djangoapps/instructor/views/serializers_v2.py index 89fa151716c5..fc2acaf84d92 100644 --- a/lms/djangoapps/instructor/views/serializers_v2.py +++ b/lms/djangoapps/instructor/views/serializers_v2.py @@ -459,9 +459,13 @@ def get_studio_url(self, data): def get_gradebook_url(self, data): """Get MFE gradebook URL for the course.""" course_key = data['course'].id - if is_writable_gradebook_enabled(course_key) and settings.WRITABLE_GRADEBOOK_URL: - return f'{settings.WRITABLE_GRADEBOOK_URL}/gradebook/{course_key}' - return None + mfe_base_url = configuration_helpers.get_value( + 'WRITABLE_GRADEBOOK_URL', + getattr(settings, 'WRITABLE_GRADEBOOK_URL', None) + ) + if not is_writable_gradebook_enabled(course_key) or not mfe_base_url: + return None + return f'{mfe_base_url.rstrip("/")}/{course_key}' def get_studio_grading_url(self, data): """Get Studio MFE grading settings URL for the course.""" @@ -472,7 +476,7 @@ def get_studio_grading_url(self, data): ) if not mfe_base_url: return None - return f'{mfe_base_url}/course/{course_key}/settings/grading' + return f'{mfe_base_url.rstrip("/")}/course/{course_key}/settings/grading' def get_admin_console_url(self, data): """Get admin console URL (requires instructor access and MFE configuration, null if not accessible).""" @@ -486,7 +490,7 @@ def get_admin_console_url(self, data): has_permissions = request.user.is_staff or has_instructor_access if not mfe_base_url or not has_permissions: return None - return f'{mfe_base_url}/authz' + return f'{mfe_base_url.rstrip("/")}/authz' def get_disable_buttons(self, data): """Check if buttons should be disabled for large courses.""" diff --git a/lms/envs/devstack.py b/lms/envs/devstack.py index 806640a918a3..9ca6f57ba204 100644 --- a/lms/envs/devstack.py +++ b/lms/envs/devstack.py @@ -278,7 +278,7 @@ def should_show_debug_toolbar(request): # lint-amnesty, pylint: disable=missing ENTERPRISE_ADMIN_PORTAL_BASE_URL = 'http://' + ENTERPRISE_ADMIN_PORTAL_NETLOC ########################## GRADEBOOK APP ############################## -WRITABLE_GRADEBOOK_URL = 'http://localhost:1994' +WRITABLE_GRADEBOOK_URL = 'http://localhost:1994/gradebook' ########################## ORA STAFF GRADING APP ############################## ORA_GRADING_MICROFRONTEND_URL = 'http://localhost:1993'