fix: certificate generation without Persistent Grades

Fix for an error related to endless recursion

Dev Notes:
The student gets the passing grade
-> CourseGradeFactory sends the `COURSE_GRADE_NOW_PASSED` signal after the grade calculation
-> `listen_for_passing_grade` receives the signal and calls `generate_certificate_task`
-> `generate_certificate_task` calls `_generate_regular_certificate_task`
-> `_generate_regular_certificate_task` trying to get grade by calling the `_get_course_grade`
-> `_get_course_grade` calls `CourseGradeFactory().read()` but I have persistent grades off, so actually that ends with `CourseGradeFactory().update()`
-> `CourseGradeFactory().update()` sends the `COURSE_GRADE_NOW_PASSED`
And so on
This commit is contained in:
dyudyunov
2022-01-19 15:55:29 +02:00
committed by Eugene Dyudyunov
parent 56cef9cb6a
commit c4bc6e29ea
2 changed files with 33 additions and 28 deletions

View File

@@ -72,7 +72,7 @@ def _generate_regular_certificate_task(user, course_key, generation_mode=None, d
eligible and a certificate can be generated.
"""
enrollment_mode = _get_enrollment_mode(user, course_key)
course_grade = _get_course_grade(user, course_key)
course_grade = _get_course_grade(user, course_key, send_grade_signals=False)
if _can_generate_regular_certificate(user, course_key, enrollment_mode, course_grade):
return _generate_certificate_task(user=user, course_key=course_key, enrollment_mode=enrollment_mode,
course_grade=course_grade, generation_mode=generation_mode,
@@ -377,11 +377,11 @@ def _get_grade_value(course_grade):
return ''
def _get_course_grade(user, course_key):
def _get_course_grade(user, course_key, send_grade_signals=False):
"""
Get the user's course grade in this course run. Note that this may be None.
"""
return CourseGradeFactory().read(user, course_key=course_key)
return CourseGradeFactory().read(user, course_key=course_key, send_grade_signals=send_grade_signals)
def _get_enrollment_mode(user, course_key):

View File

@@ -33,6 +33,7 @@ class CourseGradeFactory:
course_structure=None,
course_key=None,
create_if_needed=True,
send_grade_signals=True,
):
"""
Returns the CourseGrade for the given user in the course.
@@ -51,7 +52,7 @@ class CourseGradeFactory:
if assume_zero_if_absent(course_data.course_key):
return self._create_zero(user, course_data)
elif create_if_needed:
return self._update(user, course_data)
return self._update(user, course_data, send_grade_signals=send_grade_signals)
else:
return None
@@ -160,13 +161,16 @@ class CourseGradeFactory:
)
@staticmethod
def _update(user, course_data, force_update_subsections=False):
def _update(user, course_data, force_update_subsections=False, send_grade_signals=True):
"""
Computes, saves, and returns a CourseGrade object for the
given user and course.
Sends a COURSE_GRADE_CHANGED signal to listeners and
COURSE_GRADE_NOW_PASSED if learner has passed course or
COURSE_GRADE_NOW_FAILED if learner is now failing course
Computes, saves, and returns a CourseGrade object for the given user and course.
send_grade_signals defines if signals should be sent. Use it to avoid recursion issues in
cases when the signal listener trying to get grades but Persistent Grades are disabled.
If True - sends:
COURSE_GRADE_CHANGED signal to listeners and
COURSE_GRADE_NOW_PASSED if learner has passed course or
COURSE_GRADE_NOW_FAILED if learner is now failing course
"""
should_persist = should_persist_grades(course_data.course_key)
if should_persist and force_update_subsections:
@@ -193,26 +197,27 @@ class CourseGradeFactory:
passed=course_grade.passed,
)
COURSE_GRADE_CHANGED.send_robust(
sender=None,
user=user,
course_grade=course_grade,
course_key=course_data.course_key,
deadline=course_data.course.end,
)
if course_grade.passed:
COURSE_GRADE_NOW_PASSED.send(
sender=CourseGradeFactory,
if send_grade_signals:
COURSE_GRADE_CHANGED.send_robust(
sender=None,
user=user,
course_id=course_data.course_key,
)
else:
COURSE_GRADE_NOW_FAILED.send(
sender=CourseGradeFactory,
user=user,
course_id=course_data.course_key,
grade=course_grade,
course_grade=course_grade,
course_key=course_data.course_key,
deadline=course_data.course.end,
)
if course_grade.passed:
COURSE_GRADE_NOW_PASSED.send(
sender=CourseGradeFactory,
user=user,
course_id=course_data.course_key,
)
else:
COURSE_GRADE_NOW_FAILED.send(
sender=CourseGradeFactory,
user=user,
course_id=course_data.course_key,
grade=course_grade,
)
log.info(
'Grades: Update, %s, User: %s, %s, persisted: %s',