From 295e18ec8f528d06123c0636aa85b356c046e696 Mon Sep 17 00:00:00 2001 From: alezconsultant Date: Wed, 4 Nov 2026 22:50:33 +0400 Subject: [PATCH] refactor: remove the ENABLE_COMPETENCY_MASTERY_TRACKING setting Record a learner's competency criterion status on every subsection grade write, with no setting to switch it on. In a deployment with no competency criteria the core call is one SELECT that returns no rows and queues no roll-up task, so the gate saved almost nothing and added a configuration step an operator could miss. The setting was added by #39179 and shipped in no named release. Operators tracking master who never set it now run the competency write on every grade, and a competency storage failure fails the grade write, as the design requires. Each scored subsection grade write now runs one extra SELECT, and the query-count assertions in the grades tests are raised to match. Related to openedx/openedx-core#924 Co-Authored-By: Claude Fable 5.1 --- .../rest_api/v1/tests/test_gradebook_views.py | 1 - lms/djangoapps/grades/subsection_grade.py | 7 ++---- lms/djangoapps/grades/tests/test_api.py | 2 -- .../grades/tests/test_course_grade_factory.py | 4 +-- .../grades/tests/test_subsection_grade.py | 25 ------------------- lms/djangoapps/grades/tests/test_tasks.py | 8 +++--- lms/envs/common.py | 11 -------- 7 files changed, 8 insertions(+), 50 deletions(-) diff --git a/lms/djangoapps/grades/rest_api/v1/tests/test_gradebook_views.py b/lms/djangoapps/grades/rest_api/v1/tests/test_gradebook_views.py index d592c0f033e2..98ece505fab3 100644 --- a/lms/djangoapps/grades/rest_api/v1/tests/test_gradebook_views.py +++ b/lms/djangoapps/grades/rest_api/v1/tests/test_gradebook_views.py @@ -1723,7 +1723,6 @@ def test_subsection_does_not_exist(self, login_method): assert status.HTTP_422_UNPROCESSABLE_ENTITY == resp.status_code assert expected_data == resp.data - @override_settings(ENABLE_COMPETENCY_MASTERY_TRACKING=True) def test_override_for_unattempted_learner_records_competency_status(self): """ A staff-entered grade counts toward competency status even though the learner never attempted the subsection. diff --git a/lms/djangoapps/grades/subsection_grade.py b/lms/djangoapps/grades/subsection_grade.py index 02ddb6744984..3fabeb67c942 100644 --- a/lms/djangoapps/grades/subsection_grade.py +++ b/lms/djangoapps/grades/subsection_grade.py @@ -9,7 +9,6 @@ from decimal import Decimal from logging import getLogger -from django.conf import settings from django.db import transaction from lazy import lazy from openedx_learning.api import GradedObjectScore, record_graded_object_statuses @@ -43,11 +42,9 @@ def _enqueue_competency_rollup(user_id, object_ids): def _record_competency_status(student, grades): """ - Records competency status inside the caller's transaction and returns the object ids to roll up, or an empty - list when tracking is off, nothing is scored, or no rows changed. + Records competency status inside the caller's transaction and returns the object ids to roll up. Returns an + empty list when nothing is scored or no status row changed. """ - if not settings.ENABLE_COMPETENCY_MASTERY_TRACKING: - return [] scores = [score for score in (grade.graded_object_score() for grade in grades) if score] if not scores or not record_graded_object_statuses(user_id=student.id, scores=scores): return [] diff --git a/lms/djangoapps/grades/tests/test_api.py b/lms/djangoapps/grades/tests/test_api.py index c80221f7d532..6bd5ae9a31be 100644 --- a/lms/djangoapps/grades/tests/test_api.py +++ b/lms/djangoapps/grades/tests/test_api.py @@ -5,7 +5,6 @@ from unittest.mock import patch import ddt -from django.test import override_settings from openedx_learning.api import GradedObjectScore from common.djangoapps.student.models import CourseEnrollment @@ -72,7 +71,6 @@ def tearDown(self): super().tearDown() PersistentSubsectionGradeOverride.objects.all().delete() - @override_settings(ENABLE_COMPETENCY_MASTERY_TRACKING=True) def test_override_without_existing_grade_records_competency_status(self): CourseEnrollment.enroll(self.user, self.course.id) new_subsection = BlockFactory.create(parent=self.course, category="sequential", display_name="New Subsection") diff --git a/lms/djangoapps/grades/tests/test_course_grade_factory.py b/lms/djangoapps/grades/tests/test_course_grade_factory.py index 201e931cf943..dda12e93fe8d 100644 --- a/lms/djangoapps/grades/tests/test_course_grade_factory.py +++ b/lms/djangoapps/grades/tests/test_course_grade_factory.py @@ -73,7 +73,7 @@ def _assert_section_order(course_grade): with self.assertNumQueries(3), mock_get_score(1, 2): _assert_read(expected_pass=False, expected_percent=0) # start off with grade of 0 - num_queries = 48 + num_queries = 50 with self.assertNumQueries(num_queries), mock_get_score(1, 2): grade_factory.update(self.request.user, self.course, force_update_subsections=True) @@ -87,7 +87,7 @@ def _assert_section_order(course_grade): with self.assertNumQueries(3): _assert_read(expected_pass=True, expected_percent=0.5) # NOT updated to grade of .25 - num_queries = 24 + num_queries = 26 with self.assertNumQueries(num_queries), mock_get_score(2, 2): grade_factory.update(self.request.user, self.course, force_update_subsections=True) diff --git a/lms/djangoapps/grades/tests/test_subsection_grade.py b/lms/djangoapps/grades/tests/test_subsection_grade.py index 87727e31ca10..223b8dd51e73 100644 --- a/lms/djangoapps/grades/tests/test_subsection_grade.py +++ b/lms/djangoapps/grades/tests/test_subsection_grade.py @@ -10,7 +10,6 @@ import pytest from ddt import data, ddt, unpack from django.db import DatabaseError, connection -from django.test import override_settings from kombu.exceptions import OperationalError as KombuOperationalError from openedx_catalog import api as catalog_api from openedx_catalog.models_api import CourseRun @@ -102,7 +101,6 @@ def _create_grade(self, subsection=None): return factory.create(subsection or self.sequence, read_only=True, force_calculate=True) -@override_settings(ENABLE_COMPETENCY_MASTERY_TRACKING=True) @ddt class SubsectionGradeCompetencyStatusTest(SubsectionGradeFixtureMixin, GradeTestBase): """ @@ -178,17 +176,6 @@ def test_unattempted_passes_no_score(self): mock_record.assert_not_called() - @override_settings(ENABLE_COMPETENCY_MASTERY_TRACKING=False) - def test_setting_off_saves_grades_without_calling_core(self): - with mock_get_score(1, 2), patch(RECORD_PATH) as mock_record: - self._create_grade().update_or_create_model(self.request.user) - CreateSubsectionGrade.bulk_create_models( - UserFactory(), [self._create_grade(self.sequence2)], self.course.id, - ) - - mock_record.assert_not_called() - assert PersistentSubsectionGrade.objects.count() == 2 - def test_bulk_path_records_each_qualifying_subsection(self): with mock_get_score(1, 2), patch(RECORD_PATH) as mock_record, patch(EMIT_PATH) as mock_emit: grades = CreateSubsectionGrade.bulk_create_models( @@ -278,17 +265,6 @@ def test_single_path_enqueue_kwargs_are_what_the_task_reads(self): user_id=self.request.user.id, object_ids=[str(self.sequence.location)], ) - @override_settings(ENABLE_COMPETENCY_MASTERY_TRACKING=False) - def test_setting_off_does_not_enqueue(self): - with mock_get_score(1, 2), patch(RECORD_PATH, return_value=1), patch(ENQUEUE_PATH) as mock_enqueue: - with self.captureOnCommitCallbacks(execute=True): - self._create_grade().update_or_create_model(self.request.user) - CreateSubsectionGrade.bulk_create_models( - UserFactory(), [self._create_grade(self.sequence2)], self.course.id, - ) - - mock_enqueue.assert_not_called() - def test_bulk_path_enqueues_one_rollup_with_every_scored_object(self): with mock_get_score(1, 2), patch(RECORD_PATH, return_value=2), patch(ENQUEUE_PATH) as mock_enqueue: with self.captureOnCommitCallbacks(execute=True): @@ -429,7 +405,6 @@ def _assert_tracker_emitted_event(self, tracker_mock, grade): ) -@override_settings(ENABLE_COMPETENCY_MASTERY_TRACKING=True) class SubsectionGradeCompetencyIntegrationTest(SubsectionGradeFixtureMixin, GradeTestBase): """ Grades a subsection against a real competency criterion and checks the learner status stored with the grade. diff --git a/lms/djangoapps/grades/tests/test_tasks.py b/lms/djangoapps/grades/tests/test_tasks.py index 7d892977203a..61fb616c2dff 100644 --- a/lms/djangoapps/grades/tests/test_tasks.py +++ b/lms/djangoapps/grades/tests/test_tasks.py @@ -158,8 +158,8 @@ def test_block_structure_created_only_once(self): assert mock_block_structure_create.call_count == 1 @ddt.data( - (ModuleStoreEnum.Type.split, 1, 49, True), - (ModuleStoreEnum.Type.split, 1, 49, False), + (ModuleStoreEnum.Type.split, 1, 50, True), + (ModuleStoreEnum.Type.split, 1, 50, False), ) @ddt.unpack def test_query_counts(self, default_store, num_mongo_calls, num_sql_calls, create_multiple_subsections): @@ -170,7 +170,7 @@ def test_query_counts(self, default_store, num_mongo_calls, num_sql_calls, creat self._apply_recalculate_subsection_grade() @ddt.data( - (ModuleStoreEnum.Type.split, 1, 49), + (ModuleStoreEnum.Type.split, 1, 50), ) @ddt.unpack def test_query_counts_dont_change_with_more_content(self, default_store, num_mongo_calls, num_sql_calls): @@ -258,7 +258,7 @@ def test_problem_block_with_restricted_access(self, mock_subsection_signal): UserPartition.scheme_extensions = None @ddt.data( - (ModuleStoreEnum.Type.split, 1, 49), + (ModuleStoreEnum.Type.split, 1, 50), ) @ddt.unpack def test_persistent_grades_on_course(self, default_store, num_mongo_queries, num_sql_queries): diff --git a/lms/envs/common.py b/lms/envs/common.py index 280eadf6e9da..a40555461278 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -677,17 +677,6 @@ # .. toggle_tickets: https://openedx.atlassian.net/browse/ENT-3818 ENABLE_COURSE_ASSESSMENT_GRADE_CHANGE_SIGNAL = False -# .. toggle_name: settings.ENABLE_COMPETENCY_MASTERY_TRACKING -# .. toggle_implementation: DjangoSetting -# .. toggle_default: False -# .. toggle_description: Set to True to update a learner's competency criterion status in the same transaction that -# persists a subsection grade. Also a safety valve: turning it off stops competency code from running on grade writes. -# .. toggle_warning: When enabled, a competency storage failure makes the grade write fail, and each grade write that -# changes a status queues a roll-up task. -# .. toggle_use_cases: opt_in -# .. toggle_creation_date: 2026-10-01 -ENABLE_COMPETENCY_MASTERY_TRACKING = False - # .. toggle_name: settings.ALLOW_ADMIN_ENTERPRISE_COURSE_ENROLLMENT_DELETION # .. toggle_implementation: DjangoSetting # .. toggle_default: False