From 1f6b16c99ae7b52f8ea68c4ee6c8e7345b2d57fd Mon Sep 17 00:00:00 2001 From: Muhammad Arslan Abdul Rauf Date: Tue, 31 Mar 2026 15:17:59 +0500 Subject: [PATCH] fix: add weight from max_weight if its missing from meta --- .../contentstore/views/tests/test_block.py | 78 +++++++++++++++++++ .../xblock_storage_handlers/view_handlers.py | 22 +++++- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/cms/djangoapps/contentstore/views/tests/test_block.py b/cms/djangoapps/contentstore/views/tests/test_block.py index 31f5244e2f55..7bf2547b77e5 100644 --- a/cms/djangoapps/contentstore/views/tests/test_block.py +++ b/cms/djangoapps/contentstore/views/tests/test_block.py @@ -79,6 +79,7 @@ ALWAYS, VisibilityState, get_block_info, + _get_metadata_with_problem_defaults, _get_source_index, _xblock_type_and_display_name, add_container_page_publishing_info, @@ -3514,6 +3515,83 @@ def validate_xblock_info_consistency( self.assertIsNone(xblock_info.get("child_info", None)) +class TestGetMetadataWithProblemDefaults(ModuleStoreTestCase): + """ + Unit tests for _get_metadata_with_problem_defaults. + + The helper must inject a ``weight`` value (derived from ``max_score()``) for + problem xblocks that have never had ``weight`` explicitly saved, while leaving + every other combination untouched. + """ + + def _make_problem(self, **kwargs): + """Create and return a problem xblock from the modulestore.""" + course = CourseFactory.create() + block = BlockFactory.create( + parent_location=course.location, + category='problem', + display_name='A Problem', + **kwargs, + ) + return modulestore().get_item(block.location) + + # ------------------------------------------------------------------ + # Problem blocks – weight absent from stored metadata + # ------------------------------------------------------------------ + + def test_problem_without_weight_adds_weight_from_max_score(self): + """ + When weight is absent and max_score() > 0, it is injected into metadata. + """ + xblock = self._make_problem() + with patch.object(xblock, 'max_score', return_value=3.0): + metadata = _get_metadata_with_problem_defaults(xblock) + self.assertEqual(metadata.get('weight'), 3.0) + + def test_problem_without_weight_max_score_zero_does_not_inject(self): + """ + A zero max_score will not inject a weight. + """ + xblock = self._make_problem() + with patch.object(xblock, 'max_score', return_value=0): + metadata = _get_metadata_with_problem_defaults(xblock) + self.assertNotIn('weight', metadata) + + # ------------------------------------------------------------------ + # Problem blocks – weight already present in stored metadata + # ------------------------------------------------------------------ + + def test_problem_with_explicit_weight_is_preserved(self): + """ + When weight is already explicitly set, it will not be overwritten. + """ + xblock = self._make_problem(weight=5.0) + with patch.object(xblock, 'max_score', return_value=2.0): + metadata = _get_metadata_with_problem_defaults(xblock) + self.assertEqual(metadata.get('weight'), 5.0) + + # ------------------------------------------------------------------ + # Non-problem blocks + # ------------------------------------------------------------------ + + def test_non_problem_block_is_unmodified(self): + """ + Non-problem blocks must pass through untouched even if a max_score + method is available on them. + """ + course = CourseFactory.create() + video = BlockFactory.create( + parent_location=course.location, + category='video', + display_name='A Video', + ) + xblock = modulestore().get_item(video.location) + metadata_before = dict(get_block_info(xblock).get('metadata', {})) + metadata_result = _get_metadata_with_problem_defaults(xblock) + self.assertEqual(metadata_result, metadata_before) + self.assertNotIn('weight', metadata_result) + + @patch.dict("django.conf.settings.FEATURES", {"ENABLE_SPECIAL_EXAMS": True}) @ddt.ddt class TestSpecialExamXBlockInfo(ItemTest): diff --git a/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py b/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py index 9a30807b60a3..8e3bd45b9186 100644 --- a/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py +++ b/cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py @@ -403,6 +403,26 @@ def modify_xblock(usage_key, request): ) +def _get_metadata_with_problem_defaults(xblock): + """ + Returns own_metadata for the xblock, injecting a ``weight`` default for + problem blocks whose weight has never been explicitly saved. + + Without this, the frontend falls back to displaying 1 (its own default) + even when the problem's actual point value differs. If ``max_score()`` + returns a positive number, we inject it so the correct value is shown. + """ + metadata = own_metadata(xblock) + if xblock.scope_ids.block_type == 'problem' and 'weight' not in metadata: + try: + max_score_value = xblock.max_score() + if max_score_value and max_score_value > 0: + metadata['weight'] = float(max_score_value) + except Exception: # pylint: disable=broad-except + pass + return metadata + + def save_xblock_with_callback(xblock, user, old_metadata=None, old_content=None): """ Updates the xblock in the modulestore. @@ -1079,7 +1099,7 @@ def get_block_info( xblock_info = create_xblock_info( xblock, data=data, - metadata=own_metadata(xblock), + metadata=_get_metadata_with_problem_defaults(xblock), include_ancestor_info=include_ancestor_info, include_children_predicate=include_children_predicate )