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:
@@ -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()
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user