Merge pull request #16048 from edx/sstudent/EDUCATOR-1288
Sstudent/educator 1288
This commit is contained in:
@@ -11,8 +11,10 @@ from xblock.scorable import ScorableXBlockMixin, Score
|
||||
from courseware.model_data import get_score, set_score
|
||||
from eventtracking import tracker
|
||||
from lms.djangoapps.instructor_task.tasks_helper.module_state import GRADES_OVERRIDE_EVENT_TYPE
|
||||
from openedx.core.djangoapps.course_groups.signals.signals import COHORT_MEMBERSHIP_UPDATED
|
||||
from openedx.core.lib.grade_utils import is_score_higher_or_equal
|
||||
from student.models import user_by_anonymous_id
|
||||
from student.signals.signals import ENROLLMENT_TRACK_UPDATED
|
||||
from submissions.models import score_reset, score_set
|
||||
from track.event_transaction_utils import (
|
||||
create_new_event_transaction_id,
|
||||
@@ -22,6 +24,7 @@ from track.event_transaction_utils import (
|
||||
)
|
||||
from util.date_utils import to_timestamp
|
||||
|
||||
from ..config.waffle import waffle, WRITE_ONLY_IF_ENGAGED
|
||||
from ..constants import ScoreDatabaseTableEnum
|
||||
from ..new.course_grade_factory import CourseGradeFactory
|
||||
from ..scores import weighted_score
|
||||
@@ -31,7 +34,7 @@ from .signals import (
|
||||
PROBLEM_WEIGHTED_SCORE_CHANGED,
|
||||
SCORE_PUBLISHED,
|
||||
SUBSECTION_SCORE_CHANGED,
|
||||
SUBSECTION_OVERRIDE_CHANGED
|
||||
SUBSECTION_OVERRIDE_CHANGED,
|
||||
)
|
||||
|
||||
log = getLogger(__name__)
|
||||
@@ -237,13 +240,28 @@ def enqueue_subsection_update(sender, **kwargs): # pylint: disable=unused-argum
|
||||
|
||||
|
||||
@receiver(SUBSECTION_SCORE_CHANGED)
|
||||
def recalculate_course_grade(sender, course, course_structure, user, **kwargs): # pylint: disable=unused-argument
|
||||
def recalculate_course_grade_only(sender, course, course_structure, user, **kwargs): # pylint: disable=unused-argument
|
||||
"""
|
||||
Updates a saved course grade.
|
||||
Updates a saved course grade, but does not update the subsection
|
||||
grades the user has in this course.
|
||||
"""
|
||||
CourseGradeFactory().update(user, course=course, course_structure=course_structure)
|
||||
|
||||
|
||||
@receiver(ENROLLMENT_TRACK_UPDATED)
|
||||
@receiver(COHORT_MEMBERSHIP_UPDATED)
|
||||
def force_recalculate_course_and_subsection_grades(sender, user, course_key, **kwargs):
|
||||
"""
|
||||
Updates a saved course grade, forcing the subsection grades
|
||||
from which it is calculated to update along the way.
|
||||
|
||||
Does not create a grade if the user has never attempted a problem,
|
||||
even if the WRITE_ONLY_IF_ENGAGED waffle switch is off.
|
||||
"""
|
||||
if waffle().is_enabled(WRITE_ONLY_IF_ENGAGED) or CourseGradeFactory().read(user, course_key=course_key):
|
||||
CourseGradeFactory().update(user=user, course_key=course_key, force_update_subsections=True)
|
||||
|
||||
|
||||
def _emit_event(kwargs):
|
||||
"""
|
||||
Emits a problem submitted event only if there is no current event
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Tests for the score change signals defined in the courseware models module.
|
||||
"""
|
||||
|
||||
import itertools
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
@@ -10,15 +10,20 @@ import pytz
|
||||
from django.test import TestCase
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from opaque_keys.edx.locations import CourseLocator
|
||||
from openedx.core.djangoapps.course_groups.signals.signals import COHORT_MEMBERSHIP_UPDATED
|
||||
from student.signals.signals import ENROLLMENT_TRACK_UPDATED
|
||||
from student.tests.factories import UserFactory
|
||||
from submissions.models import score_reset, score_set
|
||||
from util.date_utils import to_timestamp
|
||||
|
||||
from ..config.waffle import waffle, WRITE_ONLY_IF_ENGAGED
|
||||
from ..constants import ScoreDatabaseTableEnum
|
||||
from ..signals.handlers import (
|
||||
disconnect_submissions_signal_receiver,
|
||||
problem_raw_score_changed_handler,
|
||||
submissions_score_reset_handler,
|
||||
submissions_score_set_handler
|
||||
submissions_score_set_handler,
|
||||
)
|
||||
from ..signals.signals import PROBLEM_RAW_SCORE_CHANGED
|
||||
|
||||
@@ -251,3 +256,32 @@ class ScoreChangedSignalRelayTest(TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
with disconnect_submissions_signal_receiver(PROBLEM_RAW_SCORE_CHANGED):
|
||||
pass
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class RecalculateUserGradeSignalsTest(TestCase):
|
||||
def setUp(self):
|
||||
super(RecalculateUserGradeSignalsTest, self).setUp()
|
||||
self.user = UserFactory()
|
||||
self.course_key = CourseLocator("test_org", "test_course_num", "test_run")
|
||||
|
||||
@patch('lms.djangoapps.grades.signals.handlers.CourseGradeFactory.update')
|
||||
@patch('lms.djangoapps.grades.signals.handlers.CourseGradeFactory.read')
|
||||
@ddt.data(*itertools.product((COHORT_MEMBERSHIP_UPDATED, ENROLLMENT_TRACK_UPDATED), (True, False), (True, False)))
|
||||
@ddt.unpack
|
||||
def test_recalculate_on_signal(self, signal, write_only_if_engaged, has_grade, read_mock, update_mock):
|
||||
"""
|
||||
Tests the grades handler for signals that trigger regrading.
|
||||
The handler should call CourseGradeFactory.update() with the
|
||||
args below, *except* if the WRITE_ONLY_IF_ENGAGED waffle flag
|
||||
is inactive and the user does not have a grade.
|
||||
"""
|
||||
if not has_grade:
|
||||
read_mock.return_value = None
|
||||
with waffle().override(WRITE_ONLY_IF_ENGAGED, active=write_only_if_engaged):
|
||||
signal.send(sender=None, user=self.user, course_key=self.course_key)
|
||||
|
||||
if not write_only_if_engaged and not has_grade:
|
||||
update_mock.assert_not_called()
|
||||
else:
|
||||
update_mock.assert_called_with(course_key=self.course_key, user=self.user, force_update_subsections=True)
|
||||
|
||||
Reference in New Issue
Block a user