From 764569f17120e3829e36f050698da213a55c3f64 Mon Sep 17 00:00:00 2001 From: Agrendalath Date: Fri, 12 Jun 2026 17:30:00 +0200 Subject: [PATCH] feat: allow hiding question count on the course outline page --- .../course_home_api/outline/serializers.py | 8 ++++-- .../outline/tests/test_view.py | 28 ++++++++++++++++++- lms/djangoapps/course_home_api/toggles.py | 22 +++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/course_home_api/outline/serializers.py b/lms/djangoapps/course_home_api/outline/serializers.py index cfa518138a95..abfbbd3d3ce9 100644 --- a/lms/djangoapps/course_home_api/outline/serializers.py +++ b/lms/djangoapps/course_home_api/outline/serializers.py @@ -3,11 +3,13 @@ """ from django.utils.translation import ngettext +from opaque_keys.edx.keys import UsageKey from rest_framework import serializers from lms.djangoapps.course_home_api.dates.serializers import DateSummarySerializer from lms.djangoapps.course_home_api.progress.serializers import CertificateDataSerializer from lms.djangoapps.course_home_api.serializers import DatesBannerSerializer, VerifiedModeSerializer +from lms.djangoapps.course_home_api.toggles import show_sequence_question_count class CourseBlockSerializer(serializers.Serializer): @@ -30,8 +32,10 @@ def get_blocks(self, block): # pylint: disable=missing-function-docstring scored = block.get('scored') if num_graded_problems and block_type == 'sequential': - questions = ngettext('({number} Question)', '({number} Questions)', num_graded_problems) - display_name += ' ' + questions.format(number=num_graded_problems) + course_key = UsageKey.from_string(block_key).course_key + if show_sequence_question_count(course_key): + questions = ngettext('({number} Question)', '({number} Questions)', num_graded_problems) + display_name += ' ' + questions.format(number=num_graded_problems) if graded and scored: icon = 'fa-pencil-square-o' diff --git a/lms/djangoapps/course_home_api/outline/tests/test_view.py b/lms/djangoapps/course_home_api/outline/tests/test_view.py index e9a3570ea6bd..d88b146875d9 100644 --- a/lms/djangoapps/course_home_api/outline/tests/test_view.py +++ b/lms/djangoapps/course_home_api/outline/tests/test_view.py @@ -21,7 +21,10 @@ from common.djangoapps.student.roles import CourseInstructorRole from common.djangoapps.student.tests.factories import UserFactory from lms.djangoapps.course_home_api.tests.utils import BaseCourseHomeTests -from lms.djangoapps.course_home_api.toggles import COURSE_HOME_SEND_COURSE_PROGRESS_ANALYTICS_FOR_STUDENT +from lms.djangoapps.course_home_api.toggles import ( + COURSE_HOME_DISABLE_SEQUENCE_QUESTION_COUNT, + COURSE_HOME_SEND_COURSE_PROGRESS_ANALYTICS_FOR_STUDENT, +) from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.content.learning_sequences.api import replace_course_outline @@ -317,6 +320,29 @@ def test_assignment(self): assert ungraded_data['display_name'] == 'Ungraded' assert ungraded_data['icon'] is None + @override_waffle_flag(COURSE_HOME_DISABLE_SEQUENCE_QUESTION_COUNT, active=True) + def test_assignment_question_count_disabled(self): + """ + Test that the "(N Questions)" suffix is omitted from sequential names when the flag is enabled. + """ + course = CourseFactory.create() + with self.store.bulk_operations(course.id): + chapter = BlockFactory.create(category='chapter', parent_location=course.location) + sequential = BlockFactory.create(display_name='Test', category='sequential', graded=True, has_score=True, + parent_location=chapter.location) + BlockFactory.create(category='problem', graded=True, has_score=True, parent_location=sequential.location) + BlockFactory.create(category='problem', graded=True, has_score=True, parent_location=sequential.location) + update_outline_from_modulestore(course.id) + url = reverse('course-home:outline-tab', args=[course.id]) + + CourseEnrollment.enroll(self.user, course.id) + response = self.client.get(url) + assert response.status_code == 200 + + exam_data = response.data['course_blocks']['blocks'][str(sequential.location)] + assert exam_data['display_name'] == 'Test' + assert exam_data['icon'] == 'fa-pencil-square-o' + @override_waffle_flag(COURSE_ENABLE_UNENROLLED_ACCESS_FLAG, active=True) @patch('lms.djangoapps.course_home_api.outline.views.generate_offer_data', new=Mock(return_value={'a': 1})) @patch('lms.djangoapps.course_home_api.outline.views.get_access_expiration_data', new=Mock(return_value={'b': 1})) diff --git a/lms/djangoapps/course_home_api/toggles.py b/lms/djangoapps/course_home_api/toggles.py index 052862796c75..9c0d293f72ee 100644 --- a/lms/djangoapps/course_home_api/toggles.py +++ b/lms/djangoapps/course_home_api/toggles.py @@ -51,6 +51,21 @@ ) +# Waffle flag to hide the graded problem count suffix (e.g. "(2 Questions)") from sequential names. +# +# .. toggle_name: course_home.disable_sequence_question_count +# .. toggle_implementation: CourseWaffleFlag +# .. toggle_default: False +# .. toggle_description: When enabled, the outline serializer will not append the "(N Questions)" suffix to +# graded sequential display names. +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2026-06-12 +# .. toggle_target_removal_date: None +COURSE_HOME_DISABLE_SEQUENCE_QUESTION_COUNT = CourseWaffleFlag( + f'{WAFFLE_FLAG_NAMESPACE}.disable_sequence_question_count', __name__ +) + + def course_home_mfe_progress_tab_is_active(course_key): # Avoiding a circular dependency from .models import DisableProgressPageStackedConfig @@ -73,3 +88,10 @@ def send_course_progress_analytics_for_student_is_enabled(course_key): Returns True if the course completion analytics feature is enabled for a given course. """ return COURSE_HOME_SEND_COURSE_PROGRESS_ANALYTICS_FOR_STUDENT.is_enabled(course_key) + + +def show_sequence_question_count(course_key): + """ + Returns True if the "(N Questions)" suffix should be appended to sequential names for the given course. + """ + return not COURSE_HOME_DISABLE_SEQUENCE_QUESTION_COUNT.is_enabled(course_key)