2022-08-29 22:01:33 +00:00
|
|
|
<!DOCTYPE html>
|
|
|
|
<body>
|
2022-09-24 23:17:09 +00:00
|
|
|
<h1>MillironX's Anatomy Quiz Generator</h1>
|
|
|
|
<h2>Quiz material weighting</h2>
|
|
|
|
<div id="weights"></div>
|
2022-09-24 23:55:26 +00:00
|
|
|
<hr />
|
|
|
|
<div>
|
|
|
|
<label for="num-questions">Number of questions </label>
|
|
|
|
<input id="num-questions" type="number" value="15" />
|
|
|
|
</div>
|
|
|
|
<hr />
|
2022-09-25 00:02:54 +00:00
|
|
|
<button id="generator">Generate!</button>
|
2022-09-24 23:55:26 +00:00
|
|
|
<hr />
|
|
|
|
<h2>Quiz</h2>
|
2022-08-29 22:01:33 +00:00
|
|
|
<ul id="quiz-terms"></ul>
|
|
|
|
|
|
|
|
<script>
|
2022-09-22 22:55:01 +00:00
|
|
|
const BoldTerms = {
|
|
|
|
bones: {
|
2022-09-25 00:04:00 +00:00
|
|
|
scapula: {
|
|
|
|
// id: scapula
|
|
|
|
asymmetric: true, // id: glenoid cavity of scapula
|
|
|
|
features: {
|
|
|
|
// articulation: glenoid cavity of scapula (head of humerus)
|
|
|
|
"glenoid cavity": {
|
|
|
|
// id: spine of scapula
|
|
|
|
articulation: "head of humerus", // id: acromion of scapula
|
|
|
|
}, // attachment: acromion of scapula (deltoideus)
|
|
|
|
spine: {}, //
|
|
|
|
acromion: {
|
|
|
|
//
|
|
|
|
attachment: "deltoideus", //
|
|
|
|
}, //
|
|
|
|
}, //
|
|
|
|
}, //
|
2022-09-22 22:55:01 +00:00
|
|
|
humerus: {
|
|
|
|
asymmetric: true,
|
|
|
|
features: {
|
|
|
|
head: {
|
|
|
|
articulation: "glenoid cavity",
|
|
|
|
},
|
|
|
|
"intertubercular groove": {},
|
|
|
|
"greater tubercle": {},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
radius: {
|
|
|
|
asymmetric: true,
|
|
|
|
features: {
|
|
|
|
head: {},
|
|
|
|
"fovea capitis": {
|
|
|
|
articulation: "capitulum of humerus",
|
|
|
|
},
|
|
|
|
"articular circumference": {
|
|
|
|
articulation: "radial notch of ulna",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
muscles: {
|
|
|
|
"superficial pectoral": {
|
|
|
|
features: {
|
|
|
|
"descending pectoral": {
|
|
|
|
origin: "cranial sternebrae",
|
|
|
|
insertion: "greater tubercle of humerus",
|
|
|
|
action: "adduct forelimb",
|
|
|
|
},
|
|
|
|
"transverse pectoral": {
|
|
|
|
origin: "cranial sternebrae",
|
|
|
|
insertion: "greater tubercle of humerus",
|
|
|
|
action: "adduct forelimb",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"deep pectoral": {
|
|
|
|
origin: "ventral sternum",
|
|
|
|
insertion: "fleshy",
|
|
|
|
action: "flex and extend shoulder joint",
|
|
|
|
},
|
|
|
|
brachiocephalicus: {
|
|
|
|
features: {
|
|
|
|
cleidobrachialis: {
|
|
|
|
origin: "clavicle",
|
|
|
|
insertion: "cranial border of humerus",
|
|
|
|
action: "extend shoulder joint",
|
|
|
|
},
|
|
|
|
cleidocephalicus: {
|
|
|
|
origin: "clavicle",
|
|
|
|
insertion: "occipital bone",
|
|
|
|
action: "extend shoulder joint",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
joints: {
|
|
|
|
humeral: {},
|
|
|
|
cubiti: {},
|
|
|
|
metacarpophalangeal: {},
|
|
|
|
},
|
|
|
|
other: {
|
|
|
|
"superficial cervical lymph node": {},
|
|
|
|
"carotid sheath": {},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2022-09-24 23:02:35 +00:00
|
|
|
function structure_question(structure, props) {
|
|
|
|
let quiz_list = [];
|
|
|
|
|
|
|
|
quiz_list.push(`id: ${structure}`);
|
|
|
|
|
2022-09-25 00:17:55 +00:00
|
|
|
let questions = [];
|
2022-09-24 23:02:35 +00:00
|
|
|
if ("features" in props) {
|
|
|
|
for (feature in props["features"]) {
|
2022-09-25 00:17:55 +00:00
|
|
|
questions = feature_question(
|
2022-09-24 23:02:35 +00:00
|
|
|
`${feature} of ${structure}`,
|
|
|
|
props["features"][feature]
|
|
|
|
);
|
|
|
|
}
|
2022-09-25 00:17:55 +00:00
|
|
|
} else {
|
|
|
|
questions = feature_question(structure, props);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const question of questions) {
|
|
|
|
quiz_list.push(question);
|
2022-09-24 23:02:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return quiz_list;
|
|
|
|
}
|
|
|
|
|
|
|
|
function feature_question(feature, props) {
|
|
|
|
let quiz_list = [];
|
|
|
|
|
|
|
|
quiz_list.push(`id: ${feature}`);
|
|
|
|
|
|
|
|
for (const prop in props) {
|
|
|
|
if (prop != "asymmetric") {
|
|
|
|
quiz_list.push(property_question(feature, prop, props[prop]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return quiz_list;
|
|
|
|
}
|
|
|
|
|
|
|
|
function property_question(feature, prop_name, prop_content) {
|
|
|
|
return `${prop_name}: ${feature} (${prop_content})`;
|
|
|
|
}
|
|
|
|
|
2022-09-24 23:17:09 +00:00
|
|
|
const weights_spinner_div = document.getElementById("weights");
|
|
|
|
for (structure_type in BoldTerms) {
|
|
|
|
const form_div = document.createElement("div");
|
|
|
|
const weight_spinner = document.createElement("input");
|
|
|
|
weight_spinner.setAttribute("id", `${structure_type}-weight`);
|
|
|
|
weight_spinner.setAttribute("type", "number");
|
|
|
|
weight_spinner.setAttribute("value", "10");
|
|
|
|
const weight_spinner_label = document.createElement("label");
|
|
|
|
weight_spinner_label.setAttribute("for", `${structure_type}-weight`);
|
|
|
|
weight_spinner_label.textContent = `${structure_type} `;
|
|
|
|
form_div.appendChild(weight_spinner_label);
|
|
|
|
form_div.appendChild(weight_spinner);
|
|
|
|
weights_spinner_div.append(form_div);
|
|
|
|
}
|
|
|
|
|
2022-09-24 23:55:26 +00:00
|
|
|
// Shamelessly stolen from
|
|
|
|
// https://github.com/trekhleb/javascript-algorithms/blob/master/src/algorithms/statistics/weighted-random/weightedRandom.js
|
|
|
|
function weighted_random(items, weights) {
|
|
|
|
const cumulativeWeights = [];
|
|
|
|
for (let i = 0; i < weights.length; i += 1) {
|
|
|
|
cumulativeWeights[i] = weights[i] + (cumulativeWeights[i - 1] || 0);
|
|
|
|
}
|
|
|
|
const maxCumulativeWeight =
|
|
|
|
cumulativeWeights[cumulativeWeights.length - 1];
|
|
|
|
const randomNumber = maxCumulativeWeight * Math.random();
|
|
|
|
|
|
|
|
for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
|
|
|
|
if (cumulativeWeights[itemIndex] >= randomNumber) {
|
|
|
|
return items[itemIndex];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shamelessly stolen from
|
|
|
|
// https://stackoverflow.com/a/15106541
|
|
|
|
function random_child(object) {
|
|
|
|
const keys = Object.keys(object);
|
|
|
|
const i = Math.floor(Math.random() * keys.length);
|
|
|
|
return {
|
|
|
|
key: keys[i],
|
|
|
|
object: object[keys[i]],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
const terms_list = document.getElementById("quiz-terms");
|
|
|
|
|
|
|
|
function generate_quiz() {
|
2022-09-25 00:02:54 +00:00
|
|
|
terms_list.innerHTML = "";
|
|
|
|
|
2022-09-24 23:55:26 +00:00
|
|
|
let weights = [];
|
|
|
|
let types = [];
|
|
|
|
|
|
|
|
for (structure_type in BoldTerms) {
|
|
|
|
weights.push(
|
|
|
|
parseInt(document.getElementById(`${structure_type}-weight`).value)
|
|
|
|
);
|
|
|
|
types.push(structure_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
const num_questions = document.getElementById("num-questions").value;
|
|
|
|
for (let i = 0; i <= num_questions; i += 1) {
|
|
|
|
// Pick the random category
|
|
|
|
const structure_type = weighted_random(types, weights);
|
|
|
|
|
|
|
|
// Pick a random structure from that category
|
|
|
|
const rand_structure = random_child(BoldTerms[structure_type]);
|
|
|
|
|
|
|
|
// Get the list of questions for that structure
|
|
|
|
const questions = structure_question(
|
|
|
|
rand_structure.key,
|
|
|
|
rand_structure.object
|
|
|
|
);
|
|
|
|
|
|
|
|
// Get a random question from that list
|
|
|
|
const rand_question =
|
|
|
|
questions[Math.floor(Math.random() * questions.length)];
|
|
|
|
|
|
|
|
// Add that structure to the list
|
|
|
|
let term_item = document.createElement("li");
|
|
|
|
term_item.innerHTML = rand_question;
|
|
|
|
terms_list.appendChild(term_item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-25 00:02:54 +00:00
|
|
|
document
|
|
|
|
.getElementById("generator")
|
|
|
|
.addEventListener("click", generate_quiz);
|
|
|
|
|
2022-09-24 23:55:26 +00:00
|
|
|
generate_quiz();
|
2022-08-29 22:01:33 +00:00
|
|
|
</script>
|
|
|
|
</body>
|