diff --git a/lms/djangoapps/grades/signals/handlers.py b/lms/djangoapps/grades/signals/handlers.py index 781cc66f60..e5fd6ec90d 100644 --- a/lms/djangoapps/grades/signals/handlers.py +++ b/lms/djangoapps/grades/signals/handlers.py @@ -26,7 +26,11 @@ from ..constants import ScoreDatabaseTableEnum from ..course_grade_factory import CourseGradeFactory from .. import events from ..scores import weighted_score -from ..tasks import RECALCULATE_GRADE_DELAY_SECONDS, recalculate_subsection_grade_v3 +from ..tasks import ( + RECALCULATE_GRADE_DELAY_SECONDS, + recalculate_subsection_grade_v3, + recalculate_course_and_subsection_grades_for_user +) log = getLogger(__name__) @@ -241,10 +245,9 @@ def 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. """ - previous_course_grade = CourseGradeFactory().read(user, course_key=course_key) - if previous_course_grade and previous_course_grade.attempted: - CourseGradeFactory().update( + recalculate_course_and_subsection_grades_for_user.apply_async( + kwargs=dict( user=user, - course_key=course_key, - force_update_subsections=True + course_key=course_key ) + ) diff --git a/lms/djangoapps/grades/tasks.py b/lms/djangoapps/grades/tasks.py index 80ca5586c3..b2dcaed31d 100644 --- a/lms/djangoapps/grades/tasks.py +++ b/lms/djangoapps/grades/tasks.py @@ -94,7 +94,7 @@ def compute_grades_for_course_v2(self, **kwargs): try: return compute_grades_for_course(kwargs['course_key'], kwargs['offset'], kwargs['batch_size']) - except Exception as exc: # pylint: disable=broad-except + except Exception as exc: raise self.retry(kwargs=kwargs, exc=exc) @@ -115,6 +115,34 @@ def compute_grades_for_course(course_key, offset, batch_size, **kwargs): # pyli raise result.error +@task( + bind=True, + time_limit=SUBSECTION_GRADE_TIMEOUT_SECONDS, + max_retries=2, + default_retry_delay=RETRY_DELAY_SECONDS, + routing_key=settings.RECALCULATE_GRADES_ROUTING_KEY +) +def recalculate_course_and_subsection_grades_for_user(self, **kwargs): # pylint: disable=unused-argument + """ + Recalculates the course grade and all subsection grades + for the given ``user`` and ``course_key`` keyword arguments. + """ + user = kwargs.get('user') + course_key = kwargs.get('course_key') + + if not user or course_key: + message = 'recalculate_course_and_subsection_grades_for_user missing "user" or "course_key" kwargs from {}' + log.error(message.format(kwargs)) + + previous_course_grade = CourseGradeFactory().read(user, course_key=course_key) + if previous_course_grade and previous_course_grade.attempted: + CourseGradeFactory().update( + user=user, + course_key=course_key, + force_update_subsections=True + ) + + @task( bind=True, base=LoggedPersistOnFailureTask, @@ -185,7 +213,7 @@ def _recalculate_subsection_grade(self, **kwargs): kwargs['user_id'], kwargs['score_deleted'], ) - except Exception as exc: # pylint: disable=broad-except + except Exception as exc: if not isinstance(exc, KNOWN_RETRY_ERRORS): log.info("tnl-6244 grades unexpected failure: {}. task id: {}. kwargs={}".format( repr(exc),