Skip to content

Commit

Permalink
Merge pull request #466 from edx/matthugs/proctoring-js-ping-echo-basic
Browse files Browse the repository at this point in the history
ping-echo implementation
  • Loading branch information
matthugs authored Dec 6, 2018
2 parents 3c17fab + 3630683 commit 8d9aa23
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 19 deletions.
30 changes: 19 additions & 11 deletions edx_proctoring/static/index.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
export const handlerWrapper = (Handler) => {
let handler = new Handler({});
let handler = new Handler({});

self.addEventListener("message", (message) => {
switch(message.data.type) {
self.addEventListener('message', (message) => {
switch (message.data.type) {
case 'config': {
handler = new Handler(message.data.options);
break;
}
case 'startExamAttempt': {
if(handler.onStartExamAttempt) {
handler.onStartExamAttempt().then(() => self.postMessage({type: 'examAttemptStarted'}))
if (handler.onStartExamAttempt) {
handler.onStartExamAttempt()
.then(() => self.postMessage({ type: 'examAttemptStarted' }))
.catch(() => self.postMessage({ type: 'examAttemptStartFailed' }));
}
break;
}
case 'endExamAttempt': {
if(handler.onEndExamAttempt) {
handler.onEndExamAttempt().then(() => self.postMessage({type: 'examAttemptEnded'}))
if (handler.onEndExamAttempt) {
handler.onEndExamAttempt()
.then(() => self.postMessage({ type: 'examAttemptEnded' }))
.catch(() => self.postMessage({ type: 'examAttemptEndFailed' }));
}
break;
}
case 'ping': {
if(handler.onPing) {
handler.onPing().then(() => self.postMessage({type: 'echo'}))
if (handler.onPing) {
handler.onPing()
.then(() => self.postMessage({ type: 'echo' }))
.catch(() => self.postMessage({ type: 'pingFailed' }));
}
break;
}
default: {
break;
}
}
});

}
};
export default handlerWrapper;
32 changes: 28 additions & 4 deletions edx_proctoring/static/proctoring/js/exam_action_handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,38 @@ var edx = edx || {};
(function($) {
'use strict';

var ONE_MINUTE_MS = 60000;

var actionToMessageTypesMap = {
'submit': {
promptEventName: 'endExamAttempt',
responseEventName: 'examAttemptEnded'
successEventName: 'examAttemptEnded',
failureEventName: 'examAttemptEndFailed'
},
'start': {
promptEventName: 'startExamAttempt',
responseEventName: 'examAttemptStarted'
successEventName: 'examAttemptStarted',
failureEventName: 'examAttemptStartFailed'
},
'ping': {
promptEventName: 'ping',
successEventName: 'echo',
failureEventName: 'pingFailed'

}
};

function workerPromiseForEventNames(eventNames) {
return function() {
var proctoringBackendWorker = new Worker(edx.courseware.proctored_exam.configuredWorkerURL);
return new Promise(function(resolve) {
return new Promise(function(resolve, reject) {
var responseHandler = function(e) {
if (e.data.type === eventNames.responseEventName) {
if (e.data.type === eventNames.successEventName) {
proctoringBackendWorker.removeEventListener('message', responseHandler);
proctoringBackendWorker.terminate();
resolve();
} else {
reject();
}
};
proctoringBackendWorker.addEventListener('message', responseHandler);
Expand All @@ -31,6 +43,12 @@ var edx = edx || {};
};
}

function timeoutPromise(timeoutMilliseconds) {
return new Promise(function(resolve, reject) {
setTimeout(reject, timeoutMilliseconds);
});
}

// Update the state of the attempt
function updateExamAttemptStatusPromise(actionUrl, action) {
return function() {
Expand Down Expand Up @@ -90,6 +108,12 @@ var edx = edx || {};
.then(reloadPage);
}
}
edx.courseware.proctored_exam.pingApplication = function() {
return Promise.race([
workerPromiseForEventNames(actionToMessageTypesMap.ping)(),
timeoutPromise(ONE_MINUTE_MS)
]);
}


}).call(this, $);
19 changes: 19 additions & 0 deletions edx_proctoring/static/proctoring/js/views/proctored_exam_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ var edx = edx || {};
// remove callback on unload event
$(window).unbind('beforeunload', this.unloadMessage);
}
var desktopApplicationJsUrl = this.model.get('desktop_application_js_url');
if (desktopApplicationJsUrl && !edx.courseware.proctored_exam.configuredWorkerURL) {
edx.courseware.proctored_exam.configuredWorkerURL = desktopApplicationJsUrl;
}

this.render();
},
Expand Down Expand Up @@ -140,6 +144,9 @@ var edx = edx || {};
updateRemainingTime: function (self) {
self.timerTick ++;
self.secondsLeft --;
if (self.timerTick % self.poll_interval === self.poll_interval / 2) {
edx.courseware.proctored_exam.pingApplication().catch(self.endExamForFailureState.bind(self));
}
if (self.timerTick % self.poll_interval === 0) {
var url = self.model.url + '/' + self.model.get('attempt_id');
var queryString = '?sourceid=in_exam&proctored=' + self.model.get('taking_as_proctored');
Expand Down Expand Up @@ -175,6 +182,18 @@ var edx = edx || {};
self.reloadPage();
}
},
endExamForFailureState: function () {
var self = this;
return $.ajax({
data: {
action: 'error'
},
url: this.model.url + '/' + this.model.get('attempt_id'),
type: 'PUT'
}).done(function() {
self.reloadPage();
});
},
toggleTimerVisibility: function (event) {
var button = $(event.currentTarget);
var icon = button.find('i');
Expand Down
33 changes: 32 additions & 1 deletion edx_proctoring/static/proctoring/spec/proctored_exam_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ describe('ProctoredExamView', function () {
this.proctored_exam_view.updateRemainingTime(this.proctored_exam_view);
expect(reloadPage).toHaveBeenCalled();
});
it("resets the remainig exam time after the ajax response", function(){
it("resets the remaining exam time after the ajax response", function(){
this.server.respondWith(
"GET",
"/api/edx_proctoring/v1/proctored_exam/attempt/" +
Expand All @@ -95,4 +95,35 @@ describe('ProctoredExamView', function () {
this.proctored_exam_view.updateRemainingTime(this.proctored_exam_view);
expect(reloadPage).toHaveBeenCalled();
});
it("calls external js global function on off-beat", function() {
edx.courseware.proctored_exam.pingApplication = jasmine.createSpy().and.returnValue(Promise.resolve());
this.proctored_exam_view.timerTick = this.proctored_exam_view.poll_interval / 2 - 1;
this.proctored_exam_view.updateRemainingTime(this.proctored_exam_view);
expect(edx.courseware.proctored_exam.pingApplication).toHaveBeenCalled();
delete edx.courseware.proctored_exam.pingApplication
});
it("reloads the page after failure-state ajax call", function(done) {
this.server.respondWith(
function(request) {
request.respond(200,
{"Content-Type": "application/json"},
"{}"
);
}
);
var reloadPage = spyOn(this.proctored_exam_view, 'reloadPage');
this.proctored_exam_view.endExamForFailureState().done(function() {
expect(reloadPage).toHaveBeenCalled();
done();
});
this.server.respond();
});
it("sets global variable when unset", function() {
expect(window.edx.courseware.proctored_exam.configuredWorkerURL).toBeUndefined();
this.proctored_exam_view.model.set("desktop_application_js_url", "nonempty string");
expect(window.edx.courseware.proctored_exam.configuredWorkerURL).not.toBeUndefined();
this.proctored_exam_view.model.set("desktop_application_js_url", "another nonempty string");
expect(window.edx.courseware.proctored_exam.configuredWorkerURL).toEqual("nonempty string");
delete window.edx.courseware.proctored_exam.configuredWorkerURL;
});
});
3 changes: 2 additions & 1 deletion edx_proctoring/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,8 @@ def test_download_software_clicked_action(self):

@ddt.data(
('submit', ProctoredExamStudentAttemptStatus.submitted),
('decline', ProctoredExamStudentAttemptStatus.declined)
('decline', ProctoredExamStudentAttemptStatus.declined),
('error', ProctoredExamStudentAttemptStatus.error),
)
@ddt.unpack
def test_submit_exam_attempt(self, action, expected_status):
Expand Down
16 changes: 15 additions & 1 deletion edx_proctoring/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,12 @@ def put(self, request, attempt_id):
request.user.id,
ProctoredExamStudentAttemptStatus.download_software_clicked
)
elif action == 'error':
exam_attempt_id = update_attempt_status(
attempt['proctored_exam']['id'],
request.user.id,
ProctoredExamStudentAttemptStatus.error
)
elif action == 'decline':
exam_attempt_id = update_attempt_status(
attempt['proctored_exam']['id'],
Expand Down Expand Up @@ -485,6 +491,8 @@ def get(self, request): # pylint: disable=unused-argument
exam = exam_info['exam']
attempt = exam_info['attempt']

provider = get_backend_provider(exam)

time_remaining_seconds = get_time_remaining_for_attempt(attempt)

proctoring_settings = getattr(settings, 'PROCTORING_SETTINGS', {})
Expand All @@ -496,6 +504,11 @@ def get(self, request): # pylint: disable=unused-argument
critically_low_threshold_pct * float(attempt['allowed_time_limit_mins']) * 60
)

if provider:
desktop_application_js_url = provider.get_javascript()
else:
desktop_application_js_url = ''

exam_url_path = ''
try:
# resolve the LMS url, note we can't assume we're running in
Expand All @@ -521,7 +534,8 @@ def get(self, request): # pylint: disable=unused-argument
'accessibility_time_string': _('you have {remaining_time} remaining').format(
remaining_time=humanized_time(int(round(time_remaining_seconds / 60.0, 0)))
),
'attempt_status': attempt['status']
'attempt_status': attempt['status'],
'desktop_application_js_url': desktop_application_js_url
}
else:
response_dict = {
Expand Down
1 change: 1 addition & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ module.exports = function(config) {

//patterns to load all files in child folders
files: [
'node_modules/babel-polyfill/dist/polyfill.js', // polyfills for e.g. Promises
'edx_proctoring/static/proctoring/js/vendor/i18n.js',
'edx_proctoring/static/proctoring/js/vendor/jquery.js',
'edx_proctoring/static/proctoring/js/vendor/underscore.js',
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@
"access": "public"
},
"devDependencies": {
"babel-polyfill": "^6.26.0",
"gulp": "^3.9.0",
"gulp-karma": "0.0.1",
"jasmine-core": "^2.8.0",
"karma": "^0.13.0",
"karma-chrome-launcher": "^0.2.0",
"karma-coverage": "^1.1.1",
"karma-phantomjs-launcher": "^1.0.4",
"karma-jasmine": "^0.3.6",
"karma-jasmine-jquery": "0.1.1",
"karma-phantomjs-launcher": "^1.0.4",
"karma-sinon": "^1.0.5",
"phantomjs-prebuilt": "^2.1.14",
"sinon": "^3.2.1"
Expand Down

0 comments on commit 8d9aa23

Please sign in to comment.