improvements to local question cache

This commit is contained in:
Ross
2021-03-10 10:07:33 +00:00
parent 91e2308386
commit 5f64d0dd1e
3 changed files with 165 additions and 103 deletions
+15
View File
@@ -613,6 +613,21 @@ img {
overflow: auto; overflow: auto;
} }
.fullscreen-overlay-transparent {
position: fixed;
display: none;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 2;
cursor: pointer;
color: lightgray;
overflow: auto;
}
#review-overlay { #review-overlay {
overflow: scroll; overflow: scroll;
padding: 20px; padding: 20px;
+1 -1
View File
@@ -163,7 +163,7 @@
</div> </div>
</div> </div>
</div> </div>
<div id="question-loading" class="fullscreen-overlay"> <div id="question-loading" class="fullscreen-overlay-transparent">
<div class="progress-block"> <div class="progress-block">
<div class="lds-ripple"><div></div><div></div></div> <div class="lds-ripple"><div></div><div></div></div>
</div> </div>
+149 -102
View File
@@ -30,13 +30,17 @@ let question_type = null;
let loaded_question = null; let loaded_question = null;
window.review = false; window.review = false;
let exam_time = null; let exam_time = null;
let packet_time = null;
let date_started = null; let date_started = null;
let score = 0; let score = 0;
let packet_generated = null;
let allow_self_marking = true; let allow_self_marking = true;
let timer = null; let timer = null;
let use_local_question_cache = false;
//window.dfile = null; //window.dfile = null;
window.config = config; window.config = config;
@@ -131,6 +135,8 @@ async function loadExamList(data) {
window.exam_list.forEach(function (exam) { window.exam_list.forEach(function (exam) {
let name = exam["name"]; let name = exam["name"];
let url = exam["url"]; let url = exam["url"];
let eid = exam["eid"];
let generated = exam["json_creation_time"];
let c = ""; let c = "";
if (exams_started.indexOf(name) > -1) { if (exams_started.indexOf(name) > -1) {
c = " session-started"; c = " session-started";
@@ -156,7 +162,7 @@ async function loadExamList(data) {
$(`<div class='packet-button${c}' title='Load packet'></div>`) $(`<div class='packet-button${c}' title='Load packet'></div>`)
.text(name) .text(name)
.click(function () { .click(function () {
loadPacketFromAjax(url); loadPacketFromAjax(url, eid, generated);
}) })
); );
}); });
@@ -256,35 +262,63 @@ async function loadPacketList(data) {
* Loads the requisite packet into the quiz system * Loads the requisite packet into the quiz system
* @param {string} path - relative path to the packet json file * @param {string} path - relative path to the packet json file
*/ */
function loadPacketFromAjax(path) { function loadPacketFromAjax(path, eid, generated) {
//console.log("loading packet from " + path); if (eid != undefined) {
$("#progress").html(`Requesting packet: ${path}`); // try and load the exam from the local indexedDB
question_db.saved_exams
.get(eid)
.then((exam) => {
if (
exam == undefined ||
Date.parse(exam["generated"]) != Date.parse(generated)
) {
ajaxRequestionPacket();
} else {
console.log(exam);
// We have an up to date exam in the database
// TODO: check the questions are valid
use_local_question_cache = true;
setUpPacket(exam, path);
$("#options-panel").hide();
}
})
.catch(function (error) {
// Will be triggered if eid does not exist in database
console.log("Unable to load exam:", eid);
console.log("error-", error);
console.log("Exam will be downloaded");
ajaxRequestionPacket();
});
$.ajax({ return;
dataType: "json", }
url: path,
cache: true,
progress: function (e) {
if (e.lengthComputable) {
var completedPercentage = Math.round((e.loaded * 100) / e.total);
$("#progress").html( function ajaxRequestionPacket() {
`${completedPercentage}%<br/>${helper.formatBytes(e.total)}` //console.log("loading packet from " + path);
); $("#progress").html(`Requesting packet: ${path}`);
}
}, $.ajax({
}) dataType: "json",
.done(function (data) { url: path,
//setUpPacket(data, path.split("/").pop()); cache: true,
if (data.hasOwnProperty("cached") && data["cached"]) { progress: function (e) {
//console.log("loading cached packet"); if (e.lengthComputable) {
} var completedPercentage = Math.round((e.loaded * 100) / e.total);
setUpPacket(data, path);
$("#options-panel").hide(); $("#progress").html(
`${completedPercentage}%<br/>${helper.formatBytes(e.total)}`
);
}
},
}) })
.fail(function () { .done(function (data) {
//console.log("Unable to load packet at: " + path); setUpPacket(data, path);
}); $("#options-panel").hide();
})
.fail(function () {
console.log("Unable to load packet at: " + path);
});
}
} }
/** /**
@@ -292,49 +326,50 @@ function loadPacketFromAjax(path) {
*/ */
function setUpQuestions(load_previous) { function setUpQuestions(load_previous) {
if (questions == undefined) { if (questions == undefined) {
return; //return;
} } else {
if (!use_local_question_cache) {
Object.keys(questions).forEach(function (e) {
// Store question data into dexie
let d = {
eid: window.exam_details.eid,
qid: e,
data: questions[e],
};
question_db.question_data.put(d);
});
// Set an order for the questions
if (!load_previous) {
if (question_order.length < 1) {
question_order = [];
Object.keys(questions).forEach(function (e) { Object.keys(questions).forEach(function (e) {
question_order.push(e); // Store question data into dexie
let d = {
eid: window.exam_details.eid,
qid: e,
data: questions[e],
};
// Make sure answers are arrays question_db.question_data.put(d);
if (!Array.isArray(questions[e]["answers"])) {
questions[e]["answers"] = [questions[e]["answers"]];
}
}); });
} }
let randomise_order = true; // Set an order for the questions
if (window.config.randomise_order != undefined) { if (!load_previous) {
randomise_order = window.config.randomise_order; if (question_order.length < 1) {
} question_order = [];
if (randomise_order) { Object.keys(questions).forEach(function (e) {
question_order = helper.shuffleArray(question_order); question_order.push(e);
// Make sure answers are arrays
if (!Array.isArray(questions[e]["answers"])) {
questions[e]["answers"] = [questions[e]["answers"]];
}
});
}
let randomise_order = true;
if (window.config.randomise_order != undefined) {
randomise_order = window.config.randomise_order;
}
if (randomise_order) {
question_order = helper.shuffleArray(question_order);
}
} }
} }
window.number_of_questions = Object.keys(questions).length; window.number_of_questions = question_order.length;
window.review = false; window.review = false;
// Horrible way to get type of questions // Horrible way to get type of questions
// We assume they are all of the same type.... // We assume they are all of the same type....
question_type = //question_type = questions[Object.keys(questions)[0]].type;
questions[Object.keys(questions)[0]].type;
if (window.exam_details.exam_mode) { if (window.exam_details.exam_mode) {
$("#options-button, #review-overlay-button").hide(); $("#options-button, #review-overlay-button").hide();
@@ -344,7 +379,6 @@ function setUpQuestions(load_previous) {
questions = null; questions = null;
//console.log(question_type); //console.log(question_type);
// exam_time can be defined in packets // exam_time can be defined in packets
@@ -425,10 +459,10 @@ db.version(1).stores({
const question_db = new Dexie("question_database"); const question_db = new Dexie("question_database");
question_db.version(1).stores({ question_db.version(1).stores({
question_data: "[eid+qid], eid, data", question_data: "[eid+qid], eid",
saved_exams: "eid, type, exam_mode, name, order, time, generated",
}); });
//db = db;
/** /**
* Parse the packet and extract metadata (if it exists) * Parse the packet and extract metadata (if it exists)
@@ -447,12 +481,16 @@ function setUpPacket(data, path) {
window.exam_details.eid = data.eid; window.exam_details.eid = data.eid;
} }
if (data.hasOwnProperty("type")) { if (data.hasOwnProperty("exam_type")) {
question_type = data.type; question_type = data.exam_type;
}
if (data.hasOwnProperty("generated")) {
packet_generated = data.generated;
} }
if (data.hasOwnProperty("exam_mode")) { if (data.hasOwnProperty("exam_mode")) {
window.exam_details.exam_mode = data.exam_mode; exam_details.exam_mode = data.exam_mode;
} }
if (data.hasOwnProperty("questions")) { if (data.hasOwnProperty("questions")) {
@@ -467,8 +505,20 @@ function setUpPacket(data, path) {
if (data.hasOwnProperty("exam_time")) { if (data.hasOwnProperty("exam_time")) {
exam_time = data.exam_time; exam_time = data.exam_time;
// packet time is the time specificed by the packet
packet_time = data.exam_time;
} }
if (use_local_question_cache) {
// If loading form cache nothing else to do here
loadSession();
return;
}
// Save the details to the question database
var clone_data = Object.assign({}, data, { questions: undefined });
question_db.saved_exams.put(clone_data);
// If the question_requests is set we need to do a request for each question // If the question_requests is set we need to do a request for each question
if (data.hasOwnProperty("question_requests") && data["question_requests"]) { if (data.hasOwnProperty("question_requests") && data["question_requests"]) {
const question_total = Object.keys(data["questions"]).length; const question_total = Object.keys(data["questions"]).length;
@@ -506,7 +556,19 @@ function setUpPacket(data, path) {
//requests.push(request) //requests.push(request)
//request_numbers.push(n) //request_numbers.push(n)
data["questions"][n] = question_json;
// Using indexed db means that we can avoid loading all data into memory
//data["questions"][n] = question_json;
// Store question data into dexie
let d = {
eid: window.exam_details.eid,
qid: n,
data: question_json,
};
question_db.question_data.put(d);
use_local_question_cache = true;
} }
// Once all questions have been downloaded we carry on loading // Once all questions have been downloaded we carry on loading
loadSession(); loadSession();
@@ -524,10 +586,7 @@ function loadSession() {
// .where("status") // .where("status")
// .equals("active") // .equals("active")
.where("[packet+aid]") .where("[packet+aid]")
.between( .between([packet_name, Dexie.minKey], [packet_name, Dexie.maxKey])
[packet_name, Dexie.minKey],
[packet_name, Dexie.maxKey]
)
.toArray() .toArray()
.then((sessions) => { .then((sessions) => {
//console.log("sessions", sessions); //console.log("sessions", sessions);
@@ -621,20 +680,18 @@ function loadSession() {
* @param {boolean} force_reload - Force question to be reloaded if already loaded * @param {boolean} force_reload - Force question to be reloaded if already loaded
*/ */
async function loadQuestion(n, section = 1, force_reload = false) { async function loadQuestion(n, section = 1, force_reload = false) {
$("#question-loading").show(); $("#question-loading").show();
const aid = window.exam_details.aid; const aid = window.exam_details.aid;
const cid = window.exam_details.cid; const cid = window.exam_details.cid;
const eid = window.exam_details.eid; const eid = window.exam_details.eid;
// Make sure we have an integer // Make sure we have an integer
n = parseInt(n); n = parseInt(n);
//console.log("loading question (n)", n); //console.log("loading question (n)", n);
const qid = question_order[n]; const qid = question_order[n];
let q = {qid: qid.toString(), eid: eid}; let q = { qid: qid.toString(), eid: eid };
let return_data = await question_db.question_data.get(q); let return_data = await question_db.question_data.get(q);
const question_data = return_data.data; const question_data = return_data.data;
@@ -645,6 +702,7 @@ async function loadQuestion(n, section = 1, force_reload = false) {
if (n == loaded_question && force_reload == false) { if (n == loaded_question && force_reload == false) {
// Question already loaded // Question already loaded
setFocus(section); setFocus(section);
$("#question-loading").hide();
return; return;
} }
@@ -856,9 +914,9 @@ async function loadQuestion(n, section = 1, force_reload = false) {
// db.answers.put({aid: [cid, eid, qidn], ans: evt.target.value}); // db.answers.put({aid: [cid, eid, qidn], ans: evt.target.value});
db.answers.put(answer); db.answers.put(answer);
$( $("#question-list-item-" + question_order.indexOf(qid) + "-2").addClass(
"#question-list-item-" + question_order.indexOf(qid) + "-2" "question-saved-localdb"
).addClass("question-saved-localdb"); );
saveSession(); saveSession();
updateQuestionListPanel(answer); updateQuestionListPanel(answer);
@@ -938,9 +996,9 @@ async function loadQuestion(n, section = 1, force_reload = false) {
// db.answers.put({aid: [cid, eid, qidn], ans: evt.target.value}); // db.answers.put({aid: [cid, eid, qidn], ans: evt.target.value});
db.answers.put(answer); db.answers.put(answer);
$( $("#question-list-item-" + question_order.indexOf(qid) + "-1").addClass(
"#question-list-item-" + question_order.indexOf(qid) + "-1" "question-saved-localdb"
).addClass("question-saved-localdb"); );
saveSession(); saveSession();
updateQuestionListPanel(answer); updateQuestionListPanel(answer);
}); });
@@ -1008,10 +1066,7 @@ async function loadQuestion(n, section = 1, force_reload = false) {
if (evt.target.value.length < 1) { if (evt.target.value.length < 1) {
db.answers.delete([aid, cid, eid, qid, qidn]); db.answers.delete([aid, cid, eid, qid, qidn]);
$( $(
"#question-list-item-" + "#question-list-item-" + question_order.indexOf(qid) + "-" + qidn
question_order.indexOf(qid) +
"-" +
qidn
).removeClass("question-saved-localdb"); ).removeClass("question-saved-localdb");
return; return;
} }
@@ -1029,10 +1084,7 @@ async function loadQuestion(n, section = 1, force_reload = false) {
console.log("SAVE", qid, qidn, evt.target.value); console.log("SAVE", qid, qidn, evt.target.value);
$( $(
"#question-list-item-" + "#question-list-item-" + question_order.indexOf(qid) + "-" + qidn
question_order.indexOf(qid) +
"-" +
qidn
).addClass("question-saved-localdb"); ).addClass("question-saved-localdb");
saveSession(); saveSession();
updateQuestionListPanel(answer); updateQuestionListPanel(answer);
@@ -1081,8 +1133,8 @@ async function loadQuestion(n, section = 1, force_reload = false) {
//rebuildQuestionListPanel(); //rebuildQuestionListPanel();
setTimeout(() => { setTimeout(() => {
setFocus(section); setFocus(section);
$("#question-loading").hide();
}, 200); }, 200);
$("#question-loading").hide();
} }
/** /**
@@ -1121,17 +1173,15 @@ function updateQuestionListPanel(answer) {
if (question_type == "rapid" && answer.qidn == "1") { if (question_type == "rapid" && answer.qidn == "1") {
if (answer.ans == "Abnormal") { if (answer.ans == "Abnormal") {
$( $("#question-list-item-" + question_order.indexOf(answer.qid) + "-2").css(
"#question-list-item-" + "display",
question_order.indexOf(answer.qid) + "block"
"-2" );
).css("display", "block");
} else { } else {
$( $("#question-list-item-" + question_order.indexOf(answer.qid) + "-2").css(
"#question-list-item-" + "display",
question_order.indexOf(answer.qid) + "none"
"-2" );
).css("display", "none");
} }
} }
} }
@@ -1651,10 +1701,7 @@ function reviewQuestions() {
score = correct_count; score = correct_count;
$("#review-score").text( $("#review-score").text(
"Score: " + "Score: " + correct_count + " out of " + question_order.length
correct_count +
" out of " +
question_order.length
); );
if (question_type == "rapid") { if (question_type == "rapid") {