Skip to content

Commit a2c2f71

Browse files
fix: add weight from max_weight if its missing from meta
1 parent 7bcf9f8 commit a2c2f71

2 files changed

Lines changed: 99 additions & 1 deletion

File tree

cms/djangoapps/contentstore/views/tests/test_block.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import (
3939
ALWAYS,
4040
VisibilityState,
41+
_get_metadata_with_problem_defaults,
4142
_get_source_index,
4243
_xblock_type_and_display_name,
4344
add_container_page_publishing_info,
@@ -3501,6 +3502,83 @@ def validate_xblock_info_consistency(
35013502
self.assertIsNone(xblock_info.get("child_info", None))
35023503

35033504

3505+
class TestGetMetadataWithProblemDefaults(ModuleStoreTestCase):
3506+
"""
3507+
Unit tests for _get_metadata_with_problem_defaults.
3508+
3509+
The helper must inject a ``weight`` value (derived from ``max_score()``) for
3510+
problem xblocks that have never had ``weight`` explicitly saved, while leaving
3511+
every other combination untouched.
3512+
"""
3513+
3514+
def _make_problem(self, **kwargs):
3515+
"""Create and return a problem xblock from the modulestore."""
3516+
course = CourseFactory.create()
3517+
block = BlockFactory.create(
3518+
parent_location=course.location,
3519+
category='problem',
3520+
display_name='A Problem',
3521+
**kwargs,
3522+
)
3523+
return modulestore().get_item(block.location)
3524+
3525+
# ------------------------------------------------------------------
3526+
# Problem blocks – weight absent from stored metadata
3527+
# ------------------------------------------------------------------
3528+
3529+
def test_problem_without_weight_adds_weight_from_max_score(self):
3530+
"""
3531+
When weight is absent and max_score() > 0, it is injected into metadata.
3532+
"""
3533+
xblock = self._make_problem()
3534+
with patch.object(xblock, 'max_score', return_value=3.0):
3535+
metadata = _get_metadata_with_problem_defaults(xblock)
3536+
self.assertEqual(metadata.get('weight'), 3.0)
3537+
3538+
def test_problem_without_weight_max_score_zero_does_not_inject(self):
3539+
"""
3540+
A zero max_score will not inject a weight.
3541+
"""
3542+
xblock = self._make_problem()
3543+
with patch.object(xblock, 'max_score', return_value=0):
3544+
metadata = _get_metadata_with_problem_defaults(xblock)
3545+
self.assertNotIn('weight', metadata)
3546+
3547+
# ------------------------------------------------------------------
3548+
# Problem blocks – weight already present in stored metadata
3549+
# ------------------------------------------------------------------
3550+
3551+
def test_problem_with_explicit_weight_is_preserved(self):
3552+
"""
3553+
When weight is already explicitly set, it will not be overwritten.
3554+
"""
3555+
xblock = self._make_problem(weight=5.0)
3556+
with patch.object(xblock, 'max_score', return_value=2.0):
3557+
metadata = _get_metadata_with_problem_defaults(xblock)
3558+
self.assertEqual(metadata.get('weight'), 5.0)
3559+
3560+
# ------------------------------------------------------------------
3561+
# Non-problem blocks
3562+
# ------------------------------------------------------------------
3563+
3564+
def test_non_problem_block_is_unmodified(self):
3565+
"""
3566+
Non-problem blocks must pass through untouched even if a max_score
3567+
method is available on them.
3568+
"""
3569+
course = CourseFactory.create()
3570+
video = BlockFactory.create(
3571+
parent_location=course.location,
3572+
category='video',
3573+
display_name='A Video',
3574+
)
3575+
xblock = modulestore().get_item(video.location)
3576+
metadata_before = dict(get_block_info(xblock).get('metadata', {}))
3577+
metadata_result = _get_metadata_with_problem_defaults(xblock)
3578+
self.assertEqual(metadata_result, metadata_before)
3579+
self.assertNotIn('weight', metadata_result)
3580+
3581+
35043582
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_SPECIAL_EXAMS": True})
35053583
@ddt.ddt
35063584
class TestSpecialExamXBlockInfo(ItemTest):

cms/djangoapps/contentstore/xblock_storage_handlers/view_handlers.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,26 @@ def modify_xblock(usage_key, request):
398398
)
399399

400400

401+
def _get_metadata_with_problem_defaults(xblock):
402+
"""
403+
Returns own_metadata for the xblock, injecting a ``weight`` default for
404+
problem blocks whose weight has never been explicitly saved.
405+
406+
Without this, the frontend falls back to displaying 1 (its own default)
407+
even when the problem's actual point value differs. If ``max_score()``
408+
returns a positive number, we inject it so the correct value is shown.
409+
"""
410+
metadata = own_metadata(xblock)
411+
if xblock.scope_ids.block_type == 'problem' and 'weight' not in metadata:
412+
try:
413+
max_score_value = xblock.max_score()
414+
if max_score_value and max_score_value > 0:
415+
metadata['weight'] = float(max_score_value)
416+
except Exception: # pylint: disable=broad-except
417+
pass
418+
return metadata
419+
420+
401421
def save_xblock_with_callback(xblock, user, old_metadata=None, old_content=None):
402422
"""
403423
Updates the xblock in the modulestore.
@@ -1074,7 +1094,7 @@ def get_block_info(
10741094
xblock_info = create_xblock_info(
10751095
xblock,
10761096
data=data,
1077-
metadata=own_metadata(xblock),
1097+
metadata=_get_metadata_with_problem_defaults(xblock),
10781098
include_ancestor_info=include_ancestor_info,
10791099
include_children_predicate=include_children_predicate
10801100
)

0 commit comments

Comments
 (0)