Skip to content

Commit

Permalink
feat: verify/reject actions on attempt update (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
zacharis278 authored Jul 25, 2023
1 parent 427e299 commit 3418bff
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 11 deletions.
37 changes: 36 additions & 1 deletion edx_exams/apps/api/v1/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1121,7 +1121,7 @@ def test_put_attempt_does_not_exist(self):
)
@ddt.unpack
@patch('edx_exams.apps.api.v1.views.update_attempt_status')
def test_put_update_exam_attempt(self, action, expected_status, mock_update_attempt_status):
def test_put_learner_update_exam_attempt(self, action, expected_status, mock_update_attempt_status):
"""
Test that an exam can be updated
"""
Expand All @@ -1137,6 +1137,41 @@ def test_put_update_exam_attempt(self, action, expected_status, mock_update_atte
self.assertEqual(response.status_code, 200)
mock_update_attempt_status.assert_called_once_with(attempt.id, expected_status)

@ddt.data(
('verify', ExamAttemptStatus.verified),
('reject', ExamAttemptStatus.rejected),
)
@ddt.unpack
@patch('edx_exams.apps.api.v1.views.update_attempt_status')
def test_put_staff_update_exam_attempt(self, action, expected_status, mock_update_attempt_status):
"""
Test staff/instructor updates
"""
# create exam attempt for user
attempt = ExamAttemptFactory(
user=self.non_staff_user,
exam=self.exam,
)

mock_update_attempt_status.return_value = attempt.id

response = self.put_api(self.staff_user, attempt.id, {'action': action})
self.assertEqual(response.status_code, 200)
mock_update_attempt_status.assert_called_once_with(attempt.id, expected_status)

def test_put_learner_verify(self):
"""
Test that a learner account cannot verify an attempt
"""
# create exam attempt for user
attempt = ExamAttemptFactory(
user=self.non_staff_user,
exam=self.exam,
)

response = self.put_api(self.non_staff_user, attempt.id, {'action': 'verify'})
self.assertEqual(response.status_code, 400)

@patch('edx_exams.apps.api.v1.views.update_attempt_status')
def test_put_exception_raised(self, mock_update_attempt_status):
"""
Expand Down
26 changes: 16 additions & 10 deletions edx_exams/apps/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,23 +535,29 @@ def put(self, request, attempt_id):
data={'detail': f'Attempt with attempt_id={attempt_id} does not exit.'}
)

# user should only be able to update their own attempt
if attempt.user.id != request.user.id:
action_mapping = {}
if request.user.is_staff:
action_mapping = {
'verify': ExamAttemptStatus.verified,
'reject': ExamAttemptStatus.rejected,
}
# instructors/staff cannot take exams so they do not need these actions
elif attempt.user.id == request.user.id:
action_mapping = {
'stop': ExamAttemptStatus.ready_to_submit,
'start': ExamAttemptStatus.started,
'submit': ExamAttemptStatus.submitted,
'click_download_software': ExamAttemptStatus.download_software_clicked,
'error': ExamAttemptStatus.error,
}
else:
error_msg = (
f'user_id={attempt.user.id} attempted to update attempt_id={attempt.id} in '
f'course_id={attempt.exam.course_id} but does not have access to it. (action={action})'
)
error = {'detail': error_msg}
return Response(status=status.HTTP_403_FORBIDDEN, data=error)

action_mapping = {
'stop': ExamAttemptStatus.ready_to_submit,
'start': ExamAttemptStatus.started,
'submit': ExamAttemptStatus.submitted,
'click_download_software': ExamAttemptStatus.download_software_clicked,
'error': ExamAttemptStatus.error,
}

to_status = action_mapping.get(action)
if to_status:
attempt_id = update_attempt_status(attempt_id, to_status)
Expand Down

0 comments on commit 3418bff

Please sign in to comment.