Skip to content

Commit

Permalink
Major performance improvements
Browse files Browse the repository at this point in the history
Both GetValue and SetValue were being performed synchronously, which
caused clogging of the frontend with many get/set calls. To address this
we do the following:

- GetValue calls do not result in HTTP requests if the value is
available in the scorm_data.
- SetValue calls are performed asynchronously; however, the ordering of
the calls is preserved, for consistency. Values are stored in scorm_data
such that GetValue calls can return even faster.

These changes result in drastic user experience improvements.
  • Loading branch information
regisb committed Dec 22, 2020
1 parent 17ecd3b commit 8fe1614
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 19 deletions.
4 changes: 4 additions & 0 deletions openedxscorm/scormxblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ def student_view(self, context=None):
json_args={
"scorm_version": self.scorm_version,
"fullscreen_on_launch": self.fullscreen_on_launch,
"scorm_data": self.scorm_data,
},
)
return frag
Expand Down Expand Up @@ -306,6 +307,9 @@ def extract_folder_base_path(self):

@XBlock.json_handler
def scorm_get_value(self, data, _suffix):
"""
Here we get only the get_value events that were not filtered by the LMSGetValue js function.
"""
name = data.get("name")
if name in ["cmi.core.lesson_status", "cmi.completion_status"]:
return {"value": self.lesson_status}
Expand Down
76 changes: 57 additions & 19 deletions openedxscorm/static/js/src/scormxblock.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,21 +68,37 @@ function ScormXBlock(runtime, element, settings) {
fullscreenOnNextEvent = true;
}

// We only make calls to the get_value handler when absolutely required.
// These calls are synchronous and they can easily clog the scorm display.
var uncachedValues = [
"cmi.core.lesson_status",
"cmi.completion_status",
"cmi.success_status",
"cmi.core.score.raw",
"cmi.score.raw"
];
var getValueUrl = runtime.handlerUrl(element, 'scorm_get_value');
var GetValue = function(cmi_element) {
var handlerUrl = runtime.handlerUrl(element, 'scorm_get_value');

var response = $.ajax({
type: "POST",
url: handlerUrl,
data: JSON.stringify({
'name': cmi_element
}),
async: false
});
response = JSON.parse(response.responseText);
return response.value;
if (cmi_element in uncachedValues) {
var response = $.ajax({
type: "POST",
url: getValueUrl,
data: JSON.stringify({
'name': cmi_element
}),
async: false
});
response = JSON.parse(response.responseText);
return response.value;
} else if (cmi_element in settings.scorm_data) {
return settings.scorm_data[cmi_element];
}
return "";
};

var setValueEvents = [];
var processingSetValueEventsQueue = false;
var setValueUrl = runtime.handlerUrl(element, 'scorm_set_value');
var SetValue = function(cmi_element, value) {
// The first event causes the module to go fullscreen
// when the setting is enabled
Expand All @@ -92,26 +108,48 @@ function ScormXBlock(runtime, element, settings) {
enterFullscreen();
}
}

var handlerUrl = runtime.handlerUrl(element, 'scorm_set_value');

SetValueAsync(cmi_element, value);
return "true";
}
function SetValueAsync(cmi_element, value) {
setValueEvents.push([cmi_element, value]);
if (!processingSetValueEventsQueue) {
// There is no running queue processor so we start one
processSetValueQueueItem();
}
}
function processSetValueQueueItem() {
if (setValueEvents.length === 0) {
// Exit if there is no event left in the queue
processingSetValueEventsQueue = false;
return;
}
processingSetValueEventsQueue = true;
params = setValueEvents.shift();
cmi_element = params[0];
value = params[1];
if (!cmi_element in uncachedValues) {
// Update the local scorm data copy to fetch results faster with get_value
settings.scorm_data[cmi_element] = value;
}
$.ajax({
type: "POST",
url: handlerUrl,
url: setValueUrl,
data: JSON.stringify({
'name': cmi_element,
'value': value
}),
async: false,
success: function(response) {
if (typeof response.lesson_score != "undefined") {
$(element).find(".lesson_score").html(response.lesson_score);
}
$(element).find(".completion_status").html(response.completion_status);
},
complete: function() {
// Recursive call to itself
processSetValueQueueItem();
}
});

return "true";
};

$(function($) {
Expand Down

0 comments on commit 8fe1614

Please sign in to comment.