Commit 5ad4465d by chrisndodge

Merge pull request #61 from edx/cdodge/review-punchlist1

address some review feedback
parents c57c8095 e6cb0693
...@@ -418,6 +418,15 @@ def update_attempt_status(exam_id, user_id, to_status): ...@@ -418,6 +418,15 @@ def update_attempt_status(exam_id, user_id, to_status):
Internal helper to handle state transitions of attempt status Internal helper to handle state transitions of attempt status
""" """
# In some configuration we may treat timeouts the same
# as the user saying he/she wises to submit the exam
alias_timeout = (
to_status == ProctoredExamStudentAttemptStatus.timed_out and
not settings.PROCTORING_SETTINGS.get('ALLOW_TIMED_OUT_STATE', False)
)
if alias_timeout:
to_status = ProctoredExamStudentAttemptStatus.ready_to_submit
exam_attempt_obj = ProctoredExamStudentAttempt.objects.get_exam_attempt(exam_id, user_id) exam_attempt_obj = ProctoredExamStudentAttempt.objects.get_exam_attempt(exam_id, user_id)
if exam_attempt_obj is None: if exam_attempt_obj is None:
raise StudentExamAttemptDoesNotExistsException('Error. Trying to look up an exam that does not exist.') raise StudentExamAttemptDoesNotExistsException('Error. Trying to look up an exam that does not exist.')
......
...@@ -39,6 +39,7 @@ def start_exam_callback(request, attempt_code): # pylint: disable=unused-argume ...@@ -39,6 +39,7 @@ def start_exam_callback(request, attempt_code): # pylint: disable=unused-argume
IMPORTANT: This is an unauthenticated endpoint, so be VERY CAREFUL about extending IMPORTANT: This is an unauthenticated endpoint, so be VERY CAREFUL about extending
this endpoint this endpoint
""" """
attempt = get_exam_attempt_by_code(attempt_code) attempt = get_exam_attempt_by_code(attempt_code)
if not attempt: if not attempt:
return HttpResponse( return HttpResponse(
......
...@@ -102,7 +102,6 @@ var edx = edx || {}; ...@@ -102,7 +102,6 @@ var edx = edx || {};
return this; return this;
}, },
unloadMessage: function () { unloadMessage: function () {
return null;
return gettext("As you are currently taking a proctored exam,\n" + return gettext("As you are currently taking a proctored exam,\n" +
"you should not be navigation away from the exam.\n" + "you should not be navigation away from the exam.\n" +
"This may be considered as a violation of the \n" + "This may be considered as a violation of the \n" +
...@@ -115,10 +114,8 @@ var edx = edx || {}; ...@@ -115,10 +114,8 @@ var edx = edx || {};
var url = self.model.url + '/' + self.model.get('attempt_id'); var url = self.model.url + '/' + self.model.get('attempt_id');
$.ajax(url).success(function(data) { $.ajax(url).success(function(data) {
if (data.status === 'error') { if (data.status === 'error') {
// Let the student know that his exam has failed due to an error. // The proctoring session is in error state
// This alert may or may not bring the browser window back to the // refresh the page to
// foreground (depending on browser as well as user settings)
alert(gettext('Your exam has failed'));
clearInterval(self.timerId); // stop the timer once the time finishes. clearInterval(self.timerId); // stop the timer once the time finishes.
$(window).unbind('beforeunload', self.unloadMessage); $(window).unbind('beforeunload', self.unloadMessage);
// refresh the page when the timer expired // refresh the page when the timer expired
......
...@@ -53,6 +53,17 @@ ...@@ -53,6 +53,17 @@
var exam_id = $(this).data('exam-id'); var exam_id = $(this).data('exam-id');
var attempt_proctored = $(this).data('attempt-proctored'); var attempt_proctored = $(this).data('attempt-proctored');
var start_immediately = $(this).data('start-immediately'); var start_immediately = $(this).data('start-immediately');
if (!attempt_proctored) {
var msg = gettext(
"Are you sure you want to take this exam without proctoring? " +
"This will make you no longer eligible to earn credit for this course."
)
if (!confirm(msg)) {
return;
}
}
if (typeof action_url === "undefined" ) { if (typeof action_url === "undefined" ) {
return false; return false;
} }
......
{% load i18n %} {% load i18n %}
<div class="sequence proctored-exam error"> <div class="failure sequence proctored-exam" data-exam-id="{{exam_id}}">
<div class="gated-sequence"> <h3>
{% trans "Your exam has been marked as failed due to an error." %} {% blocktrans %}
</div> Your proctoring session is in error
{% endblocktrans %}
</h3>
<h4>
{% blocktrans %}
Your Proctoring Session review: <b class="failure"> Failed </b>
{% endblocktrans %}
</h4>
<p>
{% blocktrans %}
It appears that your proctoring session has been shut down while you were taking this
exam. This invalidates your proctored exam and you will not be eligible for course
credit.
{% endblocktrans %}
</p>
<hr>
<p>
{% blocktrans %}
Please see <a href="{{progress_page_url}}">your progress in this course </a>
for your general course credit worthiness.
{% endblocktrans %}
</p>
</div>
<div class="footer-sequence border-b-0 padding-b-0">
<span> {% trans "Can I contest this review?" %} </span>
<p class="proctored-exam-option">
{% blocktrans %}
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor.
Aenean massa.
{% endblocktrans %}
</p>
<a href="#" class="contest-review">Contest this review</a>
<hr class="clearfix">
<span> {% trans "Is there anything I can do to make up/replace this session?" %} </span>
<p class="proctored-exam-option">
{% blocktrans %}
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor.
Aenean massa.
{% endblocktrans %}
</p>
<hr class="clearfix">
<span> {% trans "See Also" %} </span>
<p>
{% blocktrans %}
<a class="footer-link" href="#">
Frequently asked questions about proctoring and earning college credit.
</a>
{% endblocktrans %}
</p>
</div> </div>
...@@ -51,11 +51,22 @@ ...@@ -51,11 +51,22 @@
var _waiting_for_proctored_interval = null; var _waiting_for_proctored_interval = null;
$('.proctored-decline-exam').click( $('.proctored-decline-exam').click(
function(event) { function(e) {
e.preventDefault();
e.stopPropagation();
var action_url = $(this).data('change-state-url'); var action_url = $(this).data('change-state-url');
var exam_id = $(this).data('exam-id'); var exam_id = $(this).data('exam-id');
var action = $(this).data('action') var action = $(this).data('action')
var msg = gettext(
"Are you sure you want to take this exam without proctoring? " +
"This will make you no longer eligible to earn credit for this course."
)
if (!confirm(msg)) {
return;
}
// Update the state of the attempt // Update the state of the attempt
$.ajax({ $.ajax({
url: action_url, url: action_url,
......
...@@ -26,13 +26,4 @@ ...@@ -26,13 +26,4 @@
{% endblocktrans %} {% endblocktrans %}
</p> </p>
</div> </div>
<div class="footer-sequence border-b-0 padding-b-0"> {% include 'proctoring/seq_proctored_exam_footer.html' %}
<span> {% trans "See Also" %} </span>
<p>
{% blocktrans %}
<a class="footer-link" href="#">
Frequently asked questions about proctoring and earning college credit.
</a>
{% endblocktrans %}
</p>
</div>
...@@ -26,13 +26,4 @@ ...@@ -26,13 +26,4 @@
{% endblocktrans %} {% endblocktrans %}
</p> </p>
</div> </div>
<div class="footer-sequence border-b-0 padding-b-0"> {% include 'proctoring/seq_proctored_exam_footer.html' %}
<span> {% trans "See Also" %} </span>
<p>
{% blocktrans %}
<a class="footer-link" href="#">
Frequently asked questions about proctoring and earning college credit.
</a>
{% endblocktrans %}
</p>
</div>
...@@ -92,7 +92,7 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -92,7 +92,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
self.start_an_exam_msg = 'Would you like to take %s as a proctored exam?' self.start_an_exam_msg = 'Would you like to take %s as a proctored exam?'
self.timed_exam_msg = '%s is a Timed Exam' self.timed_exam_msg = '%s is a Timed Exam'
self.exam_time_expired_msg = 'You did not complete the exam in the allotted time' self.exam_time_expired_msg = 'You did not complete the exam in the allotted time'
self.exam_time_error_msg = 'Your exam has been marked as failed due to an error.' self.exam_time_error_msg = 'Your proctoring session is in error'
self.chose_proctored_exam_msg = 'You have chosen to take %s as a proctored exam' self.chose_proctored_exam_msg = 'You have chosen to take %s as a proctored exam'
self.proctored_exam_completed_msg = 'This is the end of your proctored exam' self.proctored_exam_completed_msg = 'This is the end of your proctored exam'
self.proctored_exam_submitted_msg = 'You have submitted this proctored exam for review' self.proctored_exam_submitted_msg = 'You have submitted this proctored exam for review'
...@@ -839,6 +839,7 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -839,6 +839,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
) )
self.assertIsNone(rendered_response) self.assertIsNone(rendered_response)
@patch.dict('django.conf.settings.PROCTORING_SETTINGS', {'ALLOW_TIMED_OUT_STATE': True})
def test_get_studentview_timedout(self): def test_get_studentview_timedout(self):
""" """
Verifies that if we call get_studentview when the timer has expired Verifies that if we call get_studentview when the timer has expired
...@@ -959,6 +960,7 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -959,6 +960,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
) )
self.assertIn(self.proctored_exam_completed_msg, rendered_response) self.assertIn(self.proctored_exam_completed_msg, rendered_response)
@patch.dict('django.conf.settings.PROCTORING_SETTINGS', {'ALLOW_TIMED_OUT_STATE': True})
def test_get_studentview_expired(self): def test_get_studentview_expired(self):
""" """
Test for get_student_view proctored exam which has expired. Test for get_student_view proctored exam which has expired.
...@@ -1095,6 +1097,7 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -1095,6 +1097,7 @@ class ProctoredExamApiTests(LoggedInTestCase):
(ProctoredExamStudentAttemptStatus.error, ProctoredExamStudentAttemptStatus.started), (ProctoredExamStudentAttemptStatus.error, ProctoredExamStudentAttemptStatus.started),
) )
@ddt.unpack @ddt.unpack
@patch.dict('django.conf.settings.PROCTORING_SETTINGS', {'ALLOW_TIMED_OUT_STATE': True})
def test_illegal_status_transition(self, from_status, to_status): def test_illegal_status_transition(self, from_status, to_status):
""" """
Verify that we cannot reset backwards an attempt status Verify that we cannot reset backwards an attempt status
...@@ -1109,9 +1112,28 @@ class ProctoredExamApiTests(LoggedInTestCase): ...@@ -1109,9 +1112,28 @@ class ProctoredExamApiTests(LoggedInTestCase):
) )
with self.assertRaises(ProctoredExamIllegalStatusTransition): with self.assertRaises(ProctoredExamIllegalStatusTransition):
print '*** from = {} to {}'.format(from_status, to_status)
update_attempt_status( update_attempt_status(
exam_attempt.proctored_exam_id, exam_attempt.proctored_exam_id,
self.user.id, self.user.id,
to_status to_status
) )
def test_alias_timed_out(self):
"""
Verified that timed_out will automatically state transition
to ready_to_submit
"""
exam_attempt = self._create_started_exam_attempt()
update_attempt_status(
exam_attempt.proctored_exam_id,
self.user.id,
ProctoredExamStudentAttemptStatus.timed_out
)
exam_attempt = get_exam_attempt_by_id(exam_attempt.id)
self.assertEqual(
exam_attempt['status'],
ProctoredExamStudentAttemptStatus.ready_to_submit
)
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