diff --git a/lms/djangoapps/grades/api/v1/tests/test_views.py b/lms/djangoapps/grades/api/v1/tests/test_views.py index 1f26c20643..38063958fc 100644 --- a/lms/djangoapps/grades/api/v1/tests/test_views.py +++ b/lms/djangoapps/grades/api/v1/tests/test_views.py @@ -889,6 +889,28 @@ class GradebookBulkUpdateViewTest(GradebookViewTestBase): ) self.assertEqual(status.HTTP_404_NOT_FOUND, resp.status_code) + def test_grades_frozen(self): + """ + Should receive a 403 when grades have been frozen for a course. + """ + with patch('lms.djangoapps.grades.api.v1.views.are_grades_frozen', return_value=True): + with override_waffle_flag(self.waffle_flag, active=True): + self.login_staff() + post_data = [ + { + 'user_id': self.student.id, + 'usage_id': text_type(self.subsections[self.chapter_1.location][0].location), + 'grade': {}, # doesn't matter what we put here. + } + ] + + resp = self.client.post( + self.get_url(), + data=json.dumps(post_data), + content_type='application/json', + ) + self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code) + def test_user_not_enrolled(self): with override_waffle_flag(self.waffle_flag, active=True): self.login_staff() diff --git a/lms/djangoapps/grades/api/v1/views.py b/lms/djangoapps/grades/api/v1/views.py index cc505d7778..a3a7747779 100644 --- a/lms/djangoapps/grades/api/v1/views.py +++ b/lms/djangoapps/grades/api/v1/views.py @@ -25,9 +25,8 @@ from lms.djangoapps.grades.course_data import CourseData from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory from lms.djangoapps.grades.events import SUBSECTION_GRADE_CALCULATED, subsection_grade_calculated from lms.djangoapps.grades.models import PersistentSubsectionGrade, PersistentSubsectionGradeOverride -from lms.djangoapps.grades.signals import signals from lms.djangoapps.grades.subsection_grade import CreateSubsectionGrade -from lms.djangoapps.grades.tasks import recalculate_subsection_grade_v3 +from lms.djangoapps.grades.tasks import recalculate_subsection_grade_v3, are_grades_frozen from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey from openedx.core.djangoapps.content.course_overviews.models import CourseOverview @@ -704,6 +703,13 @@ class GradebookBulkUpdateView(GradeViewMixin, GenericAPIView): course and subsection grades for the specified user. """ course_key = get_course_key(request, course_id) + if are_grades_frozen(course_key): + raise self.api_error( + status_code=status.HTTP_403_FORBIDDEN, + developer_message='Grades are frozen for this course.', + error_code='grades_frozen' + ) + course = get_course_with_access(request.user, 'staff', course_key, depth=None) result = [] diff --git a/lms/djangoapps/grades/tasks.py b/lms/djangoapps/grades/tasks.py index 0b5e149c56..e7d7c1d471 100644 --- a/lms/djangoapps/grades/tasks.py +++ b/lms/djangoapps/grades/tasks.py @@ -60,7 +60,7 @@ def compute_all_grades_for_course(**kwargs): log.debug('Grades: ignoring policy change regrade due to waffle switch') else: course_key = CourseKey.from_string(kwargs.pop('course_key')) - if _are_grades_frozen(course_key): + if are_grades_frozen(course_key): log.info("Attempted compute_all_grades_for_course for course '%s', but grades are frozen.", course_key) return for course_key_string, offset, batch_size in _course_task_args(course_key=course_key, **kwargs): @@ -115,7 +115,7 @@ def compute_grades_for_course(course_key, offset, batch_size, **kwargs): # pyli offset. """ course_key = CourseKey.from_string(course_key) - if _are_grades_frozen(course_key): + if are_grades_frozen(course_key): log.info("Attempted compute_grades_for_course for course '%s', but grades are frozen.", course_key) return @@ -148,7 +148,7 @@ def recalculate_course_and_subsection_grades_for_user(self, **kwargs): # pylint user = User.objects.get(id=user_id) course_key = CourseKey.from_string(course_key_str) - if _are_grades_frozen(course_key): + if are_grades_frozen(course_key): log.info( "Attempted recalculate_course_and_subsection_grades_for_user for course '%s', but grades are frozen.", course_key, @@ -205,7 +205,7 @@ def _recalculate_subsection_grade(self, **kwargs): """ try: course_key = CourseLocator.from_string(kwargs['course_id']) - if _are_grades_frozen(course_key): + if are_grades_frozen(course_key): log.info("Attempted _recalculate_subsection_grade for course '%s', but grades are frozen.", course_key) return @@ -354,7 +354,7 @@ def _course_task_args(course_key, **kwargs): yield (six.text_type(course_key), offset, batch_size) -def _are_grades_frozen(course_key): +def are_grades_frozen(course_key): """ Returns whether grades are frozen for the given course. """ if waffle_flags()[ENFORCE_FREEZE_GRADE_AFTER_COURSE_END].is_enabled(course_key): course = CourseOverview.get_from_id(course_key)