fix: clear block structure cache when usage key is not found (#33963)
This commit is contained in:
@@ -24,6 +24,8 @@ from common.djangoapps.util.date_utils import from_timestamp
|
||||
from lms.djangoapps.course_blocks.api import get_course_blocks
|
||||
from lms.djangoapps.courseware.model_data import get_score
|
||||
from lms.djangoapps.grades.config.models import ComputeGradesSetting
|
||||
from openedx.core.djangoapps.content.block_structure.api import clear_course_from_cache
|
||||
from openedx.core.djangoapps.content.block_structure.exceptions import UsageKeyNotInBlockStructure
|
||||
from openedx.core.djangoapps.content.course_overviews.models import \
|
||||
CourseOverview # lint-amnesty, pylint: disable=unused-import
|
||||
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
|
||||
@@ -44,6 +46,7 @@ KNOWN_RETRY_ERRORS = ( # Errors we expect occasionally, should be resolved on r
|
||||
DatabaseError,
|
||||
ValidationError,
|
||||
DatabaseNotReadyError,
|
||||
UsageKeyNotInBlockStructure,
|
||||
)
|
||||
RECALCULATE_GRADE_DELAY_SECONDS = 2 # to prevent excessive _has_db_updated failures. See TNL-6424.
|
||||
RETRY_DELAY_SECONDS = 40
|
||||
@@ -315,7 +318,8 @@ def _update_subsection_grades(
|
||||
student = User.objects.get(id=user_id)
|
||||
store = modulestore()
|
||||
with store.bulk_operations(course_key):
|
||||
course_structure = get_course_blocks(student, store.make_course_usage_key(course_key))
|
||||
course_usage_key = store.make_course_usage_key(course_key)
|
||||
course_structure = get_course_blocks(student, course_usage_key)
|
||||
subsections_to_update = course_structure.get_transformer_block_field(
|
||||
scored_block_usage_key,
|
||||
GradesTransformer,
|
||||
@@ -323,6 +327,17 @@ def _update_subsection_grades(
|
||||
set(),
|
||||
)
|
||||
|
||||
# Clear the course cache if access is restricted and course blocks are
|
||||
# cached without the restricted blocks.
|
||||
if not subsections_to_update:
|
||||
clear_course_from_cache(course_usage_key.course_key)
|
||||
raise UsageKeyNotInBlockStructure(
|
||||
"Scored block usage_key '{0}' is not found in the block_structure with root '{1}'".format(
|
||||
str(scored_block_usage_key),
|
||||
str(course_usage_key)
|
||||
)
|
||||
)
|
||||
|
||||
course = store.get_course(course_key, depth=0)
|
||||
subsection_grade_factory = SubsectionGradeFactory(student, course, course_structure)
|
||||
|
||||
|
||||
@@ -14,14 +14,13 @@ import pytz
|
||||
from django.db.utils import IntegrityError
|
||||
from django.utils import timezone
|
||||
from edx_toggles.toggles.testutils import override_waffle_flag
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory, check_mongo_calls
|
||||
from stevedore.extension import Extension, ExtensionManager
|
||||
|
||||
from common.djangoapps.student.models import CourseEnrollment, anonymous_id_for_user
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from common.djangoapps.track.event_transaction_utils import create_new_event_transaction_id, get_event_transaction_id
|
||||
from common.djangoapps.util.date_utils import to_timestamp
|
||||
from lms.djangoapps.courseware.tests.test_group_access import MemoryUserPartitionScheme
|
||||
from lms.djangoapps.grades import tasks
|
||||
from lms.djangoapps.grades.config.waffle import ENFORCE_FREEZE_GRADE_AFTER_COURSE_END
|
||||
from lms.djangoapps.grades.constants import ScoreDatabaseTableEnum
|
||||
@@ -36,6 +35,10 @@ from lms.djangoapps.grades.tasks import (
|
||||
recalculate_subsection_grade_v3
|
||||
)
|
||||
from openedx.core.djangoapps.content.block_structure.exceptions import BlockStructureNotFound
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import BlockFactory, CourseFactory, check_mongo_calls
|
||||
from xmodule.partitions.partitions import USER_PARTITION_SCHEME_NAMESPACE, Group, UserPartition
|
||||
|
||||
from .utils import mock_get_score
|
||||
|
||||
@@ -209,6 +212,48 @@ class RecalculateSubsectionGradeTest(HasCourseWithProblemsMixin, ModuleStoreTest
|
||||
{self.sequential.location, accessible_seq.location},
|
||||
)
|
||||
|
||||
@patch('lms.djangoapps.grades.signals.signals.SUBSECTION_SCORE_CHANGED.send')
|
||||
def test_problem_block_with_restricted_access(self, mock_subsection_signal):
|
||||
"""
|
||||
Test that `SUBSECTION_SCORE_CHANGED` is sent for a restricted problem block.
|
||||
"""
|
||||
self.set_up_course()
|
||||
|
||||
UserPartition.scheme_extensions = ExtensionManager.make_test_instance(
|
||||
[
|
||||
Extension(
|
||||
"memory",
|
||||
USER_PARTITION_SCHEME_NAMESPACE,
|
||||
MemoryUserPartitionScheme(),
|
||||
None
|
||||
)
|
||||
],
|
||||
namespace=USER_PARTITION_SCHEME_NAMESPACE
|
||||
)
|
||||
verified_group = Group(60, 'verified')
|
||||
verified_partition = UserPartition(
|
||||
0,
|
||||
'Verified Partition',
|
||||
'Verified Learners',
|
||||
[verified_group],
|
||||
scheme=UserPartition.get_scheme("memory"),
|
||||
)
|
||||
|
||||
accessible_seq = BlockFactory.create(parent=self.chapter, category='sequential')
|
||||
restricted_problem = BlockFactory.create(
|
||||
parent=accessible_seq,
|
||||
category='problem',
|
||||
display_name='Restricted Problem',
|
||||
group_access={verified_partition.id: [verified_group.id]}
|
||||
)
|
||||
|
||||
self.recalculate_subsection_grade_kwargs['usage_id'] = str(restricted_problem.location)
|
||||
verified_partition.scheme.set_group_for_user(self.user, verified_partition, verified_group)
|
||||
|
||||
self._apply_recalculate_subsection_grade()
|
||||
assert mock_subsection_signal.call_count == 1
|
||||
UserPartition.scheme_extensions = None
|
||||
|
||||
@ddt.data(
|
||||
(ModuleStoreEnum.Type.split, 2, 41),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user