diff --git a/cms/djangoapps/contentstore/models.py b/cms/djangoapps/contentstore/models.py index d80517c2a842..21ed253cbd79 100644 --- a/cms/djangoapps/contentstore/models.py +++ b/cms/djangoapps/contentstore/models.py @@ -17,7 +17,12 @@ from opaque_keys.edx.locator import LibraryContainerLocator from openedx_content.api import get_published_version from openedx_content.models_api import Component, Container -from openedx_django_lib.fields import immutable_uuid_field, manual_date_time_field, ref_field + +try: + from openedx_django_lib.fields import immutable_uuid_field, manual_date_time_field, ref_field +except ImportError: # pragma: no cover - runtime compatibility shim for different openedx_django_lib versions + from openedx_django_lib.fields import immutable_uuid_field, manual_date_time_field + from openedx_django_lib.fields import key_field as ref_field logger = logging.getLogger(__name__) diff --git a/cms/djangoapps/contentstore/rest_api/v1/serializers/course_waffle_flags.py b/cms/djangoapps/contentstore/rest_api/v1/serializers/course_waffle_flags.py index 46f8cebd140d..5e614dda77a8 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/serializers/course_waffle_flags.py +++ b/cms/djangoapps/contentstore/rest_api/v1/serializers/course_waffle_flags.py @@ -81,10 +81,15 @@ def get_use_new_advanced_settings_page(self, obj): def get_use_new_grading_page(self, obj): """ - Method to get the use_new_grading_page switch + Method to indicate whether we should use the new grading page. + + This used to be based on a waffle flag but the flag is being removed so we + default it to true for now until we can remove the need for it from the consumers + of this serializer and the related APIs. + + See https://github.com/openedx/edx-platform/issues/36275 """ - course_key = self.get_course_key() - return toggles.use_new_grading_page(course_key) + return True def get_use_new_updates_page(self, obj): """ diff --git a/cms/djangoapps/contentstore/tests/test_contentstore.py b/cms/djangoapps/contentstore/tests/test_contentstore.py index 5c33e156721b..a5346dfd675c 100644 --- a/cms/djangoapps/contentstore/tests/test_contentstore.py +++ b/cms/djangoapps/contentstore/tests/test_contentstore.py @@ -1460,7 +1460,11 @@ def test_get_html(handler): resp = self.client.get_html( get_url(handler, course_key, 'course_key_string') ) - self.assertEqual(resp.status_code, 200) # noqa: PT009 + # When grading is routed to the authoring MFE, we expect a redirect. + if handler == 'grading_handler': + self.assertEqual(resp.status_code, 302) # noqa: PT009 + else: + self.assertEqual(resp.status_code, 200) # noqa: PT009 def test_get_json(handler): # Helper function for getting HTML for a page in Studio and @@ -1495,7 +1499,7 @@ def test_get_json(handler): test_get_html('course_team_handler') with override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True): test_get_html('settings_handler') - with override_waffle_flag(toggles.LEGACY_STUDIO_GRADING, True): + with override_settings(COURSE_AUTHORING_MICROFRONTEND_URL='https://mfe.example'): test_get_html('grading_handler') with override_waffle_flag(toggles.LEGACY_STUDIO_ADVANCED_SETTINGS, True): test_get_html('advanced_settings_handler') diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index 5d726627f27e..bf31759b7744 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -168,7 +168,6 @@ def test_discussion_fields_available(self, is_pages_and_resources_enabled, @override_waffle_flag(toggles.LEGACY_STUDIO_EXPORT, True) @override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_TEAM, True) @override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True) - @override_waffle_flag(toggles.LEGACY_STUDIO_GRADING, True) def test_disable_advanced_settings_feature(self, disable_advanced_settings): """ If this feature is enabled, only Django Staff/Superuser should be able to access the "Advanced Settings" page. @@ -178,7 +177,7 @@ def test_disable_advanced_settings_feature(self, disable_advanced_settings): with override_settings(FEATURES={ 'DISABLE_ADVANCED_SETTINGS': disable_advanced_settings, - }): + }, COURSE_AUTHORING_MICROFRONTEND_URL='https://mfe.example'): for handler in ( 'import_handler', 'export_handler', @@ -190,18 +189,28 @@ def test_disable_advanced_settings_feature(self, disable_advanced_settings): response = self.non_staff_client.get_html( get_url(self.course.id, handler) ) - self.assertEqual(response.status_code, 200) # noqa: PT009 + # grading_handler is served by the authoring MFE when configured, + # so expect a redirect (302) in that case. + if handler == 'grading_handler': + self.assertEqual(response.status_code, 302) # noqa: PT009 + else: + self.assertEqual(response.status_code, 200) # noqa: PT009 if disable_advanced_settings: - self.assertNotIn(advanced_settings_link_html, response.content) # noqa: PT009 + if handler != 'grading_handler': + self.assertNotIn(advanced_settings_link_html, response.content) # noqa: PT009 else: - self.assertIn(advanced_settings_link_html, response.content) # noqa: PT009 + if handler != 'grading_handler': + self.assertIn(advanced_settings_link_html, response.content) # noqa: PT009 # Test that staff users see the "Advanced Settings" tab link. response = self.client.get_html( get_url(self.course.id, handler) ) - self.assertEqual(response.status_code, 200) # noqa: PT009 - self.assertIn(advanced_settings_link_html, response.content) # noqa: PT009 + if handler == 'grading_handler': + self.assertEqual(response.status_code, 302) # noqa: PT009 + else: + self.assertEqual(response.status_code, 200) # noqa: PT009 + self.assertIn(advanced_settings_link_html, response.content) # noqa: PT009 # Test that non-staff users can't access the "Advanced Settings" page. response = self.non_staff_client.get_html(self.course_setting_url) diff --git a/cms/djangoapps/contentstore/toggles.py b/cms/djangoapps/contentstore/toggles.py index 30a030f966a9..df35864b00d8 100644 --- a/cms/djangoapps/contentstore/toggles.py +++ b/cms/djangoapps/contentstore/toggles.py @@ -216,24 +216,6 @@ def use_new_advanced_settings_page(course_key): return not LEGACY_STUDIO_ADVANCED_SETTINGS.is_enabled(course_key) -# .. toggle_name: legacy_studio.grading -# .. toggle_implementation: WaffleFlag -# .. toggle_default: False -# .. toggle_description: Temporarily fall back to the old Studio Course Grading page. -# .. toggle_use_cases: temporary -# .. toggle_creation_date: 2025-03-14 -# .. toggle_target_removal_date: 2025-09-14 -# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275 -# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available. -LEGACY_STUDIO_GRADING = CourseWaffleFlag('legacy_studio.grading', __name__) - - -def use_new_grading_page(course_key): - """ - Returns a boolean if new studio grading mfe is enabled - """ - return not LEGACY_STUDIO_GRADING.is_enabled(course_key) - # .. toggle_name: legacy_studio.import # .. toggle_implementation: WaffleFlag diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 35818125efab..275fe02baf9c 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -45,7 +45,6 @@ use_new_certificates_page, use_new_course_team_page, use_new_export_page, - use_new_grading_page, use_new_group_configurations_page, use_new_import_page, use_new_schedule_details_page, @@ -330,13 +329,10 @@ def get_grading_url(course_locator) -> str: """ Gets course authoring microfrontend URL for grading page view. """ - grading_url = None - if use_new_grading_page(course_locator): - mfe_base_url = get_course_authoring_url(course_locator) - course_mfe_url = f'{mfe_base_url}/course/{course_locator}/settings/grading' - if mfe_base_url: - grading_url = course_mfe_url - return grading_url + mfe_base_url = get_course_authoring_url(course_locator) + if mfe_base_url: + return f'{mfe_base_url}/course/{course_locator}/settings/grading' + return None def get_course_team_url(course_locator) -> str: diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 3d248944bdd5..7db0b0bf4e32 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -100,14 +100,12 @@ from ..toggles import ( default_enable_flexible_peer_openassessments, use_new_advanced_settings_page, - use_new_grading_page, use_new_group_configurations_page, use_new_schedule_details_page, ) from ..utils import ( add_instructor, get_advanced_settings_url, - get_course_grading, get_course_outline_url, get_course_rerun_context, get_course_settings, @@ -1425,10 +1423,7 @@ def grading_handler(request, course_key_string, grader_index=None): raise PermissionDenied() if 'text/html' in request.META.get('HTTP_ACCEPT', '') and request.method == 'GET': - if use_new_grading_page(course_key): - return redirect(get_grading_url(course_key)) - grading_context = get_course_grading(course_key) - return render_to_response('settings_graders.html', grading_context) + return redirect(get_grading_url(course_key)) elif 'application/json' in request.META.get('HTTP_ACCEPT', ''): if request.method == 'GET': if grader_index is None: diff --git a/cms/djangoapps/contentstore/views/tests/test_exam_settings_view.py b/cms/djangoapps/contentstore/views/tests/test_exam_settings_view.py index ef1d7f945dd7..4f3f9663db0a 100644 --- a/cms/djangoapps/contentstore/views/tests/test_exam_settings_view.py +++ b/cms/djangoapps/contentstore/views/tests/test_exam_settings_view.py @@ -27,8 +27,8 @@ @override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True) @override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True) @override_waffle_flag(toggles.LEGACY_STUDIO_CONFIGURATIONS, True) -@override_waffle_flag(toggles.LEGACY_STUDIO_GRADING, True) @override_waffle_flag(toggles.LEGACY_STUDIO_ADVANCED_SETTINGS, True) +@override_settings(COURSE_AUTHORING_MICROFRONTEND_URL='https://mfe.example') class TestExamSettingsView(CourseTestCase, UrlResetMixin): """ Unit tests for the exam settings view. @@ -64,8 +64,12 @@ def test_view_without_exam_settings_enabled(self, handler): """ outline_url = reverse_course_url(handler, self.course.id) resp = self.client.get(outline_url, HTTP_ACCEPT='text/html') - self.assertEqual(resp.status_code, 200) # noqa: PT009 - self.assertNotContains(resp, 'Proctored Exam Settings') + # grading_handler is routed to the authoring MFE and returns a redirect. + if handler == 'grading_handler': + self.assertEqual(resp.status_code, 302) # noqa: PT009 + else: + self.assertEqual(resp.status_code, 200) # noqa: PT009 + self.assertNotContains(resp, 'Proctored Exam Settings') @ddt.data( "certificates_list_handler", @@ -81,8 +85,12 @@ def test_view_with_exam_settings_enabled(self, handler): """ outline_url = reverse_course_url(handler, self.course.id) resp = self.client.get(outline_url, HTTP_ACCEPT='text/html') - self.assertEqual(resp.status_code, 200) # noqa: PT009 - self.assertContains(resp, 'Proctored Exam Settings') + # grading_handler is routed to the authoring MFE and returns a redirect. + if handler == 'grading_handler': + self.assertEqual(resp.status_code, 302) # noqa: PT009 + else: + self.assertEqual(resp.status_code, 200) # noqa: PT009 + self.assertContains(resp, 'Proctored Exam Settings') @override_settings( PROCTORING_BACKENDS={ diff --git a/cms/static/cms/js/build.js b/cms/static/cms/js/build.js index 73089ed04a76..f9f9bccea342 100644 --- a/cms/static/cms/js/build.js +++ b/cms/static/cms/js/build.js @@ -27,8 +27,7 @@ 'js/factories/manage_users', 'js/factories/outline', 'js/factories/settings', - 'js/factories/settings_advanced', - 'js/factories/settings_graders' + 'js/factories/settings_advanced' ]), /** * By default all the configuration for optimization happens from the command diff --git a/cms/static/js/factories/settings_graders.js b/cms/static/js/factories/settings_graders.js deleted file mode 100644 index 61b8fcbcad03..000000000000 --- a/cms/static/js/factories/settings_graders.js +++ /dev/null @@ -1,27 +0,0 @@ -define([ - 'jquery', 'js/views/settings/grading', 'js/models/settings/course_grading_policy' -], function($, GradingView, CourseGradingPolicyModel) { - 'use strict'; - - return function(courseDetails, gradingUrl, courseAssignmentLists, gradeDesignations) { - var model, editor; - - $('form :input') - .focus(function() { - $('label[for="' + this.id + '"]').addClass('is-focused'); - }) - .blur(function() { - $('label').removeClass('is-focused'); - }); - - model = new CourseGradingPolicyModel(courseDetails, {parse: true}); - model.urlRoot = gradingUrl; - editor = new GradingView({ - el: $('.settings-grading'), - model: model, - courseAssignmentLists: courseAssignmentLists, - gradeDesignations: gradeDesignations - }); - editor.render(); - }; -}); diff --git a/cms/templates/settings_graders.html b/cms/templates/settings_graders.html deleted file mode 100644 index eb0a057b046e..000000000000 --- a/cms/templates/settings_graders.html +++ /dev/null @@ -1,185 +0,0 @@ -<%page expression_filter="h"/> -<%inherit file="base.html" /> -<%def name="online_help_token()"><% return "grading" %>%def> -<%block name="title">${_("Grading Settings")}%block> -<%block name="bodyclass">is-signedin course grading view-settings%block> - -<%namespace name='static' file='static_content.html'/> -<%! - from six.moves.urllib.parse import quote - import json - from cms.djangoapps.contentstore import utils - from django.utils.translation import gettext as _ - from common.djangoapps.student.auth import has_studio_advanced_settings_access - from cms.djangoapps.models.settings.encoder import CourseSettingsEncoder - from openedx.core.djangolib.js_utils import ( - dump_js_escaped_json, js_escaped_string - ) -%> - -<%block name="header_extras"> -% for template_name in ["course_grade_policy", "course_grade_cutoff"]: - -% endfor -%block> - -<%block name="jsextra"> - -%block> -<%block name="requirejs"> - require(["js/factories/settings_graders"], function(SettingsGradersFactory) { - SettingsGradersFactory( - _.extend( - ${dump_js_escaped_json(course_details, cls=CourseSettingsEncoder) | n, decode.utf8}, - { is_credit_course: ${is_credit_course | n, dump_js_escaped_json} } - ), - "${grading_url | n, js_escaped_string}", - ${course_assignment_lists | n, dump_js_escaped_json}, - ${default_grade_designations | n, dump_js_escaped_json}, - ); - }); -%block> - -<%block name="content"> -