fix: clear block structure cache when usage key is not found (#33963)

This commit is contained in:
Asad Ali
2024-03-13 01:30:44 +05:00
committed by GitHub
parent 794c6781fa
commit 19c04fcf03
2 changed files with 64 additions and 4 deletions

View File

@@ -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)

View File

@@ -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),
)