@pie-element/ebsr 13.1.2-next.5 → 13.2.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/configure/CHANGELOG.md +10 -0
- package/configure/package.json +3 -3
- package/controller/CHANGELOG.md +6 -0
- package/controller/lib/index.js +75 -6
- package/controller/lib/index.js.map +1 -1
- package/controller/package.json +1 -1
- package/controller/src/__tests__/index.test.js +1 -1
- package/controller/src/index.js +95 -5
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [13.2.0-next.0](https://github.com/pie-framework/pie-elements/compare/@pie-element/ebsr@13.1.2-next.6...@pie-element/ebsr@13.2.0-next.0) (2026-03-18)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- **ebsr:** add trace log for scoring in outcome function PD-5456 ([c83e1c2](https://github.com/pie-framework/pie-elements/commit/c83e1c29c8a0e7a03e3479cd00e9321b1e3f9edc))
|
|
11
|
+
|
|
12
|
+
## [13.1.2-next.6](https://github.com/pie-framework/pie-elements/compare/@pie-element/ebsr@13.1.2-next.5...@pie-element/ebsr@13.1.2-next.6) (2026-03-12)
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
- bump libs, update mathquill, switch interface PD-5791 ([686a7c0](https://github.com/pie-framework/pie-elements/commit/686a7c0d41ff82f5ddad7cecd93cc0c18324a81b))
|
|
17
|
+
|
|
6
18
|
## [13.1.2-next.5](https://github.com/pie-framework/pie-elements/compare/@pie-element/ebsr@13.1.2-next.4...@pie-element/ebsr@13.1.2-next.5) (2026-03-09)
|
|
7
19
|
|
|
8
20
|
**Note:** Version bump only for package @pie-element/ebsr
|
package/configure/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [12.1.2-next.7](https://github.com/pie-framework/pie-elements/compare/@pie-element/ebsr-configure@12.1.2-next.6...@pie-element/ebsr-configure@12.1.2-next.7) (2026-03-18)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @pie-element/ebsr-configure
|
|
9
|
+
|
|
10
|
+
## [12.1.2-next.6](https://github.com/pie-framework/pie-elements/compare/@pie-element/ebsr-configure@12.1.2-next.5...@pie-element/ebsr-configure@12.1.2-next.6) (2026-03-12)
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
- bump libs, update mathquill, switch interface PD-5791 ([686a7c0](https://github.com/pie-framework/pie-elements/commit/686a7c0d41ff82f5ddad7cecd93cc0c18324a81b))
|
|
15
|
+
|
|
6
16
|
## [12.1.2-next.5](https://github.com/pie-framework/pie-elements/compare/@pie-element/ebsr-configure@12.1.2-next.4...@pie-element/ebsr-configure@12.1.2-next.5) (2026-03-09)
|
|
7
17
|
|
|
8
18
|
**Note:** Version bump only for package @pie-element/ebsr-configure
|
package/configure/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-element/ebsr-configure",
|
|
3
3
|
"private": true,
|
|
4
|
-
"version": "12.1.2-next.
|
|
4
|
+
"version": "12.1.2-next.7",
|
|
5
5
|
"description": "",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"module": "src/index.js",
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"@emotion/style": "^0.8.0",
|
|
12
12
|
"@mui/icons-material": "^7.3.4",
|
|
13
13
|
"@mui/material": "^7.3.4",
|
|
14
|
-
"@pie-element/multiple-choice": "^12.
|
|
14
|
+
"@pie-element/multiple-choice": "^12.2.0-next.0",
|
|
15
15
|
"@pie-framework/pie-configure-events": "^1.2.0",
|
|
16
|
-
"@pie-lib/config-ui": "12.2.0-next.
|
|
16
|
+
"@pie-lib/config-ui": "12.2.0-next.17",
|
|
17
17
|
"lodash-es": "^4.17.23",
|
|
18
18
|
"prop-types": "^15.8.1",
|
|
19
19
|
"react": "18.3.1",
|
package/controller/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [10.2.0-next.0](https://github.com/pie-framework/pie-elements/compare/@pie-element/ebsr-controller@10.1.1-next.1...@pie-element/ebsr-controller@10.2.0-next.0) (2026-03-18)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- **ebsr:** add trace log for scoring in outcome function PD-5456 ([c83e1c2](https://github.com/pie-framework/pie-elements/commit/c83e1c29c8a0e7a03e3479cd00e9321b1e3f9edc))
|
|
11
|
+
|
|
6
12
|
## [10.1.1-next.1](https://github.com/pie-framework/pie-elements/compare/@pie-element/ebsr-controller@10.1.0-next.2...@pie-element/ebsr-controller@10.1.1-next.1) (2026-02-26)
|
|
7
13
|
|
|
8
14
|
**Note:** Version bump only for package @pie-element/ebsr-controller
|
package/controller/lib/index.js
CHANGED
|
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.createDefaultModel = exports.createCorrectResponseSession = void 0;
|
|
7
|
+
exports.getLogTrace = exports.createDefaultModel = exports.createCorrectResponseSession = void 0;
|
|
8
8
|
exports.model = model;
|
|
9
9
|
exports.normalize = void 0;
|
|
10
10
|
exports.outcome = outcome;
|
|
@@ -192,6 +192,69 @@ const getScore = (config, sessionPart, key, partialScoringEnabled) => {
|
|
|
192
192
|
}
|
|
193
193
|
return parseFloat(maxScore ? (correctChoices / maxScore).toFixed(2) : 0);
|
|
194
194
|
};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Generates detailed trace log for math-templated scoring evaluation
|
|
198
|
+
* @param {Object} question
|
|
199
|
+
* @param {Object} session
|
|
200
|
+
* @param {Object} env
|
|
201
|
+
* @returns {Array<string>} traceLog
|
|
202
|
+
*/
|
|
203
|
+
const getLogTrace = (question, session, env) => {
|
|
204
|
+
const traceLog = [];
|
|
205
|
+
if (!session || !session.value) {
|
|
206
|
+
traceLog.push('Student did not answer the question.');
|
|
207
|
+
return traceLog;
|
|
208
|
+
}
|
|
209
|
+
const {
|
|
210
|
+
value
|
|
211
|
+
} = session;
|
|
212
|
+
const {
|
|
213
|
+
partA,
|
|
214
|
+
partB
|
|
215
|
+
} = value;
|
|
216
|
+
const partialScoringEnabled = _controllerUtils.partialScoring.enabled(question, env);
|
|
217
|
+
traceLog.push(`Scoring method: ${partialScoringEnabled ? 'partial scoring' : 'all-or-nothing scoring'}.`);
|
|
218
|
+
const partAquestionType = question.partA.choiceMode === 'radio' ? 'multiple-choice (radio)' : 'multiple-select (checkbox)';
|
|
219
|
+
traceLog.push(`Part A question type: ${partAquestionType}.`);
|
|
220
|
+
const partASelections = partA?.value || [];
|
|
221
|
+
const partACorrectChoices = (question.partA.choices || []).filter(c => c.correct);
|
|
222
|
+
const partACorrectValues = partACorrectChoices.map(c => c.value);
|
|
223
|
+
const partACorrectSelected = partASelections.filter(v => partACorrectValues.includes(v));
|
|
224
|
+
const partAIncorrectSelected = partASelections.filter(v => !partACorrectValues.includes(v));
|
|
225
|
+
if (partASelections.length) {
|
|
226
|
+
traceLog.push(`Part A: student selected ${partASelections.length} choice(s) (${partACorrectSelected.length} correct, ${partAIncorrectSelected.length} incorrect).`);
|
|
227
|
+
}
|
|
228
|
+
const scoreA = getScore(question, partA, 'partA', partialScoringEnabled);
|
|
229
|
+
traceLog.push(`Part A score: ${scoreA}.`);
|
|
230
|
+
const partBquestionType = question.partB.choiceMode === 'radio' ? 'multiple-choice (radio)' : 'multiple-select (checkbox)';
|
|
231
|
+
traceLog.push(`Part B question type: ${partBquestionType}.`);
|
|
232
|
+
const partBSelections = partB?.value || [];
|
|
233
|
+
const partBCorrectChoices = (question.partB.choices || []).filter(c => c.correct);
|
|
234
|
+
const partBCorrectValues = partBCorrectChoices.map(c => c.value);
|
|
235
|
+
const correctSelected = partBSelections.filter(v => partBCorrectValues.includes(v));
|
|
236
|
+
const incorrectSelected = partBSelections.filter(v => !partBCorrectValues.includes(v));
|
|
237
|
+
if (partBSelections.length) {
|
|
238
|
+
traceLog.push(`Part B: student selected ${partBSelections.length} choice(s) (${correctSelected.length} correct, ${incorrectSelected.length} incorrect).`);
|
|
239
|
+
}
|
|
240
|
+
const scoreB = getScore(question, partB, 'partB', partialScoringEnabled);
|
|
241
|
+
traceLog.push(`Part B score: ${scoreB}.`);
|
|
242
|
+
if (!partialScoringEnabled) {
|
|
243
|
+
traceLog.push('Final score is awarded only if both Part A and Part B are completely correct.');
|
|
244
|
+
traceLog.push(`Final score: ${scoreA === 1 && scoreB === 1 ? 1 : 0}.`);
|
|
245
|
+
} else {
|
|
246
|
+
traceLog.push('With partial scoring enabled, Part A must be correct to earn any credit.');
|
|
247
|
+
let finalScore = 0;
|
|
248
|
+
if (scoreA === 1 && scoreB === 1) {
|
|
249
|
+
finalScore = 2;
|
|
250
|
+
} else if (scoreA === 1 && scoreB < 1) {
|
|
251
|
+
finalScore = 1;
|
|
252
|
+
}
|
|
253
|
+
traceLog.push(`Final score: ${finalScore}.`);
|
|
254
|
+
}
|
|
255
|
+
return traceLog;
|
|
256
|
+
};
|
|
257
|
+
exports.getLogTrace = getLogTrace;
|
|
195
258
|
function outcome(config, session, env) {
|
|
196
259
|
return new Promise(resolve => {
|
|
197
260
|
const {
|
|
@@ -202,7 +265,8 @@ function outcome(config, session, env) {
|
|
|
202
265
|
score: 0,
|
|
203
266
|
scoreA: 0,
|
|
204
267
|
scoreB: 0,
|
|
205
|
-
empty: true
|
|
268
|
+
empty: true,
|
|
269
|
+
logTrace: ['Student did not answer the question.']
|
|
206
270
|
});
|
|
207
271
|
}
|
|
208
272
|
if (value) {
|
|
@@ -213,6 +277,7 @@ function outcome(config, session, env) {
|
|
|
213
277
|
const partialScoringEnabled = _controllerUtils.partialScoring.enabled(config, env);
|
|
214
278
|
const scoreA = getScore(config, partA, 'partA', partialScoringEnabled);
|
|
215
279
|
const scoreB = getScore(config, partB, 'partB', partialScoringEnabled);
|
|
280
|
+
const logTrace = getLogTrace(config, session, env);
|
|
216
281
|
if (!partialScoringEnabled) {
|
|
217
282
|
// The EBSR item is worth 1 point
|
|
218
283
|
// That point is awarded if and only if both parts are fully correct, otherwise no points are awarded
|
|
@@ -220,7 +285,8 @@ function outcome(config, session, env) {
|
|
|
220
285
|
score: scoreA === 1 && scoreB === 1 ? 1 : 0,
|
|
221
286
|
scoreA,
|
|
222
287
|
scoreB,
|
|
223
|
-
max: 1
|
|
288
|
+
max: 1,
|
|
289
|
+
logTrace
|
|
224
290
|
});
|
|
225
291
|
} else {
|
|
226
292
|
// The EBSR item is worth 2 points
|
|
@@ -231,7 +297,8 @@ function outcome(config, session, env) {
|
|
|
231
297
|
score: 2,
|
|
232
298
|
scoreA,
|
|
233
299
|
scoreB,
|
|
234
|
-
max: 2
|
|
300
|
+
max: 2,
|
|
301
|
+
logTrace
|
|
235
302
|
});
|
|
236
303
|
} else {
|
|
237
304
|
// If Part A is correct and part B is incorrect, 1 point is awarded
|
|
@@ -239,7 +306,8 @@ function outcome(config, session, env) {
|
|
|
239
306
|
score: 1,
|
|
240
307
|
scoreA,
|
|
241
308
|
scoreB,
|
|
242
|
-
max: 2
|
|
309
|
+
max: 2,
|
|
310
|
+
logTrace
|
|
243
311
|
});
|
|
244
312
|
}
|
|
245
313
|
} else {
|
|
@@ -248,7 +316,8 @@ function outcome(config, session, env) {
|
|
|
248
316
|
score: 0,
|
|
249
317
|
scoreA,
|
|
250
318
|
scoreB,
|
|
251
|
-
max: 2
|
|
319
|
+
max: 2,
|
|
320
|
+
logTrace
|
|
252
321
|
});
|
|
253
322
|
}
|
|
254
323
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["_defaults","_interopRequireDefault","require","_controllerUtils","_utils","_lodashEs","_translator","translator","Translator","prepareChoice","model","env","defaultFeedback","choice","out","label","value","role","mode","rationale","rationaleEnabled","correct","feedbackEnabled","feedbackType","feedback","type","parsePart","part","key","session","Object","assign","incorrect","choices","map","disabled","complete","min","filter","c","length","responseCorrect","isResponseCorrect","undefined","normalizePart","base","choicesLayout","verticalMode","normalize","partA","partB","language","question","defaults","exports","updateSession","normalizedQuestion","shuffledValues","us","id","element","update","Promise","resolve","partASession","get","partALockChoiceOrder","lockChoices","partAChoices","partBChoices","getShuffledChoices","partBSession","partBLockChoiceOrder","isEmpty","catch","e","console","error","partLabels","partLabel","t","lng","index","partLabelType","teacherInstructions","teacherInstructionsEnabled","prompt","promptEnabled","extraCSSRules","createDefaultModel","isCorrect","getScore","config","sessionPart","partialScoringEnabled","maxScore","sessionPartValue","chosen","find","v","correctAndNotChosen","incorrectAndChosen","correctChoices","reduce","total","parseFloat","toFixed","outcome","score","scoreA","scoreB","empty","partialScoring","enabled","max","returnPartCorrect","answers","forEach","i","push","createCorrectResponseSession","partACorrect","partBCorrect","getInnerText","html","replaceAll","getContent","replace","validatePart","minAnswerChoices","maxAnswerChoices","reversedChoices","reverse","errors","choicesErrors","rationaleErrors","hasCorrectResponse","field","required","identicalAnswer","slice","some","nbOfChoices","answerChoices","correctResponse","validate","partAConfig","partBConfig","partAErrors","partBErrors"],"sources":["../src/index.js"],"sourcesContent":["import defaults from './defaults';\nimport { lockChoices, getShuffledChoices, partialScoring } from '@pie-lib/controller-utils';\nimport { isResponseCorrect } from './utils';\nimport { get, isEmpty } from 'lodash-es';\nimport Translator from '@pie-lib/translator';\n\nconst { translator } = Translator;\n\nconst prepareChoice = (model, env, defaultFeedback) => (choice) => {\n const out = {\n label: choice.label,\n value: choice.value,\n };\n\n if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {\n out.rationale = model.rationaleEnabled ? choice.rationale : null;\n } else {\n out.rationale = null;\n }\n\n if (env.mode === 'evaluate') {\n out.correct = !!choice.correct;\n\n if (model.feedbackEnabled) {\n const feedbackType = (choice.feedback && choice.feedback.type) || 'none';\n\n if (feedbackType === 'default') {\n out.feedback = defaultFeedback[choice.correct ? 'correct' : 'incorrect'];\n } else if (feedbackType === 'custom') {\n out.feedback = choice.feedback.value;\n }\n }\n }\n\n return out;\n};\n\nconst parsePart = (part, key, session, env) => {\n const defaultFeedback = Object.assign({ correct: 'Correct', incorrect: 'Incorrect' }, part.defaultFeedback);\n\n let choices = part.choices ? part.choices.map(prepareChoice(part, env, defaultFeedback)) : [];\n\n return {\n ...part,\n choices,\n disabled: env.mode !== 'gather',\n complete: {\n min: part.choices.filter((c) => c.correct).length,\n },\n responseCorrect: env.mode === 'evaluate' ? isResponseCorrect(part, key, session) : undefined,\n };\n};\n\nconst normalizePart = (model, base) => ({\n ...base,\n ...model,\n choicesLayout: model.choicesLayout || (model.verticalMode === false && 'horizontal') || 'vertical',\n});\n\nexport const normalize = ({ partA = {}, partB = {}, language, ...question }) => ({\n ...defaults,\n ...question,\n partA: normalizePart(partA, { ...defaults.partA, language }),\n partB: normalizePart(partB, { ...defaults.partB, language }),\n});\n\n/**\n *\n * @param {*} question\n * @param {*} session\n * @param {*} env\n * @param {*} updateSession - optional - a function that will set the properties passed into it on the session.\n */\nexport async function model(question, session, env, updateSession) {\n const normalizedQuestion = normalize(question);\n const partA = parsePart(normalizedQuestion.partA, 'partA', session, env);\n const partB = parsePart(normalizedQuestion.partB, 'partB', session, env);\n\n const shuffledValues = {};\n\n const us = (part) => (id, element, update) =>\n new Promise((resolve) => {\n shuffledValues[part] = update.shuffledValues;\n resolve();\n });\n\n const partASession = get(session, 'value.partA');\n const partALockChoiceOrder = lockChoices(normalizedQuestion.partA, partASession, env);\n\n const { choices: partAChoices } = partA || {};\n const { choices: partBChoices } = partB || {};\n\n if (!partALockChoiceOrder && partAChoices && partAChoices.length) {\n partA.choices = await getShuffledChoices(\n partAChoices,\n { shuffledValues: (session.shuffledValues || {}).partA },\n us('partA'),\n 'value',\n );\n }\n\n const partBSession = get(session, 'value.partB');\n const partBLockChoiceOrder = lockChoices(normalizedQuestion.partB, partBSession, env);\n\n if (!partBLockChoiceOrder && partBChoices && partBChoices.length) {\n partB.choices = await getShuffledChoices(\n partBChoices,\n { shuffledValues: (session.shuffledValues || {}).partB },\n us('partB'),\n 'value',\n );\n }\n\n if (!isEmpty(shuffledValues)) {\n if (updateSession && typeof updateSession === 'function') {\n updateSession(session.id, session.element, {\n shuffledValues,\n }).catch((e) => {\n // eslint-disable-next-line no-console\n console.error('update session failed', e);\n });\n }\n }\n\n if (normalizedQuestion.partLabels) {\n const language = normalizedQuestion.language;\n partA.partLabel = translator.t('ebsr.part', {\n lng: language,\n index: normalizedQuestion.partLabelType === 'Letters' ? 'A' : '1',\n });\n partB.partLabel = translator.t('ebsr.part', {\n lng: language,\n index: normalizedQuestion.partLabelType === 'Letters' ? 'B' : '2',\n });\n } else {\n partA.partLabel = undefined;\n partB.partLabel = undefined;\n }\n\n if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {\n partA.teacherInstructions = normalizedQuestion.partA.teacherInstructionsEnabled\n ? normalizedQuestion.partA.teacherInstructions\n : null;\n partB.teacherInstructions = normalizedQuestion.partB.teacherInstructionsEnabled\n ? normalizedQuestion.partB.teacherInstructions\n : null;\n } else {\n partA.teacherInstructions = null;\n partB.teacherInstructions = null;\n }\n\n partA.prompt = normalizedQuestion.partA.promptEnabled ? normalizedQuestion.partA.prompt : null;\n partB.prompt = normalizedQuestion.partB.promptEnabled ? normalizedQuestion.partB.prompt : null;\n\n return new Promise((resolve) => {\n resolve({\n disabled: env.mode !== 'gather',\n mode: env.mode,\n extraCSSRules: normalizedQuestion.extraCSSRules,\n partA,\n partB,\n });\n });\n}\n\nexport const createDefaultModel = (model = {}) =>\n new Promise((resolve) => {\n resolve({\n ...defaults,\n ...model,\n });\n });\n\nconst isCorrect = (c) => c.correct === true;\n\nconst getScore = (config, sessionPart, key, partialScoringEnabled) => {\n const { choices = [] } = (config && config[key]) || {};\n const maxScore = choices.length;\n const { value: sessionPartValue } = sessionPart || {};\n\n const chosen = (c) => !!(sessionPartValue || []).find((v) => v === c.value);\n const correctAndNotChosen = (c) => isCorrect(c) && !chosen(c);\n const incorrectAndChosen = (c) => !isCorrect(c) && chosen(c);\n const correctChoices = choices.reduce((total, choice) => {\n if (correctAndNotChosen(choice) || incorrectAndChosen(choice)) {\n return total - 1;\n } else {\n return total;\n }\n }, choices.length);\n\n // determine score for a part\n if (!partialScoringEnabled && correctChoices < maxScore) {\n return 0;\n }\n\n return parseFloat(maxScore ? (correctChoices / maxScore).toFixed(2) : 0);\n};\n\nexport function outcome(config, session, env) {\n return new Promise((resolve) => {\n const { value } = session || {};\n\n if (!session || !value) {\n resolve({ score: 0, scoreA: 0, scoreB: 0, empty: true });\n }\n\n if (value) {\n const { partA, partB } = value || {};\n\n const partialScoringEnabled = partialScoring.enabled(config, env);\n\n const scoreA = getScore(config, partA, 'partA', partialScoringEnabled);\n const scoreB = getScore(config, partB, 'partB', partialScoringEnabled);\n\n if (!partialScoringEnabled) {\n // The EBSR item is worth 1 point\n // That point is awarded if and only if both parts are fully correct, otherwise no points are awarded\n resolve({ score: scoreA === 1 && scoreB === 1 ? 1 : 0, scoreA, scoreB, max: 1 });\n } else {\n // The EBSR item is worth 2 points\n if (scoreA === 1) {\n if (scoreB === 1) {\n // If Part A and Part B are both correct, 2 points are awarded\n resolve({ score: 2, scoreA, scoreB, max: 2 });\n } else {\n // If Part A is correct and part B is incorrect, 1 point is awarded\n resolve({ score: 1, scoreA, scoreB, max: 2 });\n }\n } else {\n // For all other combinations, no points are awarded\n resolve({ score: 0, scoreA, scoreB, max: 2 });\n }\n }\n }\n });\n}\n\nconst returnPartCorrect = (choices) => {\n let answers = [];\n\n choices.forEach((i) => {\n const { correct, value } = i;\n if (correct) {\n answers.push(value);\n }\n });\n return answers;\n};\n\nexport const createCorrectResponseSession = (question, env) => {\n return new Promise((resolve) => {\n if (env.mode !== 'evaluate' && env.role === 'instructor') {\n const { partA, partB } = question;\n\n const partACorrect = returnPartCorrect(partA.choices);\n const partBCorrect = returnPartCorrect(partB.choices);\n\n resolve({\n value: {\n partA: {\n id: 'partA',\n value: partACorrect,\n },\n partB: {\n id: 'partB',\n value: partBCorrect,\n },\n },\n id: '1',\n });\n } else {\n resolve(null);\n }\n });\n};\n\n// remove all html tags\nconst getInnerText = (html) => (html || '').replaceAll(/<[^>]*>/g, '');\n\n// remove all html tags except img, iframe and source tag for audio\nconst getContent = (html) => (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '');\n\nconst validatePart = (model = {}, config = {}) => {\n const { choices } = model;\n const { minAnswerChoices = 2, maxAnswerChoices } = config;\n const reversedChoices = [...(choices || [])].reverse();\n const errors = {};\n const choicesErrors = {};\n const rationaleErrors = {};\n let hasCorrectResponse = false;\n\n ['teacherInstructions', 'prompt'].forEach((field) => {\n if (config[field]?.required && !getContent(model[field])) {\n errors[field] = 'This field is required.';\n }\n });\n\n reversedChoices.forEach((choice, index) => {\n const { correct, value, label, rationale } = choice;\n\n if (correct) {\n hasCorrectResponse = true;\n }\n\n if (!getContent(label)) {\n choicesErrors[value] = 'Content should not be empty.';\n } else {\n const identicalAnswer = reversedChoices.slice(index + 1).some((c) => c.label === label);\n\n if (identicalAnswer) {\n choicesErrors[value] = 'Content should be unique.';\n }\n }\n\n if (config.rationale?.required && !getContent(rationale)) {\n rationaleErrors[value] = 'This field is required.';\n }\n });\n\n const nbOfChoices = (choices || []).length;\n\n if (nbOfChoices < minAnswerChoices) {\n errors.answerChoices = `There should be at least ${minAnswerChoices} choices defined.`;\n } else if (nbOfChoices > maxAnswerChoices) {\n errors.answerChoices = `No more than ${maxAnswerChoices} choices should be defined.`;\n }\n\n if (!hasCorrectResponse) {\n errors.correctResponse = 'No correct response defined.';\n }\n\n if (!isEmpty(choicesErrors)) {\n errors.choices = choicesErrors;\n }\n\n if (!isEmpty(rationaleErrors)) {\n errors.rationale = rationaleErrors;\n }\n\n return errors;\n};\n\nexport const validate = (model = {}, config = {}) => {\n const { partA, partB } = model || {};\n const { partA: partAConfig, partB: partBConfig } = config || {};\n\n const partAErrors = validatePart(partA, partAConfig);\n const partBErrors = validatePart(partB, partBConfig);\n\n return { partA: partAErrors, partB: partBErrors };\n};\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"index.js","names":["_defaults","_interopRequireDefault","require","_controllerUtils","_utils","_lodashEs","_translator","translator","Translator","prepareChoice","model","env","defaultFeedback","choice","out","label","value","role","mode","rationale","rationaleEnabled","correct","feedbackEnabled","feedbackType","feedback","type","parsePart","part","key","session","Object","assign","incorrect","choices","map","disabled","complete","min","filter","c","length","responseCorrect","isResponseCorrect","undefined","normalizePart","base","choicesLayout","verticalMode","normalize","partA","partB","language","question","defaults","exports","updateSession","normalizedQuestion","shuffledValues","us","id","element","update","Promise","resolve","partASession","get","partALockChoiceOrder","lockChoices","partAChoices","partBChoices","getShuffledChoices","partBSession","partBLockChoiceOrder","isEmpty","catch","e","console","error","partLabels","partLabel","t","lng","index","partLabelType","teacherInstructions","teacherInstructionsEnabled","prompt","promptEnabled","extraCSSRules","createDefaultModel","isCorrect","getScore","config","sessionPart","partialScoringEnabled","maxScore","sessionPartValue","chosen","find","v","correctAndNotChosen","incorrectAndChosen","correctChoices","reduce","total","parseFloat","toFixed","getLogTrace","traceLog","push","partialScoring","enabled","partAquestionType","choiceMode","partASelections","partACorrectChoices","partACorrectValues","partACorrectSelected","includes","partAIncorrectSelected","scoreA","partBquestionType","partBSelections","partBCorrectChoices","partBCorrectValues","correctSelected","incorrectSelected","scoreB","finalScore","outcome","score","empty","logTrace","max","returnPartCorrect","answers","forEach","i","createCorrectResponseSession","partACorrect","partBCorrect","getInnerText","html","replaceAll","getContent","replace","validatePart","minAnswerChoices","maxAnswerChoices","reversedChoices","reverse","errors","choicesErrors","rationaleErrors","hasCorrectResponse","field","required","identicalAnswer","slice","some","nbOfChoices","answerChoices","correctResponse","validate","partAConfig","partBConfig","partAErrors","partBErrors"],"sources":["../src/index.js"],"sourcesContent":["import defaults from './defaults';\nimport { lockChoices, getShuffledChoices, partialScoring } from '@pie-lib/controller-utils';\nimport { isResponseCorrect } from './utils';\nimport { get, isEmpty } from 'lodash-es';\nimport Translator from '@pie-lib/translator';\n\nconst { translator } = Translator;\n\nconst prepareChoice = (model, env, defaultFeedback) => (choice) => {\n const out = {\n label: choice.label,\n value: choice.value,\n };\n\n if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {\n out.rationale = model.rationaleEnabled ? choice.rationale : null;\n } else {\n out.rationale = null;\n }\n\n if (env.mode === 'evaluate') {\n out.correct = !!choice.correct;\n\n if (model.feedbackEnabled) {\n const feedbackType = (choice.feedback && choice.feedback.type) || 'none';\n\n if (feedbackType === 'default') {\n out.feedback = defaultFeedback[choice.correct ? 'correct' : 'incorrect'];\n } else if (feedbackType === 'custom') {\n out.feedback = choice.feedback.value;\n }\n }\n }\n\n return out;\n};\n\nconst parsePart = (part, key, session, env) => {\n const defaultFeedback = Object.assign({ correct: 'Correct', incorrect: 'Incorrect' }, part.defaultFeedback);\n\n let choices = part.choices ? part.choices.map(prepareChoice(part, env, defaultFeedback)) : [];\n\n return {\n ...part,\n choices,\n disabled: env.mode !== 'gather',\n complete: {\n min: part.choices.filter((c) => c.correct).length,\n },\n responseCorrect: env.mode === 'evaluate' ? isResponseCorrect(part, key, session) : undefined,\n };\n};\n\nconst normalizePart = (model, base) => ({\n ...base,\n ...model,\n choicesLayout: model.choicesLayout || (model.verticalMode === false && 'horizontal') || 'vertical',\n});\n\nexport const normalize = ({ partA = {}, partB = {}, language, ...question }) => ({\n ...defaults,\n ...question,\n partA: normalizePart(partA, { ...defaults.partA, language }),\n partB: normalizePart(partB, { ...defaults.partB, language }),\n});\n\n/**\n *\n * @param {*} question\n * @param {*} session\n * @param {*} env\n * @param {*} updateSession - optional - a function that will set the properties passed into it on the session.\n */\nexport async function model(question, session, env, updateSession) {\n const normalizedQuestion = normalize(question);\n const partA = parsePart(normalizedQuestion.partA, 'partA', session, env);\n const partB = parsePart(normalizedQuestion.partB, 'partB', session, env);\n\n const shuffledValues = {};\n\n const us = (part) => (id, element, update) =>\n new Promise((resolve) => {\n shuffledValues[part] = update.shuffledValues;\n resolve();\n });\n\n const partASession = get(session, 'value.partA');\n const partALockChoiceOrder = lockChoices(normalizedQuestion.partA, partASession, env);\n\n const { choices: partAChoices } = partA || {};\n const { choices: partBChoices } = partB || {};\n\n if (!partALockChoiceOrder && partAChoices && partAChoices.length) {\n partA.choices = await getShuffledChoices(\n partAChoices,\n { shuffledValues: (session.shuffledValues || {}).partA },\n us('partA'),\n 'value',\n );\n }\n\n const partBSession = get(session, 'value.partB');\n const partBLockChoiceOrder = lockChoices(normalizedQuestion.partB, partBSession, env);\n\n if (!partBLockChoiceOrder && partBChoices && partBChoices.length) {\n partB.choices = await getShuffledChoices(\n partBChoices,\n { shuffledValues: (session.shuffledValues || {}).partB },\n us('partB'),\n 'value',\n );\n }\n\n if (!isEmpty(shuffledValues)) {\n if (updateSession && typeof updateSession === 'function') {\n updateSession(session.id, session.element, {\n shuffledValues,\n }).catch((e) => {\n // eslint-disable-next-line no-console\n console.error('update session failed', e);\n });\n }\n }\n\n if (normalizedQuestion.partLabels) {\n const language = normalizedQuestion.language;\n partA.partLabel = translator.t('ebsr.part', {\n lng: language,\n index: normalizedQuestion.partLabelType === 'Letters' ? 'A' : '1',\n });\n partB.partLabel = translator.t('ebsr.part', {\n lng: language,\n index: normalizedQuestion.partLabelType === 'Letters' ? 'B' : '2',\n });\n } else {\n partA.partLabel = undefined;\n partB.partLabel = undefined;\n }\n\n if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {\n partA.teacherInstructions = normalizedQuestion.partA.teacherInstructionsEnabled\n ? normalizedQuestion.partA.teacherInstructions\n : null;\n partB.teacherInstructions = normalizedQuestion.partB.teacherInstructionsEnabled\n ? normalizedQuestion.partB.teacherInstructions\n : null;\n } else {\n partA.teacherInstructions = null;\n partB.teacherInstructions = null;\n }\n\n partA.prompt = normalizedQuestion.partA.promptEnabled ? normalizedQuestion.partA.prompt : null;\n partB.prompt = normalizedQuestion.partB.promptEnabled ? normalizedQuestion.partB.prompt : null;\n\n return new Promise((resolve) => {\n resolve({\n disabled: env.mode !== 'gather',\n mode: env.mode,\n extraCSSRules: normalizedQuestion.extraCSSRules,\n partA,\n partB,\n });\n });\n}\n\nexport const createDefaultModel = (model = {}) =>\n new Promise((resolve) => {\n resolve({\n ...defaults,\n ...model,\n });\n });\n\nconst isCorrect = (c) => c.correct === true;\n\nconst getScore = (config, sessionPart, key, partialScoringEnabled) => {\n const { choices = [] } = (config && config[key]) || {};\n const maxScore = choices.length;\n const { value: sessionPartValue } = sessionPart || {};\n\n const chosen = (c) => !!(sessionPartValue || []).find((v) => v === c.value);\n const correctAndNotChosen = (c) => isCorrect(c) && !chosen(c);\n const incorrectAndChosen = (c) => !isCorrect(c) && chosen(c);\n const correctChoices = choices.reduce((total, choice) => {\n if (correctAndNotChosen(choice) || incorrectAndChosen(choice)) {\n return total - 1;\n } else {\n return total;\n }\n }, choices.length);\n\n // determine score for a part\n if (!partialScoringEnabled && correctChoices < maxScore) {\n return 0;\n }\n\n return parseFloat(maxScore ? (correctChoices / maxScore).toFixed(2) : 0);\n};\n\n/**\n * Generates detailed trace log for math-templated scoring evaluation\n * @param {Object} question\n * @param {Object} session\n * @param {Object} env\n * @returns {Array<string>} traceLog\n */\nexport const getLogTrace = (question, session, env) => {\n const traceLog = [];\n\n if (!session || !session.value) {\n traceLog.push('Student did not answer the question.');\n return traceLog;\n }\n\n const { value } = session;\n const { partA, partB } = value;\n\n const partialScoringEnabled = partialScoring.enabled(question, env);\n\n traceLog.push(\n `Scoring method: ${partialScoringEnabled ? 'partial scoring' : 'all-or-nothing scoring'}.`\n );\n\n const partAquestionType =\n question.partA.choiceMode === 'radio'\n ? 'multiple-choice (radio)'\n : 'multiple-select (checkbox)';\n traceLog.push(`Part A question type: ${partAquestionType}.`);\n\n const partASelections = partA?.value || [];\n const partACorrectChoices = (question.partA.choices || []).filter((c) => c.correct);\n const partACorrectValues = partACorrectChoices.map((c) => c.value);\n const partACorrectSelected = partASelections.filter((v) => partACorrectValues.includes(v));\n const partAIncorrectSelected = partASelections.filter((v) => !partACorrectValues.includes(v));\n\n if (partASelections.length) {\n traceLog.push(\n `Part A: student selected ${partASelections.length} choice(s) (${partACorrectSelected.length} correct, ${partAIncorrectSelected.length} incorrect).`\n );\n }\n \n const scoreA = getScore(question, partA, 'partA', partialScoringEnabled);\n traceLog.push(`Part A score: ${scoreA}.`);\n\n const partBquestionType =\n question.partB.choiceMode === 'radio'\n ? 'multiple-choice (radio)'\n : 'multiple-select (checkbox)';\n traceLog.push(`Part B question type: ${partBquestionType}.`);\n\n const partBSelections = partB?.value || [];\n const partBCorrectChoices = (question.partB.choices || []).filter((c) => c.correct);\n const partBCorrectValues = partBCorrectChoices.map((c) => c.value);\n const correctSelected = partBSelections.filter((v) => partBCorrectValues.includes(v));\n const incorrectSelected = partBSelections.filter((v) => !partBCorrectValues.includes(v));\n\n if (partBSelections.length) {\n traceLog.push(\n `Part B: student selected ${partBSelections.length} choice(s) (${correctSelected.length} correct, ${incorrectSelected.length} incorrect).`\n );\n }\n\n const scoreB = getScore(question, partB, 'partB', partialScoringEnabled);\n traceLog.push(`Part B score: ${scoreB}.`);\n\n if (!partialScoringEnabled) {\n traceLog.push(\n 'Final score is awarded only if both Part A and Part B are completely correct.'\n );\n traceLog.push(`Final score: ${scoreA === 1 && scoreB === 1 ? 1 : 0}.`);\n } else {\n traceLog.push(\n 'With partial scoring enabled, Part A must be correct to earn any credit.'\n );\n\n let finalScore = 0;\n if (scoreA === 1 && scoreB === 1) {\n finalScore = 2;\n } else if (scoreA === 1 && scoreB < 1) {\n finalScore = 1;\n }\n\n traceLog.push(`Final score: ${finalScore}.`);\n }\n\n return traceLog;\n};\n\nexport function outcome(config, session, env) {\n return new Promise((resolve) => {\n const { value } = session || {};\n\n if (!session || !value) {\n resolve({ score: 0, scoreA: 0, scoreB: 0, empty: true, logTrace: ['Student did not answer the question.'] });\n }\n\n if (value) {\n const { partA, partB } = value || {};\n\n const partialScoringEnabled = partialScoring.enabled(config, env);\n\n const scoreA = getScore(config, partA, 'partA', partialScoringEnabled);\n const scoreB = getScore(config, partB, 'partB', partialScoringEnabled);\n const logTrace = getLogTrace(config, session, env);\n\n if (!partialScoringEnabled) {\n // The EBSR item is worth 1 point\n // That point is awarded if and only if both parts are fully correct, otherwise no points are awarded\n resolve({ score: scoreA === 1 && scoreB === 1 ? 1 : 0, scoreA, scoreB, max: 1, logTrace });\n } else {\n // The EBSR item is worth 2 points\n if (scoreA === 1) {\n if (scoreB === 1) {\n // If Part A and Part B are both correct, 2 points are awarded\n resolve({ score: 2, scoreA, scoreB, max: 2, logTrace });\n } else {\n // If Part A is correct and part B is incorrect, 1 point is awarded\n resolve({ score: 1, scoreA, scoreB, max: 2, logTrace });\n }\n } else {\n // For all other combinations, no points are awarded\n resolve({ score: 0, scoreA, scoreB, max: 2, logTrace });\n }\n }\n }\n });\n}\n\nconst returnPartCorrect = (choices) => {\n let answers = [];\n\n choices.forEach((i) => {\n const { correct, value } = i;\n if (correct) {\n answers.push(value);\n }\n });\n return answers;\n};\n\nexport const createCorrectResponseSession = (question, env) => {\n return new Promise((resolve) => {\n if (env.mode !== 'evaluate' && env.role === 'instructor') {\n const { partA, partB } = question;\n\n const partACorrect = returnPartCorrect(partA.choices);\n const partBCorrect = returnPartCorrect(partB.choices);\n\n resolve({\n value: {\n partA: {\n id: 'partA',\n value: partACorrect,\n },\n partB: {\n id: 'partB',\n value: partBCorrect,\n },\n },\n id: '1',\n });\n } else {\n resolve(null);\n }\n });\n};\n\n// remove all html tags\nconst getInnerText = (html) => (html || '').replaceAll(/<[^>]*>/g, '');\n\n// remove all html tags except img, iframe and source tag for audio\nconst getContent = (html) => (html || '').replace(/(<(?!img|iframe|source)([^>]+)>)/gi, '');\n\nconst validatePart = (model = {}, config = {}) => {\n const { choices } = model;\n const { minAnswerChoices = 2, maxAnswerChoices } = config;\n const reversedChoices = [...(choices || [])].reverse();\n const errors = {};\n const choicesErrors = {};\n const rationaleErrors = {};\n let hasCorrectResponse = false;\n\n ['teacherInstructions', 'prompt'].forEach((field) => {\n if (config[field]?.required && !getContent(model[field])) {\n errors[field] = 'This field is required.';\n }\n });\n\n reversedChoices.forEach((choice, index) => {\n const { correct, value, label, rationale } = choice;\n\n if (correct) {\n hasCorrectResponse = true;\n }\n\n if (!getContent(label)) {\n choicesErrors[value] = 'Content should not be empty.';\n } else {\n const identicalAnswer = reversedChoices.slice(index + 1).some((c) => c.label === label);\n\n if (identicalAnswer) {\n choicesErrors[value] = 'Content should be unique.';\n }\n }\n\n if (config.rationale?.required && !getContent(rationale)) {\n rationaleErrors[value] = 'This field is required.';\n }\n });\n\n const nbOfChoices = (choices || []).length;\n\n if (nbOfChoices < minAnswerChoices) {\n errors.answerChoices = `There should be at least ${minAnswerChoices} choices defined.`;\n } else if (nbOfChoices > maxAnswerChoices) {\n errors.answerChoices = `No more than ${maxAnswerChoices} choices should be defined.`;\n }\n\n if (!hasCorrectResponse) {\n errors.correctResponse = 'No correct response defined.';\n }\n\n if (!isEmpty(choicesErrors)) {\n errors.choices = choicesErrors;\n }\n\n if (!isEmpty(rationaleErrors)) {\n errors.rationale = rationaleErrors;\n }\n\n return errors;\n};\n\nexport const validate = (model = {}, config = {}) => {\n const { partA, partB } = model || {};\n const { partA: partAConfig, partB: partBConfig } = config || {};\n\n const partAErrors = validatePart(partA, partAConfig);\n const partBErrors = validatePart(partB, partBConfig);\n\n return { partA: partAErrors, partB: partBErrors };\n};\n"],"mappings":"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","ignoreList":[]}
|
package/controller/package.json
CHANGED
|
@@ -312,7 +312,7 @@ describe('controller', () => {
|
|
|
312
312
|
const returnsScoreWhen = (session) => {
|
|
313
313
|
it(`returns empty: true if session is ${JSON.stringify(session)}`, async () => {
|
|
314
314
|
const result = await outcome(question, session);
|
|
315
|
-
expect(result).toEqual({ score: 0, scoreA: 0, scoreB: 0, empty: true });
|
|
315
|
+
expect(result).toEqual({ score: 0, scoreA: 0, scoreB: 0, empty: true, logTrace: ['Student did not answer the question.'] });
|
|
316
316
|
});
|
|
317
317
|
};
|
|
318
318
|
|
package/controller/src/index.js
CHANGED
|
@@ -197,12 +197,101 @@ const getScore = (config, sessionPart, key, partialScoringEnabled) => {
|
|
|
197
197
|
return parseFloat(maxScore ? (correctChoices / maxScore).toFixed(2) : 0);
|
|
198
198
|
};
|
|
199
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Generates detailed trace log for math-templated scoring evaluation
|
|
202
|
+
* @param {Object} question
|
|
203
|
+
* @param {Object} session
|
|
204
|
+
* @param {Object} env
|
|
205
|
+
* @returns {Array<string>} traceLog
|
|
206
|
+
*/
|
|
207
|
+
export const getLogTrace = (question, session, env) => {
|
|
208
|
+
const traceLog = [];
|
|
209
|
+
|
|
210
|
+
if (!session || !session.value) {
|
|
211
|
+
traceLog.push('Student did not answer the question.');
|
|
212
|
+
return traceLog;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const { value } = session;
|
|
216
|
+
const { partA, partB } = value;
|
|
217
|
+
|
|
218
|
+
const partialScoringEnabled = partialScoring.enabled(question, env);
|
|
219
|
+
|
|
220
|
+
traceLog.push(
|
|
221
|
+
`Scoring method: ${partialScoringEnabled ? 'partial scoring' : 'all-or-nothing scoring'}.`
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const partAquestionType =
|
|
225
|
+
question.partA.choiceMode === 'radio'
|
|
226
|
+
? 'multiple-choice (radio)'
|
|
227
|
+
: 'multiple-select (checkbox)';
|
|
228
|
+
traceLog.push(`Part A question type: ${partAquestionType}.`);
|
|
229
|
+
|
|
230
|
+
const partASelections = partA?.value || [];
|
|
231
|
+
const partACorrectChoices = (question.partA.choices || []).filter((c) => c.correct);
|
|
232
|
+
const partACorrectValues = partACorrectChoices.map((c) => c.value);
|
|
233
|
+
const partACorrectSelected = partASelections.filter((v) => partACorrectValues.includes(v));
|
|
234
|
+
const partAIncorrectSelected = partASelections.filter((v) => !partACorrectValues.includes(v));
|
|
235
|
+
|
|
236
|
+
if (partASelections.length) {
|
|
237
|
+
traceLog.push(
|
|
238
|
+
`Part A: student selected ${partASelections.length} choice(s) (${partACorrectSelected.length} correct, ${partAIncorrectSelected.length} incorrect).`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const scoreA = getScore(question, partA, 'partA', partialScoringEnabled);
|
|
243
|
+
traceLog.push(`Part A score: ${scoreA}.`);
|
|
244
|
+
|
|
245
|
+
const partBquestionType =
|
|
246
|
+
question.partB.choiceMode === 'radio'
|
|
247
|
+
? 'multiple-choice (radio)'
|
|
248
|
+
: 'multiple-select (checkbox)';
|
|
249
|
+
traceLog.push(`Part B question type: ${partBquestionType}.`);
|
|
250
|
+
|
|
251
|
+
const partBSelections = partB?.value || [];
|
|
252
|
+
const partBCorrectChoices = (question.partB.choices || []).filter((c) => c.correct);
|
|
253
|
+
const partBCorrectValues = partBCorrectChoices.map((c) => c.value);
|
|
254
|
+
const correctSelected = partBSelections.filter((v) => partBCorrectValues.includes(v));
|
|
255
|
+
const incorrectSelected = partBSelections.filter((v) => !partBCorrectValues.includes(v));
|
|
256
|
+
|
|
257
|
+
if (partBSelections.length) {
|
|
258
|
+
traceLog.push(
|
|
259
|
+
`Part B: student selected ${partBSelections.length} choice(s) (${correctSelected.length} correct, ${incorrectSelected.length} incorrect).`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const scoreB = getScore(question, partB, 'partB', partialScoringEnabled);
|
|
264
|
+
traceLog.push(`Part B score: ${scoreB}.`);
|
|
265
|
+
|
|
266
|
+
if (!partialScoringEnabled) {
|
|
267
|
+
traceLog.push(
|
|
268
|
+
'Final score is awarded only if both Part A and Part B are completely correct.'
|
|
269
|
+
);
|
|
270
|
+
traceLog.push(`Final score: ${scoreA === 1 && scoreB === 1 ? 1 : 0}.`);
|
|
271
|
+
} else {
|
|
272
|
+
traceLog.push(
|
|
273
|
+
'With partial scoring enabled, Part A must be correct to earn any credit.'
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
let finalScore = 0;
|
|
277
|
+
if (scoreA === 1 && scoreB === 1) {
|
|
278
|
+
finalScore = 2;
|
|
279
|
+
} else if (scoreA === 1 && scoreB < 1) {
|
|
280
|
+
finalScore = 1;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
traceLog.push(`Final score: ${finalScore}.`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return traceLog;
|
|
287
|
+
};
|
|
288
|
+
|
|
200
289
|
export function outcome(config, session, env) {
|
|
201
290
|
return new Promise((resolve) => {
|
|
202
291
|
const { value } = session || {};
|
|
203
292
|
|
|
204
293
|
if (!session || !value) {
|
|
205
|
-
resolve({ score: 0, scoreA: 0, scoreB: 0, empty: true });
|
|
294
|
+
resolve({ score: 0, scoreA: 0, scoreB: 0, empty: true, logTrace: ['Student did not answer the question.'] });
|
|
206
295
|
}
|
|
207
296
|
|
|
208
297
|
if (value) {
|
|
@@ -212,24 +301,25 @@ export function outcome(config, session, env) {
|
|
|
212
301
|
|
|
213
302
|
const scoreA = getScore(config, partA, 'partA', partialScoringEnabled);
|
|
214
303
|
const scoreB = getScore(config, partB, 'partB', partialScoringEnabled);
|
|
304
|
+
const logTrace = getLogTrace(config, session, env);
|
|
215
305
|
|
|
216
306
|
if (!partialScoringEnabled) {
|
|
217
307
|
// The EBSR item is worth 1 point
|
|
218
308
|
// That point is awarded if and only if both parts are fully correct, otherwise no points are awarded
|
|
219
|
-
resolve({ score: scoreA === 1 && scoreB === 1 ? 1 : 0, scoreA, scoreB, max: 1 });
|
|
309
|
+
resolve({ score: scoreA === 1 && scoreB === 1 ? 1 : 0, scoreA, scoreB, max: 1, logTrace });
|
|
220
310
|
} else {
|
|
221
311
|
// The EBSR item is worth 2 points
|
|
222
312
|
if (scoreA === 1) {
|
|
223
313
|
if (scoreB === 1) {
|
|
224
314
|
// If Part A and Part B are both correct, 2 points are awarded
|
|
225
|
-
resolve({ score: 2, scoreA, scoreB, max: 2 });
|
|
315
|
+
resolve({ score: 2, scoreA, scoreB, max: 2, logTrace });
|
|
226
316
|
} else {
|
|
227
317
|
// If Part A is correct and part B is incorrect, 1 point is awarded
|
|
228
|
-
resolve({ score: 1, scoreA, scoreB, max: 2 });
|
|
318
|
+
resolve({ score: 1, scoreA, scoreB, max: 2, logTrace });
|
|
229
319
|
}
|
|
230
320
|
} else {
|
|
231
321
|
// For all other combinations, no points are awarded
|
|
232
|
-
resolve({ score: 0, scoreA, scoreB, max: 2 });
|
|
322
|
+
resolve({ score: 0, scoreA, scoreB, max: 2, logTrace });
|
|
233
323
|
}
|
|
234
324
|
}
|
|
235
325
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-element/ebsr",
|
|
3
3
|
"repository": "pie-framework/pie-elements",
|
|
4
|
-
"version": "13.
|
|
4
|
+
"version": "13.2.0-next.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@pie-element/multiple-choice": "^12.
|
|
10
|
+
"@pie-element/multiple-choice": "^12.2.0-next.0",
|
|
11
11
|
"@pie-framework/pie-player-events": "^0.1.0",
|
|
12
12
|
"@pie-lib/translator": "3.2.0-next.3",
|
|
13
13
|
"classnames": "^2.2.5",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"author": "pie framework developers",
|
|
18
18
|
"license": "ISC",
|
|
19
|
-
"gitHead": "
|
|
19
|
+
"gitHead": "11169f258ac462817f622c5272b360009b6675ab",
|
|
20
20
|
"scripts": {
|
|
21
21
|
"postpublish": "../../scripts/postpublish"
|
|
22
22
|
},
|