Skip to content

Commit 4330e20

Browse files
committed
Only init exam storage after questions are ready
Now that questions can take some time to become ready after the list is initialised, the total marks available for the exam aren't calculated at the time Exam.init runs. This moves the storage initialisation to happen after the questions are all ready. I don't think that anything can use the storage in the intervening time, so it should be safe.
1 parent 119541e commit 4330e20

4 files changed

Lines changed: 95 additions & 10 deletions

File tree

runtime/scripts/exam.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -637,17 +637,20 @@ Exam.prototype = /** @lends Numbas.Exam.prototype */ {
637637
{
638638
var exam = this;
639639
if(exam.store) {
640-
job(exam.store.init,exam.store,exam); //initialise storage
641-
job(exam.set_exam_variables, exam);
640+
exam.store.init(exam); //initialise storage
641+
exam.set_exam_variables();
642642
}
643+
643644
job(exam.chooseQuestionSubset,exam); //choose questions to use
644645
job(exam.makeQuestionList,exam); //create question objects
646+
645647
exam.signals.on('question list initialised', function() {
646648
if(exam.store) {
647-
job(exam.store.init_questions,exam.store,exam); //initialise question storage
648-
job(exam.store.save,exam.store); //make sure data get saved to LMS
649+
exam.store.init_questions(); //initialise question storage
650+
exam.store.save(); //make sure data get saved to LMS
649651
}
650652
});
653+
651654
var ready_signals = ['question list initialised'];
652655
if(exam.settings.navigateMode=='diagnostic') {
653656
ready_signals.push('diagnostic controller initialised');

runtime/scripts/scorm-storage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,6 @@ SCORMStorage.prototype = /** @lends Numbas.storage.SCORMStorage.prototype */ {
200200
this.set('cmi.score.scaled',0);
201201
this.set('cmi.score.raw',0);
202202
this.set('cmi.score.min',0);
203-
this.set('cmi.score.max',exam.mark);
204203
this.questionIndices = {};
205204
this.partIndices = {};
206205
},
@@ -210,6 +209,7 @@ SCORMStorage.prototype = /** @lends Numbas.storage.SCORMStorage.prototype */ {
210209
this.initQuestion(this.exam.questionList[i]);
211210
}
212211
this.setSuspendData();
212+
this.set('cmi.score.max', this.exam.mark);
213213
},
214214

215215
/** Initialise a question - make an objective for it, and initialise all its parts.

tests/numbas-runtime.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24904,17 +24904,20 @@ Exam.prototype = /** @lends Numbas.Exam.prototype */ {
2490424904
{
2490524905
var exam = this;
2490624906
if(exam.store) {
24907-
job(exam.store.init,exam.store,exam); //initialise storage
24908-
job(exam.set_exam_variables, exam);
24907+
exam.store.init(exam); //initialise storage
24908+
exam.set_exam_variables();
2490924909
}
24910+
2491024911
job(exam.chooseQuestionSubset,exam); //choose questions to use
2491124912
job(exam.makeQuestionList,exam); //create question objects
24913+
2491224914
exam.signals.on('question list initialised', function() {
2491324915
if(exam.store) {
24914-
job(exam.store.init_questions,exam.store,exam); //initialise question storage
24915-
job(exam.store.save,exam.store); //make sure data get saved to LMS
24916+
exam.store.init_questions(); //initialise question storage
24917+
exam.store.save(); //make sure data get saved to LMS
2491624918
}
2491724919
});
24920+
2491824921
var ready_signals = ['question list initialised'];
2491924922
if(exam.settings.navigateMode=='diagnostic') {
2492024923
ready_signals.push('diagnostic controller initialised');
@@ -27655,7 +27658,6 @@ SCORMStorage.prototype = /** @lends Numbas.storage.SCORMStorage.prototype */ {
2765527658
this.set('cmi.score.scaled',0);
2765627659
this.set('cmi.score.raw',0);
2765727660
this.set('cmi.score.min',0);
27658-
this.set('cmi.score.max',exam.mark);
2765927661
this.questionIndices = {};
2766027662
this.partIndices = {};
2766127663
},
@@ -27665,6 +27667,7 @@ SCORMStorage.prototype = /** @lends Numbas.storage.SCORMStorage.prototype */ {
2766527667
this.initQuestion(this.exam.questionList[i]);
2766627668
}
2766727669
this.setSuspendData();
27670+
this.set('cmi.score.max', this.exam.mark);
2766827671
},
2766927672

2767027673
/** Initialise a question - make an objective for it, and initialise all its parts.

tests/parts/part-tests.mjs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2498,6 +2498,85 @@ mark:
24982498
}
24992499
return API;
25002500
}
2501+
2502+
2503+
QUnit.test('SCORM initialisation', async function(assert) {
2504+
var done = assert.async();
2505+
var exam_def = {
2506+
name: "Exam",
2507+
question_groups: [
2508+
{
2509+
questions: [
2510+
{
2511+
name: "Q",
2512+
variables: {
2513+
x: {
2514+
name: "x",
2515+
definition: "random(1..100#0)",
2516+
description: "A random number between 1 and 100",
2517+
templateType: "anything"
2518+
}
2519+
},
2520+
parts: [
2521+
{
2522+
type: 'numberentry',
2523+
minvalue: '5',
2524+
maxvalue: '6',
2525+
marks: 3
2526+
}
2527+
]
2528+
}
2529+
]
2530+
}
2531+
]
2532+
};
2533+
await with_scorm(
2534+
async function(data, results, scorm) {
2535+
var e = Numbas.createExamFromJSON(exam_def,Numbas.store,false);
2536+
e.init();
2537+
await e.signals.on('ready');
2538+
const expected_data = {
2539+
"cmi.objectives._count": 1,
2540+
"cmi.interactions._count": 1,
2541+
"cmi.learner_name": "",
2542+
"cmi.learner_id": "",
2543+
"cmi.location": "",
2544+
"cmi.score.raw": 0,
2545+
"cmi.score.scaled": 0,
2546+
"cmi.score.min": 0,
2547+
"cmi.score.max": "3",
2548+
"cmi.total_time": 0,
2549+
"cmi.success_status": "unknown",
2550+
"cmi.completion_status": "incomplete",
2551+
"cmi.exit": "suspend",
2552+
"cmi.progress_measure": "0",
2553+
"cmi.session_time": "PT0H0M0S",
2554+
"cmi.objectives.0.id": "q0",
2555+
"cmi.objectives.0.score.min": "0",
2556+
"cmi.objectives.0.score.max": "3",
2557+
"cmi.objectives.0.score.raw": "0",
2558+
"cmi.objectives.0.success_status": "unknown",
2559+
"cmi.objectives.0.completion_status": "not attempted",
2560+
"cmi.objectives.0.progress_measure": "0",
2561+
"cmi.objectives.0.description": "Q",
2562+
"cmi.interactions.0.id": "q0p0",
2563+
"cmi.interactions.0.objectives.0.id": "q0",
2564+
"cmi.interactions.0.objectives._count": 1,
2565+
"cmi.interactions.0.weighting": "3",
2566+
"cmi.interactions.0.result": "0",
2567+
"cmi.interactions.0.description": "numberentry",
2568+
"cmi.interactions.0.type": "fill-in",
2569+
"cmi.interactions.0.correct_responses.0.pattern": "5[:]6",
2570+
"cmi.interactions.0.correct_responses._count": 1,
2571+
};
2572+
Object.entries(expected_data).forEach(([k,v]) => {
2573+
assert.equal(scorm.GetValue(k), v, `${k} = ${v}`);
2574+
});
2575+
done();
2576+
}
2577+
);
2578+
});
2579+
25012580
QUnit.test('Resume an exam',async function(assert) {
25022581
var done = assert.async();
25032582
var exam_def = {

0 commit comments

Comments
 (0)