Commit 24b968ce by Chris Dodge

add warning when navigating away from exam when in proctored exam

parent 9bd100c2
...@@ -78,7 +78,7 @@ class ProctoredExamStudentAttemptSerializer(serializers.ModelSerializer): ...@@ -78,7 +78,7 @@ class ProctoredExamStudentAttemptSerializer(serializers.ModelSerializer):
fields = ( fields = (
"id", "created", "modified", "user_id", "started_at", "completed_at", "id", "created", "modified", "user_id", "started_at", "completed_at",
"external_id", "status", "proctored_exam_id", "allowed_time_limit_mins", "external_id", "status", "proctored_exam_id", "allowed_time_limit_mins",
"attempt_code", "is_sample_attempt" "attempt_code", "is_sample_attempt", "taking_as_proctored"
) )
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
defaults: { defaults: {
in_timed_exam: false, in_timed_exam: false,
is_proctored: false, taking_as_proctored: false,
exam_display_name: '', exam_display_name: '',
exam_url_path: '', exam_url_path: '',
time_remaining_seconds: 0, time_remaining_seconds: 0,
......
...@@ -16,6 +16,10 @@ var edx = edx || {}; ...@@ -16,6 +16,10 @@ var edx = edx || {};
/* give an extra 5 seconds where the timer holds at 00:00 before page refreshes */ /* give an extra 5 seconds where the timer holds at 00:00 before page refreshes */
this.grace_period_secs = 5; this.grace_period_secs = 5;
// we need to keep a copy here because the model will
// get destroyed before onbeforeunload is called
this.taking_as_proctored = false;
var template_html = $(this.templateId).text(); var template_html = $(this.templateId).text();
if (template_html !== null) { if (template_html !== null) {
/* don't assume this backbone view is running on a page with the underscore templates */ /* don't assume this backbone view is running on a page with the underscore templates */
...@@ -24,12 +28,24 @@ var edx = edx || {}; ...@@ -24,12 +28,24 @@ var edx = edx || {};
/* re-render if the model changes */ /* re-render if the model changes */
this.listenTo(this.model, 'change', this.modelChanged); this.listenTo(this.model, 'change', this.modelChanged);
$(window).unbind('beforeunload', this.unloadMessage);
/* make the async call to the backend REST API */ /* make the async call to the backend REST API */
/* after it loads, the listenTo event will file and */ /* after it loads, the listenTo event will file and */
/* will call into the rendering */ /* will call into the rendering */
this.model.fetch(); this.model.fetch();
}, },
modelChanged: function () { modelChanged: function () {
// if we are a proctored exam, then we need to alert user that he/she
// should not leave the exam
if (this.model.get('taking_as_proctored') && this.model.get('time_remaining_seconds') > 0) {
$(window).bind('beforeunload', this.unloadMessage);
} else {
// remove callback on unload event
$(window).unbind('beforeunload', this.unloadMessage);
}
this.render(); this.render();
}, },
render: function () { render: function () {
...@@ -44,13 +60,20 @@ var edx = edx || {}; ...@@ -44,13 +60,20 @@ var edx = edx || {};
} }
return this; return this;
}, },
unloadMessage: function () {
return "As you are currently taking a proctored exam,\n" +
"you should not be navigation away from the exam.\n" +
"This may be considered as a violation of the \n" +
"proctored exam and you may be disqualified for \n" +
"credit eligibility in this course.\n";
},
updateRemainingTime: function (self) { updateRemainingTime: function (self) {
self.$el.find('div.exam-timer').removeClass("low-time warning critical"); self.$el.find('div.exam-timer').removeClass("low-time warning critical");
self.$el.find('div.exam-timer').addClass(self.model.getRemainingTimeState()); self.$el.find('div.exam-timer').addClass(self.model.getRemainingTimeState());
self.$el.find('span#time_remaining_id b').html(self.model.getFormattedRemainingTime()); self.$el.find('span#time_remaining_id b').html(self.model.getFormattedRemainingTime());
if (self.model.getRemainingSeconds() <= -self.grace_period_secs) { if (self.model.getRemainingSeconds() <= -self.grace_period_secs) {
clearInterval(self.timerId); // stop the timer once the time finishes. clearInterval(self.timerId); // stop the timer once the time finishes.
$(window).unbind('beforeunload', this.unloadMessage);
// refresh the page when the timer expired // refresh the page when the timer expired
location.reload(); location.reload();
} }
......
...@@ -377,7 +377,7 @@ class StudentProctoredExamAttemptCollection(AuthenticatedAPIView): ...@@ -377,7 +377,7 @@ class StudentProctoredExamAttemptCollection(AuthenticatedAPIView):
response_dict = { response_dict = {
'in_timed_exam': True, 'in_timed_exam': True,
'is_proctored': True, 'taking_as_proctored': exam['attempt']['taking_as_proctored'],
'exam_display_name': exam['exam']['exam_name'], 'exam_display_name': exam['exam']['exam_name'],
'exam_url_path': '', 'exam_url_path': '',
'time_remaining_seconds': time_remaining_seconds, 'time_remaining_seconds': time_remaining_seconds,
......
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