From 5f64d0dd1e7ac79adb261975ff8b0885ac0e2f19 Mon Sep 17 00:00:00 2001 From: Ross Date: Wed, 10 Mar 2021 10:07:33 +0000 Subject: [PATCH] improvements to local question cache --- css/main.css | 15 +++ index.html | 2 +- js/main.js | 251 ++++++++++++++++++++++++++++++--------------------- 3 files changed, 165 insertions(+), 103 deletions(-) diff --git a/css/main.css b/css/main.css index 051cd02..7a5739e 100644 --- a/css/main.css +++ b/css/main.css @@ -613,6 +613,21 @@ img { 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 { overflow: scroll; padding: 20px; diff --git a/index.html b/index.html index cdfb0e7..0854bb7 100644 --- a/index.html +++ b/index.html @@ -163,7 +163,7 @@ -
+
diff --git a/js/main.js b/js/main.js index 57b8fb3..7bb88bd 100644 --- a/js/main.js +++ b/js/main.js @@ -30,13 +30,17 @@ let question_type = null; let loaded_question = null; window.review = false; let exam_time = null; +let packet_time = null; let date_started = null; let score = 0; +let packet_generated = null; let allow_self_marking = true; let timer = null; +let use_local_question_cache = false; + //window.dfile = null; window.config = config; @@ -131,6 +135,8 @@ async function loadExamList(data) { window.exam_list.forEach(function (exam) { let name = exam["name"]; let url = exam["url"]; + let eid = exam["eid"]; + let generated = exam["json_creation_time"]; let c = ""; if (exams_started.indexOf(name) > -1) { c = " session-started"; @@ -156,7 +162,7 @@ async function loadExamList(data) { $(`
`) .text(name) .click(function () { - loadPacketFromAjax(url); + loadPacketFromAjax(url, eid, generated); }) ); }); @@ -256,35 +262,63 @@ async function loadPacketList(data) { * Loads the requisite packet into the quiz system * @param {string} path - relative path to the packet json file */ -function loadPacketFromAjax(path) { - //console.log("loading packet from " + path); - $("#progress").html(`Requesting packet: ${path}`); +function loadPacketFromAjax(path, eid, generated) { + if (eid != undefined) { + // 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({ - dataType: "json", - url: path, - cache: true, - progress: function (e) { - if (e.lengthComputable) { - var completedPercentage = Math.round((e.loaded * 100) / e.total); + return; + } - $("#progress").html( - `${completedPercentage}%
${helper.formatBytes(e.total)}` - ); - } - }, - }) - .done(function (data) { - //setUpPacket(data, path.split("/").pop()); - if (data.hasOwnProperty("cached") && data["cached"]) { - //console.log("loading cached packet"); - } - setUpPacket(data, path); - $("#options-panel").hide(); + function ajaxRequestionPacket() { + //console.log("loading packet from " + path); + $("#progress").html(`Requesting packet: ${path}`); + + $.ajax({ + dataType: "json", + url: path, + cache: true, + progress: function (e) { + if (e.lengthComputable) { + var completedPercentage = Math.round((e.loaded * 100) / e.total); + + $("#progress").html( + `${completedPercentage}%
${helper.formatBytes(e.total)}` + ); + } + }, }) - .fail(function () { - //console.log("Unable to load packet at: " + path); - }); + .done(function (data) { + 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) { if (questions == undefined) { - return; - } - - 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 = []; + //return; + } else { + if (!use_local_question_cache) { 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 - if (!Array.isArray(questions[e]["answers"])) { - questions[e]["answers"] = [questions[e]["answers"]]; - } + question_db.question_data.put(d); }); } - 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); + // Set an order for the questions + if (!load_previous) { + if (question_order.length < 1) { + question_order = []; + Object.keys(questions).forEach(function (e) { + 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; // Horrible way to get type of questions // We assume they are all of the same type.... - question_type = - questions[Object.keys(questions)[0]].type; + //question_type = questions[Object.keys(questions)[0]].type; if (window.exam_details.exam_mode) { $("#options-button, #review-overlay-button").hide(); @@ -344,7 +379,6 @@ function setUpQuestions(load_previous) { questions = null; - //console.log(question_type); // exam_time can be defined in packets @@ -425,10 +459,10 @@ db.version(1).stores({ const question_db = new Dexie("question_database"); 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) @@ -447,12 +481,16 @@ function setUpPacket(data, path) { window.exam_details.eid = data.eid; } - if (data.hasOwnProperty("type")) { - question_type = data.type; + if (data.hasOwnProperty("exam_type")) { + question_type = data.exam_type; + } + + if (data.hasOwnProperty("generated")) { + packet_generated = data.generated; } if (data.hasOwnProperty("exam_mode")) { - window.exam_details.exam_mode = data.exam_mode; + exam_details.exam_mode = data.exam_mode; } if (data.hasOwnProperty("questions")) { @@ -467,8 +505,20 @@ function setUpPacket(data, path) { if (data.hasOwnProperty("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 (data.hasOwnProperty("question_requests") && data["question_requests"]) { const question_total = Object.keys(data["questions"]).length; @@ -506,7 +556,19 @@ function setUpPacket(data, path) { //requests.push(request) //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 loadSession(); @@ -524,10 +586,7 @@ function loadSession() { // .where("status") // .equals("active") .where("[packet+aid]") - .between( - [packet_name, Dexie.minKey], - [packet_name, Dexie.maxKey] - ) + .between([packet_name, Dexie.minKey], [packet_name, Dexie.maxKey]) .toArray() .then((sessions) => { //console.log("sessions", sessions); @@ -621,20 +680,18 @@ function loadSession() { * @param {boolean} force_reload - Force question to be reloaded if already loaded */ async function loadQuestion(n, section = 1, force_reload = false) { - $("#question-loading").show(); const aid = window.exam_details.aid; const cid = window.exam_details.cid; const eid = window.exam_details.eid; - // Make sure we have an integer n = parseInt(n); //console.log("loading question (n)", 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); 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) { // Question already loaded setFocus(section); + $("#question-loading").hide(); 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(answer); - $( - "#question-list-item-" + question_order.indexOf(qid) + "-2" - ).addClass("question-saved-localdb"); + $("#question-list-item-" + question_order.indexOf(qid) + "-2").addClass( + "question-saved-localdb" + ); saveSession(); 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(answer); - $( - "#question-list-item-" + question_order.indexOf(qid) + "-1" - ).addClass("question-saved-localdb"); + $("#question-list-item-" + question_order.indexOf(qid) + "-1").addClass( + "question-saved-localdb" + ); saveSession(); updateQuestionListPanel(answer); }); @@ -1008,10 +1066,7 @@ async function loadQuestion(n, section = 1, force_reload = false) { if (evt.target.value.length < 1) { db.answers.delete([aid, cid, eid, qid, qidn]); $( - "#question-list-item-" + - question_order.indexOf(qid) + - "-" + - qidn + "#question-list-item-" + question_order.indexOf(qid) + "-" + qidn ).removeClass("question-saved-localdb"); return; } @@ -1029,10 +1084,7 @@ async function loadQuestion(n, section = 1, force_reload = false) { console.log("SAVE", qid, qidn, evt.target.value); $( - "#question-list-item-" + - question_order.indexOf(qid) + - "-" + - qidn + "#question-list-item-" + question_order.indexOf(qid) + "-" + qidn ).addClass("question-saved-localdb"); saveSession(); updateQuestionListPanel(answer); @@ -1081,8 +1133,8 @@ async function loadQuestion(n, section = 1, force_reload = false) { //rebuildQuestionListPanel(); setTimeout(() => { setFocus(section); - $("#question-loading").hide(); }, 200); + $("#question-loading").hide(); } /** @@ -1121,17 +1173,15 @@ function updateQuestionListPanel(answer) { if (question_type == "rapid" && answer.qidn == "1") { if (answer.ans == "Abnormal") { - $( - "#question-list-item-" + - question_order.indexOf(answer.qid) + - "-2" - ).css("display", "block"); + $("#question-list-item-" + question_order.indexOf(answer.qid) + "-2").css( + "display", + "block" + ); } else { - $( - "#question-list-item-" + - question_order.indexOf(answer.qid) + - "-2" - ).css("display", "none"); + $("#question-list-item-" + question_order.indexOf(answer.qid) + "-2").css( + "display", + "none" + ); } } } @@ -1651,10 +1701,7 @@ function reviewQuestions() { score = correct_count; $("#review-score").text( - "Score: " + - correct_count + - " out of " + - question_order.length + "Score: " + correct_count + " out of " + question_order.length ); if (question_type == "rapid") {