//cid = null;
cid = 1234;
eid = 5678;
//var questions = null
var question_order = [];
var number_of_questions = null;
var question_type = null;
var loaded_question = null;
var review = false;
var allow_self_marking = true;
var dfile = null;
var a = null;
var b = null;
function setUpQuestions() {
// Set an order for the questions
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"]];
}
});
question_order = shuffleArray(question_order);
number_of_questions = Object.keys(questions).length;
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;
// Make sure answers is an array
//
loadQuestion(0, 1, true);
createReviewPanel();
}
// randomise order
function shuffleArray(array) {
var currentIndex = array.length,
temporaryValue,
randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
// Set up cornerstone (the dicom viewer)
cornerstoneBase64ImageLoader.external.cornerstone = cornerstone;
cornerstoneWebImageLoader.external.cornerstone = cornerstone;
//cornerstoneWebImageLoader.configure({
// beforeSend: function(xhr) {
// // Add custom headers here (e.g. auth tokens)
// //xhr.setRequestHeader('x-auth-token', 'my auth token');
// }
//});
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
//cornerstoneWADOImageLoader.configure({
// beforeSend: function(xhr) {
// // Add custom headers here (e.g. auth tokens)
// //xhr.setRequestHeader('APIKEY', 'my auth token');
// }
//});
cornerstoneTools.init();
//Set up database
var db = new Dexie("answers_database");
db.version(1).stores({
answers: "[cid+eid+qid+qidn], [cid+eid], ans",
flags: "[cid+eid+qid+qidn], [cid+eid]",
user_answers: "qid, ans"
});
setUpQuestions();
function loadQuestion(n, section = 1, force_reload = false) {
// Make sure we have an integer
n = parseInt(n);
qid = question_order[n];
current_question = questions[qid];
console.log("N=", n, section, current_question);
if (n == loaded_question && force_reload == false) {
// Question already loaded
setFocus(section);
return;
}
loaded_question = n;
// Set up question navigation
$(".navigation[value='next']").off();
$(".navigation[value='previous']").off();
if (n == 0) {
$(".navigation[value='previous']").attr("disabled", "disabled");
} else {
$(".navigation[value='previous']")
.removeAttr("disabled")
.click(function() {
loadQuestion(n - 1);
});
}
if (n == number_of_questions - 1) {
$(".navigation[value='next']").attr("disabled", "disabled");
} else {
$(".navigation[value='next']")
.removeAttr("disabled")
.click(function() {
loadQuestion(n + 1);
});
}
display_n = n + 1;
if (current_question.title) {
$(".question .title").get(0).innerHTML =
'' +
display_n +
" " +
current_question.title;
} else {
$(".question .title").get(0).innerHTML =
'' + display_n + "";
}
// Close any open figures
el = document.getElementById("dicom-image");
if (el != undefined) {
cornerstone.disable(el);
$(".canvas-panel").remove();
}
// Set up thumbnails
thumbnails = $(".thumbs");
// Why are we checking this?
if (thumbnails) {
thumbnails.empty();
// TODO: figure captions (need to extend base json)
current_question.images.forEach(function createThumbnail(image, id) {
// For thumbnails we only want a single image
if (Array.isArray(image)) {
image = image[0]; // Do we want the middle image?
}
thumbnails.append(
'
...
'
);
thumbnail = $(".figure .thumbnail").get(id);
//cornerstone.enable(thumbnail)
//based_img = current_question.images[id];
based_img = image;
// based (image) data url, just load the image directly
if (based_img.startsWith("data:image/")) {
img = $("", {
src: based_img,
id: "thumb-" + id,
class: "thumbnail",
title: "Click on the thumbnail to view and manipulate the image.",
draggable: "false",
style: "height: 100px;"
});
$("#figure-" + id).append(img);
// otherwise try to load it as a dicom
} else {
// convert the data url to a file
urltoFile(based_img, "dicom", "application/dicom").then(function(
dfile
) {
// load the file using cornerstoneWADO file loader
const imageId = cornerstoneWADOImageLoader.wadouri.fileManager.add(
dfile
);
cornerstone.loadAndCacheImage(imageId).then(function(image) {
img = $("").get(0);
img.id = "thumb-" + id;
img.class = "thumbnail";
img.title =
"Click on the thumbnail to view and manipulate the image.";
img.draggable = "false";
img.style = "height: 100px; width: 100px";
$("#figure-" + id).append(img);
const element = document.getElementById("thumb-" + id);
cornerstone.enable(element);
cornerstone.displayImage(element, image);
cornerstone.resize(element);
}); //.catch( function(error) {
});
}
});
}
$(".thumbs .figure").each(function handleThumbnailClicks(id, thumbnail) {
thumbnail.onclick = function(event) {
view(event, this);
};
});
function view(t, source) {
if (current_question) {
// Check if the figure is already loaded (or if another one is)
open_figure = $("#dicom-image").data("figure");
figure_to_load = source.id;
if (open_figure != undefined) {
// No full size figure / dicom loaded yet
if (figure_to_load == open_figure) {
return;
} else {
el = document.getElementById("dicom-image");
if (el != undefined) {
cornerstone.disable(el);
$(".canvas-panel").remove();
}
}
}
$(".figure-open")
.removeClass("figure-open")
.addClass("figure");
source.className = "figure-open";
$(".question").append(
'
'
);
images = current_question.images[figure_to_load.split("-")[1]];
//images = current_question.images
// Make sure we have an array
if (!Array.isArray(images)) {
images = [images];
}
load(images);
async function load(images) {
imageIds = [];
for (i = 0; i < images.length; i++) {
data_url = images[i];
// check stack type
if (data_url.startsWith("data:image")) {
imageId = "base64://" + data_url.split(",")[1];
imageIds.push(imageId);
} else if (data_url.startsWith("data:application/dicom")) {
//stack = stack.split(";")[1];
dfile = await urltoFile(data_url, "dicom", "application/dicom");
const imageId = cornerstoneWADOImageLoader.wadouri.fileManager.add(
dfile
);
imageIds.push(imageId);
//cornerstone.loadImage(imageId).then(function(image) {
// tempFunction(image);
//});
}
}
const stack = {
currentImageIdIndex: 0,
imageIds
};
//cornerstone.loadAndCacheImage(imageIds[0]).then(function(image) {
cornerstone.loadAndCacheImage(imageIds[0]).then(function(image) {
loadMainImage(image, stack);
});
}
}
}
//Answer setup
ap = $(".answer-panel");
ap.empty();
switch (current_question.type) {
case "rapid": {
ap.append(
'
' +
display_n +
'.1Case normal/abnormal
'
);
ap.append(
'
' +
display_n +
'.2Reason
'
);
// Handle changing display of optional answer fields and saving of data
$("#rapid-option").change(function(evt) {
if (evt.target.value == "Abnormal") {
$("#rapid-text").css("display", "block");
} else {
$("#rapid-text").css("display", "none");
}
db.answers.put({
cid,
eid: eid,
qid: qid,
qidn: "1",
ans: evt.target.value
});
updateReviewPanel();
});
// Save long answers on textchange
$(".long-answer").change(function(evt) {
// ignore blank text and delete any stored value
if (evt.target.value.length < 1) {
db.answers.delete([cid, eid, qid, "2"]);
$(
"#question-list-item-" + question_order.indexOf(qid) + "-2"
).removeClass("question-saved-localdb");
return;
}
//db.answers.put({aid: [cid, eid, qidn], ans: evt.target.value});
db.answers.put({
cid: cid,
eid: eid,
qid: qid,
qidn: "2",
ans: evt.target.value
});
$("#question-list-item-" + question_order.indexOf(qid) + "-2").addClass(
"question-saved-localdb"
);
updateReviewPanel();
});
// We chain our db requests as we can only check answers once
// they have been loaded (should probably use then or after)
db.answers
.get({ cid: cid, eid: eid, qid: qid, qidn: "1" })
.then(function(answer) {
if (answer != undefined) {
$("#rapid-option option:contains(" + answer.ans + ")").prop(
"selected",
true
);
// For some reason a change event is not fired...
if (answer.ans == "Abnormal") {
$("#rapid-text").css("display", "block");
}
$("#rapid-option-not-answered").remove();
}
})
.catch(function(error) {
console.log("error-", error);
})
.then(
db.answers
.get({ cid: cid, eid: eid, qid: qid, qidn: "2" })
.then(function(answer) {
if (answer != undefined) {
console.log(answer);
$(".long-answer").text(answer.ans);
}
markAnswer(qid, "rapid");
})
.catch(function(error) {
console.log("error-", error);
})
);
addFlagEvents();
loadFlagsFromDb("1");
loadFlagsFromDb("2");
break;
}
case "anatomy": {
ap.append(
'
"
);
$("#review-answer-list a").click(function(evt) {
loadQuestion(this.dataset.qid);
$("#review-overlay").hide();
});
db.user_answers
.get({ qid: qid })
.then(function(answers) {
question_answers = questions[qid]["answers"];
if (answers == undefined) {
} else {
question_answers = question_answers.concat(answers.ans);
}
section_1_answer = current_answers[[qid, "1"]];
section_2_answer = current_answers[[qid, "2"]];
if (section_1_answer == undefined) {
section_1_answer = "Not Answered";
}
if (section_2_answer == undefined) {
if (section_1_answer == "Normal") {
section_2_answer = "Normal";
} else {
section_2_answer = "Not Answered";
}
}
el = $("#review-answer-" + qid + " span");
// Helper function to define how review items are displayed
// Yes it is a bit shit
function setReviewAnswer(
el,
c,
user_answer,
normal,
question_answers
) {
if (normal) {
el.html(
"Answer: " +
user_answer +
" (Normal)"
);
} else {
el.html(
"Answer: " +
user_answer +
" (Abnormal: " +
question_answers.join(", ") +
")"
);
}
}
if (question_type == "rapid") {
// First check normal vs abnormal
if (questions[qid]["normal"] == true) {
if (section_1_answer == "Normal") {
setReviewAnswer(
el,
"correct",
section_1_answer,
true,
question_answers
);
correct_count++;
} else {
setReviewAnswer(
el,
"incorrect",
section_1_answer,
true,
question_answers
);
}
} else {
if (answerInArray(question_answers, section_2_answer)) {
correct_count++;
setReviewAnswer(
el,
"correct",
section_2_answer,
false,
question_answers
);
} else {
setReviewAnswer(
el,
"incorrect",
section_2_answer,
false,
question_answers
);
}
}
} else if (question_type == "anatomy") {
// Anatomy answers are either correct or incorrect
if (answerInArray(question_answers, section_1_answer)) {
correct_count++;
setReviewAnswer(
el,
"correct",
section_1_answer,
false,
question_answers
);
} else {
setReviewAnswer(
el,
"incorrect",
section_1_answer,
false,
question_answers
);
}
}
$("#review-score").text(
"Score: " + correct_count + " out of " + question_order.length
);
})
.catch(function(error) {
console.log("error-", error);
});
});
})
.catch(function(error) {
console.log("error - ", error);
});
}
// Marks the loaded question and updates display
function markAnswer(qid, type) {
console.log("mark", qid);
if (review == true) {
// Disable all possible answer elements
$("#rapid-option,.long-answer").attr("disabled", "true");
if (type == "rapid") {
option = document.getElementById("rapid-option");
if (current_question.normal == true) {
$(".answer-panel").append(
"
This is normal
"
);
if (option.value == "Normal") {
option.classList.add("correct");
} else {
option.classList.add("incorrect");
}
// If answer is normal we have nothing else to add.
return addFeedback();
}
}
$(".answer-panel").append(
"