Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix _update_subsection_grades when restricted blocks are not cached #33963

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 16 additions & 1 deletion lms/djangoapps/grades/tasks.py
Expand Up @@ -24,6 +24,8 @@
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
Expand All @@ -44,6 +46,7 @@
DatabaseError,
ValidationError,
DatabaseNotReadyError,
UsageKeyNotInBlockStructure,
)
RECALCULATE_GRADE_DELAY_SECONDS = 2 # to prevent excessive _has_db_updated failures. See TNL-6424.
RETRY_DELAY_SECONDS = 40
Expand Down Expand Up @@ -315,14 +318,26 @@ 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,
'subsections',
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)

Expand Down
51 changes: 48 additions & 3 deletions lms/djangoapps/grades/tests/test_tasks.py
Expand Up @@ -14,14 +14,13 @@
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
Expand All @@ -36,6 +35,10 @@
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

Expand Down Expand Up @@ -209,6 +212,48 @@ def test_other_inaccessible_subsection(self, mock_subsection_signal):
{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),
)
Expand Down