Commit e2913575 by Muhammad Shoaib

added the new button for restarting the practice proctored exam.

 This is only visible in the error case scenario
parent c764e433
...@@ -213,16 +213,22 @@ def create_exam_attempt(exam_id, user_id, taking_as_proctored=False): ...@@ -213,16 +213,22 @@ def create_exam_attempt(exam_id, user_id, taking_as_proctored=False):
one exam_attempt per user per exam. Multiple attempts by user will be archived one exam_attempt per user per exam. Multiple attempts by user will be archived
in a separate table in a separate table
""" """
if ProctoredExamStudentAttempt.objects.get_exam_attempt(exam_id, user_id):
err_msg = (
'Cannot create new exam attempt for exam_id = {exam_id} and '
'user_id = {user_id} because it already exists!'
).format(exam_id=exam_id, user_id=user_id)
raise StudentExamAttemptAlreadyExistsException(err_msg)
# for now the student is allowed the exam default # for now the student is allowed the exam default
exam = get_exam_by_id(exam_id) exam = get_exam_by_id(exam_id)
existing_attempt = ProctoredExamStudentAttempt.objects.get_exam_attempt(exam_id, user_id)
if existing_attempt:
if existing_attempt.is_sample_attempt:
# Archive the existing attempt by deleting it.
existing_attempt.delete_exam_attempt()
else:
err_msg = (
'Cannot create new exam attempt for exam_id = {exam_id} and '
'user_id = {user_id} because it already exists!'
).format(exam_id=exam_id, user_id=user_id)
raise StudentExamAttemptAlreadyExistsException(err_msg)
allowed_time_limit_mins = exam['time_limit_mins'] allowed_time_limit_mins = exam['time_limit_mins']
# add in the allowed additional time # add in the allowed additional time
...@@ -506,9 +512,13 @@ def get_student_view(user_id, course_id, content_id, ...@@ -506,9 +512,13 @@ def get_student_view(user_id, course_id, content_id,
'credit_state' in context and 'credit_state' in context and
context['credit_state'] context['credit_state']
) )
if check_mode: if check_mode:
# Return None (show exam content) for non verified users unless it's a practice exam
# where both honor and verified users are shown the proctoring screen.
if context['credit_state']['enrollment_mode'] != 'verified': if context['credit_state']['enrollment_mode'] != 'verified':
return None if not (context['is_practice_exam'] and context['credit_state']['enrollment_mode'] == 'honor'):
return None
student_view_template = None student_view_template = None
...@@ -602,6 +612,7 @@ def get_student_view(user_id, course_id, content_id, ...@@ -602,6 +612,7 @@ def get_student_view(user_id, course_id, content_id,
'total_time': total_time, 'total_time': total_time,
'exam_id': exam_id, 'exam_id': exam_id,
'progress_page_url': progress_page_url, 'progress_page_url': progress_page_url,
'is_sample_attempt': attempt['is_sample_attempt'] if attempt else False,
'enter_exam_endpoint': reverse('edx_proctoring.proctored_exam.attempt.collection'), 'enter_exam_endpoint': reverse('edx_proctoring.proctored_exam.attempt.collection'),
'exam_started_poll_url': reverse( 'exam_started_poll_url': reverse(
'edx_proctoring.proctored_exam.attempt', 'edx_proctoring.proctored_exam.attempt',
......
{% load i18n %}
<p>{% blocktrans %} You can also retake the exam. {% endblocktrans %}</p>
<button type="button" name="submit-proctored-exam" class="start-timed-exam" data-ajax-url="{{enter_exam_endpoint}}" data-exam-id="{{exam_id}}" data-attempt-proctored=true data-start-immediately=false >
{% blocktrans %}
Re-run the Exam
{% endblocktrans %}
</button>
<script type="text/javascript">
$('.start-timed-exam').click(
function(event) {
var action_url = $(this).data('ajax-url');
var exam_id = $(this).data('exam-id');
var attempt_proctored = $(this).data('attempt-proctored');
var start_immediately = $(this).data('start-immediately');
if (typeof action_url === "undefined" ) {
return false;
}
$.post(
action_url,
{
"exam_id": exam_id,
"attempt_proctored": attempt_proctored,
"start_clock": start_immediately
},
function(data) {
// reload the page, because we've unlocked it
location.reload();
}
);
}
);
</script>
\ No newline at end of file
...@@ -4,3 +4,6 @@ ...@@ -4,3 +4,6 @@
{% trans "Your exam has been marked as failed due to an error." %} {% trans "Your exam has been marked as failed due to an error." %}
</div> </div>
</div> </div>
{% if is_sample_attempt %}
{% include 'proctoring/rerun_practice_attempt.html' %}
{% endif %}
\ No newline at end of file
...@@ -169,6 +169,7 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -169,6 +169,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
user_id=self.user_id, user_id=self.user_id,
external_id=self.external_id, external_id=self.external_id,
started_at=started_at if started_at else datetime.now(pytz.UTC), started_at=started_at if started_at else datetime.now(pytz.UTC),
is_sample_attempt=True,
status=ProctoredExamStudentAttemptStatus.started, status=ProctoredExamStudentAttemptStatus.started,
allowed_time_limit_mins=10 allowed_time_limit_mins=10
) )
...@@ -368,6 +369,14 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -368,6 +369,14 @@ class ProctoredExamApiTests(LoggedInTestCase):
with self.assertRaises(StudentExamAttemptAlreadyExistsException): with self.assertRaises(StudentExamAttemptAlreadyExistsException):
create_exam_attempt(proctored_exam_student_attempt.proctored_exam.id, self.user_id) create_exam_attempt(proctored_exam_student_attempt.proctored_exam.id, self.user_id)
def test_recreate_a_practice_exam_attempt(self): # pylint: disable=invalid-name
"""
Taking the practice exam several times should not cause an exception.
"""
practice_exam_student_attempt = self._create_started_practice_exam_attempt()
new_attempt_id = create_exam_attempt(practice_exam_student_attempt.proctored_exam.id, self.user_id)
self.assertGreater(practice_exam_student_attempt, new_attempt_id, "New attempt not created.")
def test_get_exam_attempt(self): def test_get_exam_attempt(self):
""" """
Test to get the existing exam attempt. Test to get the existing exam attempt.
...@@ -560,9 +569,30 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -560,9 +569,30 @@ class ProctoredExamApiTests(LoggedInTestCase):
self.assertIn('data-exam-id="%d"' % self.proctored_exam_id, rendered_response) self.assertIn('data-exam-id="%d"' % self.proctored_exam_id, rendered_response)
self.assertIn(self.start_an_exam_msg % self.exam_name, rendered_response) self.assertIn(self.start_an_exam_msg % self.exam_name, rendered_response)
def test_get_honot_view(self): def test_get_honor_view_with_practice_exam(self): # pylint: disable=invalid-name
""" """
Test for get_student_view promting when the student is enrolled in non-verified Test for get_student_view prompting when the student is enrolled in non-verified
track for a practice exam, this should return not None
"""
rendered_response = get_student_view(
user_id=self.user_id,
course_id=self.course_id,
content_id=self.content_id,
context={
'is_proctored': True,
'display_name': self.exam_name,
'default_time_limit_mins': 90,
'credit_state': {
'enrollment_mode': 'honor'
},
'is_practice_exam': True
}
)
self.assertIsNotNone(rendered_response)
def test_get_honor_view(self):
"""
Test for get_student_view prompting when the student is enrolled in non-verified
track, this should return None track, this should return None
""" """
rendered_response = get_student_view( rendered_response = get_student_view(
...@@ -575,7 +605,8 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -575,7 +605,8 @@ class ProctoredExamApiTests(LoggedInTestCase):
'default_time_limit_mins': 90, 'default_time_limit_mins': 90,
'credit_state': { 'credit_state': {
'enrollment_mode': 'honor' 'enrollment_mode': 'honor'
} },
'is_practice_exam': False
} }
) )
self.assertIsNone(rendered_response) self.assertIsNone(rendered_response)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment