EDUCATOR-5240: LMS Returns 500 when requesting grade override history for a Subsection a Course Staff does not have access to (#24849)

* recreate issue

* add success and error_message

* catch case where target block has been hidden
This commit is contained in:
Jansen Kantor
2020-09-08 15:59:33 -04:00
committed by GitHub
parent dd8e730b5c
commit d0bd99a028
3 changed files with 67 additions and 3 deletions

View File

@@ -102,6 +102,8 @@ class SubsectionGradeResponseSerializer(serializers.Serializer):
"""
Serializer for subsection grade response.
"""
success = serializers.BooleanField()
error_message = serializers.CharField(required=False)
subsection_id = serializers.CharField()
user_id = serializers.IntegerField()
course_id = serializers.CharField()

View File

@@ -13,6 +13,7 @@ from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.db.models import Case, Exists, F, OuterRef, When, Q
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, UsageKey
from rest_framework import status
@@ -920,6 +921,10 @@ class GradebookBulkUpdateView(GradeViewMixin, PaginatedAPIView):
)
class SubsectionUnavailableToUserException(Exception):
pass
@view_auth_classes()
class SubsectionGradeView(GradeViewMixin, APIView):
"""
@@ -1043,6 +1048,8 @@ class SubsectionGradeView(GradeViewMixin, APIView):
developer_message='Invalid UserID',
error_code='invalid_user_id'
)
success = True
err_msg = ""
override = None
history = []
history_record_limit = request.GET.get('history_record_limit')
@@ -1067,16 +1074,30 @@ class SubsectionGradeView(GradeViewMixin, APIView):
'possible_graded': original_grade.possible_graded,
}
except PersistentSubsectionGrade.DoesNotExist:
grade_data = self._get_grade_data_for_not_attempted_assignment(user_id, usage_key)
try:
grade_data = self._get_grade_data_for_not_attempted_assignment(user_id, usage_key)
except SubsectionUnavailableToUserException as exc:
success = False
err_msg = str(exc)
grade_data = {
'earned_all': 0,
'possible_all': 0,
'earned_graded': 0,
'possible_graded': 0,
}
results = SubsectionGradeResponseSerializer({
response_data = {
'success': success,
'original_grade': grade_data,
'override': override,
'history': history,
'subsection_id': usage_key,
'user_id': user_id,
'course_id': usage_key.course_key,
})
}
if not success:
response_data['error_message'] = err_msg
results = SubsectionGradeResponseSerializer(response_data)
return Response(results.data)
def _get_grade_data_for_not_attempted_assignment(self, user_id, usage_key):
@@ -1085,6 +1106,10 @@ class SubsectionGradeView(GradeViewMixin, APIView):
"""
student = get_user_model().objects.get(id=user_id)
course_structure = get_course_blocks(student, usage_key)
if usage_key not in course_structure:
raise SubsectionUnavailableToUserException(
_("Cannot override subsection grade: subsection is not available for target learner.")
)
subsection_grade_factory = SubsectionGradeFactory(student, course_structure=course_structure)
grade = subsection_grade_factory.create(course_structure[usage_key], read_only=True, force_calculate=True)
grade_data = {

View File

@@ -1706,6 +1706,7 @@ class SubsectionGradeViewTest(GradebookViewTestBase):
)
expected_data = {
'success': True,
'original_grade': OrderedDict([
('earned_all', 1.0),
('possible_all', 2.0),
@@ -1733,6 +1734,7 @@ class SubsectionGradeViewTest(GradebookViewTestBase):
)
expected_data = {
'success': True,
'original_grade': OrderedDict([
('earned_all', 6.0),
('possible_all', 12.0),
@@ -1770,6 +1772,7 @@ class SubsectionGradeViewTest(GradebookViewTestBase):
)
expected_data = {
'success': True,
'original_grade': OrderedDict([
('earned_all', 6.0),
('possible_all', 12.0),
@@ -1827,6 +1830,7 @@ class SubsectionGradeViewTest(GradebookViewTestBase):
)
expected_data = {
'success': True,
'original_grade': OrderedDict([
('earned_all', 6.0),
('possible_all', 12.0),
@@ -1928,6 +1932,7 @@ class SubsectionGradeViewTest(GradebookViewTestBase):
self.get_url(subsection_id=self.usage_key, user_id=other_user.id)
)
expected_data = {
'success': True,
'original_grade': OrderedDict([
('earned_all', 0.0),
('possible_all', 0.0),
@@ -1952,3 +1957,35 @@ class SubsectionGradeViewTest(GradebookViewTestBase):
)
self.assertEqual(status.HTTP_403_FORBIDDEN, resp.status_code)
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
def test_get_override_for_unreleased_block(self):
self.login_course_staff()
unreleased_subsection = ItemFactory.create(
parent_location=self.chapter_1.location,
category='sequential',
graded=True,
start=datetime(2999, 1, 1, tzinfo=UTC), # arbitrary future date
display_name='Unreleased Section',
)
resp = self.client.get(
self.get_url(subsection_id=unreleased_subsection.location)
)
expected_data = {
'success': False,
'error_message': "Cannot override subsection grade: subsection is not available for target learner.",
'original_grade': OrderedDict([
('earned_all', 0.0),
('possible_all', 0.0),
('earned_graded', 0.0),
('possible_graded', 0.0)
]),
'user_id': self.user_id,
'override': None,
'course_id': text_type(self.usage_key.course_key),
'subsection_id': text_type(unreleased_subsection.location),
'history': []
}
self.assertEqual(expected_data, resp.data)