@pie-element/hotspot 10.1.2-next.4 → 10.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 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/hotspot@10.1.2-next.4...@pie-element/hotspot@10.2.0-next.0) (2026-03-18)
7
+
8
+ ### Features
9
+
10
+ - **hotspot:** add trace log for scoring in outcome function PD-5439 ([5950d30](https://github.com/pie-framework/pie-elements/commit/5950d30444c2112720ba99d8b3f11c887a3a5e26))
11
+
6
12
  ## [10.1.2-next.4](https://github.com/pie-framework/pie-elements/compare/@pie-element/hotspot@10.1.2-next.3...@pie-element/hotspot@10.1.2-next.4) (2026-03-12)
7
13
 
8
14
  ### Bug Fixes
@@ -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
+ # [7.2.0-next.0](https://github.com/pie-framework/pie-elements/compare/@pie-element/hotspot-controller@7.1.1-next.1...@pie-element/hotspot-controller@7.2.0-next.0) (2026-03-18)
7
+
8
+ ### Features
9
+
10
+ - **hotspot:** add trace log for scoring in outcome function PD-5439 ([5950d30](https://github.com/pie-framework/pie-elements/commit/5950d30444c2112720ba99d8b3f11c887a3a5e26))
11
+
6
12
  ## [7.1.1-next.1](https://github.com/pie-framework/pie-elements/compare/@pie-element/hotspot-controller@7.1.0-next.1...@pie-element/hotspot-controller@7.1.1-next.1) (2026-02-26)
7
13
 
8
14
  **Note:** Version bump only for package @pie-element/hotspot-controller
@@ -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;
@@ -157,24 +157,108 @@ const getScore = (config, session, env = {}) => {
157
157
  const str = ((correctAnswers - extraAnswers) / total).toFixed(2);
158
158
  return str < 0 ? 0 : parseFloat(str);
159
159
  };
160
+
161
+ /**
162
+ * Generates detailed trace log for scoring evaluation
163
+ * @param {Object} model - the question model
164
+ * @param {Object} session - the student session
165
+ * @param {Object} env - the environment
166
+ * @returns {Array} traceLog - array of trace messages
167
+ */
168
+ const getLogTrace = (model, session, env) => {
169
+ const traceLog = [];
170
+ const {
171
+ answers
172
+ } = session || {};
173
+ const {
174
+ shapes
175
+ } = model || {};
176
+ const allShapes = [];
177
+ if (shapes) {
178
+ if (shapes.rectangles) allShapes.push(...shapes.rectangles);
179
+ if (shapes.polygons) allShapes.push(...shapes.polygons);
180
+ if (shapes.circles) allShapes.push(...shapes.circles);
181
+ }
182
+ const correctShapes = allShapes.filter(shape => shape.correct);
183
+ const totalShapes = allShapes.length;
184
+ traceLog.push(`Total of ${totalShapes} hotspot(s) defined, ${correctShapes.length} correct.`);
185
+ if (answers && answers.length > 0) {
186
+ traceLog.push(`Student selected ${answers.length} hotspot(s).`);
187
+ let correctSelections = 0;
188
+ let incorrectSelections = 0;
189
+ answers.forEach(answer => {
190
+ const shape = allShapes.find(s => s.id === answer.id);
191
+ if (shape && shape.correct) {
192
+ correctSelections++;
193
+ } else {
194
+ incorrectSelections++;
195
+ }
196
+ });
197
+ const missedCorrect = correctShapes.filter(correctShape => !answers.some(answer => answer.id === correctShape.id)).length;
198
+ if (correctSelections > 0) {
199
+ traceLog.push(`${correctSelections} correct hotspot(s) selected.`);
200
+ }
201
+ if (incorrectSelections > 0) {
202
+ traceLog.push(`${incorrectSelections} incorrect hotspot(s) selected.`);
203
+ }
204
+ if (missedCorrect > 0) {
205
+ traceLog.push(`${missedCorrect} correct hotspot(s) missed.`);
206
+ }
207
+ } else {
208
+ traceLog.push('No hotspots selected.');
209
+ }
210
+ const partialScoringEnabled = _controllerUtils.partialScoring.enabled(model, env);
211
+ if (partialScoringEnabled) {
212
+ traceLog.push(`Score calculated using partial scoring.`);
213
+ if (answers && answers.length > 0) {
214
+ let correctSelections = 0;
215
+ let incorrectSelections = 0;
216
+ answers.forEach(answer => {
217
+ const shape = allShapes.find(s => s.id === answer.id);
218
+ if (shape && shape.correct) {
219
+ correctSelections++;
220
+ } else {
221
+ incorrectSelections++;
222
+ }
223
+ });
224
+ const totalCorrectAvailable = correctShapes.length;
225
+ traceLog.push(`Partial scoring calculation: ${correctSelections} correct selections out of ${totalCorrectAvailable} available.`);
226
+ if (incorrectSelections > totalCorrectAvailable) {
227
+ const extraSelections = incorrectSelections - (totalCorrectAvailable - correctSelections);
228
+ traceLog.push(`${extraSelections} extra incorrect selection(s) beyond required amount are deducted from score.`);
229
+ }
230
+ }
231
+ } else {
232
+ traceLog.push(`Score calculated using all-or-nothing scoring.`);
233
+ }
234
+ const score = getScore(model, session, env);
235
+ traceLog.push(`Score: ${score}.`);
236
+ return traceLog;
237
+ };
238
+ exports.getLogTrace = getLogTrace;
160
239
  function outcome(config, session, env = {}) {
161
240
  return new Promise(resolve => {
162
241
  log('outcome...');
163
242
  if (!session || (0, _lodashEs.isEmpty)(session)) {
164
243
  resolve({
165
244
  score: 0,
166
- empty: true
245
+ empty: true,
246
+ traceLog: ['No hotspots selected. Score: 0.']
167
247
  });
168
248
  }
169
249
  if (session.answers) {
250
+ const traceLog = getLogTrace(config, session, env);
170
251
  const score = getScore(config, session, env);
171
252
  resolve({
172
- score
253
+ score,
254
+ empty: false,
255
+ traceLog
173
256
  });
174
257
  } else {
175
258
  resolve({
176
259
  score: 0,
177
- empty: true
260
+ empty: true,
261
+ traceLog: ['No hotspots selected. Score: 0.']
178
262
  });
179
263
  }
180
264
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_debug","_interopRequireDefault","require","_lodashEs","_controllerUtils","_utils","_defaults","log","debug","normalize","question","defaults","exports","model","session","env","normalizedQuestion","imageUrl","dimensions","hotspotColor","hoverOutlineColor","selectedHotspotColor","multipleCorrect","outlineColor","partialScoring","prompt","shapes","language","fontSizeFactor","autoplayAudioEnabled","completeAudioEnabled","customAudioButton","rectangles","polygons","circles","shouldIncludeCorrectResponse","mode","role","Promise","resolve","out","disabled","map","index","correct","rectProps","polyProps","circleProps","responseCorrect","isResponseCorrect","undefined","extraCSSRules","rationale","rationaleEnabled","teacherInstructions","teacherInstructionsEnabled","promptEnabled","strokeWidth","createDefaultModel","getScore","config","answers","partialScoringEnabled","enabled","correctAnswers","selectedChoices","choices","correctChoices","filter","choice","forEach","shape","selected","answer","id","correctlySelected","extraAnswers","length","total","str","toFixed","parseFloat","outcome","isEmpty","score","empty","returnShapesCorrect","i","push","createCorrectResponseSession","rectangleCorrect","polygonsCorrect","circlesCorrect","getInnerText","html","replaceAll","getContent","replace","validate","minShapes","maxShapes","maxSelections","errors","field","required","allShapes","Object","values","reduce","acc","nbOfSelections","nbOfShapes","selections"],"sources":["../src/index.js"],"sourcesContent":["import debug from 'debug';\nimport { isEmpty } from 'lodash-es';\nimport { partialScoring } from '@pie-lib/controller-utils';\n\nimport { isResponseCorrect } from './utils';\n\nimport defaults from './defaults';\n\nconst log = debug('pie-elements:hotspot:controller');\n\nexport const normalize = (question) => ({\n ...defaults,\n ...question,\n});\n\nexport function model(question, session, env) {\n const normalizedQuestion = normalize(question);\n const {\n imageUrl,\n dimensions,\n hotspotColor,\n hoverOutlineColor,\n selectedHotspotColor,\n multipleCorrect,\n outlineColor,\n partialScoring,\n prompt,\n shapes,\n language,\n fontSizeFactor,\n autoplayAudioEnabled,\n completeAudioEnabled,\n customAudioButton,\n } = normalizedQuestion;\n const { rectangles, polygons, circles } = shapes || {};\n\n const shouldIncludeCorrectResponse = env.mode === 'evaluate' || (env.role === 'instructor' && env.mode === 'view');\n\n return new Promise((resolve) => {\n const out = {\n disabled: env.mode !== 'gather',\n mode: env.mode,\n dimensions,\n imageUrl,\n outlineColor,\n hotspotColor,\n hoverOutlineColor,\n selectedHotspotColor,\n multipleCorrect,\n partialScoring,\n language,\n fontSizeFactor,\n autoplayAudioEnabled,\n completeAudioEnabled,\n customAudioButton,\n shapes: {\n ...shapes,\n // eslint-disable-next-line no-unused-vars\n rectangles: (rectangles || []).map(({ index, correct, ...rectProps }) =>\n shouldIncludeCorrectResponse ? { correct, ...rectProps } : { ...rectProps },\n ),\n // eslint-disable-next-line no-unused-vars\n polygons: (polygons || []).map(({ index, correct, ...polyProps }) =>\n shouldIncludeCorrectResponse ? { correct, ...polyProps } : { ...polyProps },\n ),\n // eslint-disable-next-line no-unused-vars\n circles: (circles || []).map(({ index, correct, ...circleProps }) =>\n shouldIncludeCorrectResponse ? { correct, ...circleProps } : { ...circleProps },\n ),\n },\n responseCorrect: env.mode === 'evaluate' ? isResponseCorrect(normalizedQuestion, session) : undefined,\n extraCSSRules: normalizedQuestion.extraCSSRules,\n };\n\n if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {\n out.rationale = normalizedQuestion.rationaleEnabled ? normalizedQuestion.rationale : null;\n out.teacherInstructions = normalizedQuestion.teacherInstructionsEnabled\n ? normalizedQuestion.teacherInstructions\n : null;\n } else {\n out.rationale = null;\n out.teacherInstructions = null;\n }\n\n out.prompt = normalizedQuestion.promptEnabled ? prompt : null;\n out.strokeWidth = normalizedQuestion.strokeWidth;\n\n resolve(out);\n });\n}\n\nexport const createDefaultModel = (model = {}) =>\n new Promise((resolve) => {\n resolve({\n ...defaults,\n ...model,\n });\n });\n\nconst getScore = (config, session, env = {}) => {\n const { answers } = session || {};\n\n if (!config.shapes || (!config.shapes.rectangles && !config.shapes.polygons && !config.shapes.circles)) {\n return 0;\n }\n\n const { shapes: { rectangles = [], polygons = [], circles = [] } = {} } = config;\n const partialScoringEnabled = partialScoring.enabled(config, env);\n\n if (!partialScoringEnabled) {\n return isResponseCorrect(config, session) ? 1 : 0;\n }\n\n let correctAnswers = 0;\n let selectedChoices = 0;\n\n const choices = [...rectangles, ...polygons, ...circles];\n\n const correctChoices = choices.filter((choice) => choice.correct);\n\n choices.forEach((shape) => {\n const selected = answers && answers.filter((answer) => answer.id === shape.id)[0];\n const correctlySelected = shape.correct && selected;\n\n if (selected) {\n selectedChoices += 1;\n }\n\n if (correctlySelected) {\n correctAnswers += 1;\n }\n });\n\n const extraAnswers = selectedChoices > correctChoices.length ? selectedChoices - correctChoices.length : 0;\n\n const total = correctChoices.length === 0 ? 1 : correctChoices.length;\n const str = ((correctAnswers - extraAnswers) / total).toFixed(2);\n\n return str < 0 ? 0 : parseFloat(str);\n};\n\nexport function outcome(config, session, env = {}) {\n return new Promise((resolve) => {\n log('outcome...');\n\n if (!session || isEmpty(session)) {\n resolve({ score: 0, empty: true });\n }\n\n if (session.answers) {\n const score = getScore(config, session, env);\n resolve({ score });\n } else {\n resolve({ score: 0, empty: true });\n }\n });\n}\n\nconst returnShapesCorrect = (shapes) => {\n let answers = [];\n\n shapes.forEach((i) => {\n const { correct, id } = i;\n if (correct) {\n answers.push({ id });\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 { shapes: { rectangles = [], circles = [], polygons = {} } = {} } = question;\n\n const rectangleCorrect = returnShapesCorrect(rectangles);\n const polygonsCorrect = returnShapesCorrect(polygons);\n const circlesCorrect = returnShapesCorrect(circles);\n\n resolve({\n answers: [...rectangleCorrect, ...polygonsCorrect, ...circlesCorrect],\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\nexport const validate = (model = {}, config = {}) => {\n const { shapes } = model;\n const { minShapes = 2, maxShapes, maxSelections } = config;\n const errors = {};\n\n ['teacherInstructions', 'prompt', 'rationale'].forEach((field) => {\n if (config[field]?.required && !getContent(model[field])) {\n errors[field] = 'This field is required.';\n }\n });\n\n const allShapes = Object.values(shapes || {}).reduce((acc, shape) => [...acc, ...shape], []);\n\n const nbOfSelections = (allShapes || []).reduce((acc, shape) => (shape.correct ? acc + 1 : acc), 0);\n\n const nbOfShapes = (allShapes || []).length;\n\n if (nbOfShapes < minShapes) {\n errors.shapes = `There should be at least ${minShapes} shapes defined.`;\n } else if (nbOfShapes > maxShapes) {\n errors.shapes = `No more than ${maxShapes} shapes should be defined.`;\n }\n\n if (nbOfSelections < 1) {\n errors.selections = 'There should be at least 1 shape selected.';\n } else if (nbOfSelections > maxSelections) {\n errors.selections = `No more than ${maxSelections} shapes should be selected.`;\n }\n\n return errors;\n};\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["_debug","_interopRequireDefault","require","_lodashEs","_controllerUtils","_utils","_defaults","log","debug","normalize","question","defaults","exports","model","session","env","normalizedQuestion","imageUrl","dimensions","hotspotColor","hoverOutlineColor","selectedHotspotColor","multipleCorrect","outlineColor","partialScoring","prompt","shapes","language","fontSizeFactor","autoplayAudioEnabled","completeAudioEnabled","customAudioButton","rectangles","polygons","circles","shouldIncludeCorrectResponse","mode","role","Promise","resolve","out","disabled","map","index","correct","rectProps","polyProps","circleProps","responseCorrect","isResponseCorrect","undefined","extraCSSRules","rationale","rationaleEnabled","teacherInstructions","teacherInstructionsEnabled","promptEnabled","strokeWidth","createDefaultModel","getScore","config","answers","partialScoringEnabled","enabled","correctAnswers","selectedChoices","choices","correctChoices","filter","choice","forEach","shape","selected","answer","id","correctlySelected","extraAnswers","length","total","str","toFixed","parseFloat","getLogTrace","traceLog","allShapes","push","correctShapes","totalShapes","correctSelections","incorrectSelections","find","s","missedCorrect","correctShape","some","totalCorrectAvailable","extraSelections","score","outcome","isEmpty","empty","returnShapesCorrect","i","createCorrectResponseSession","rectangleCorrect","polygonsCorrect","circlesCorrect","getInnerText","html","replaceAll","getContent","replace","validate","minShapes","maxShapes","maxSelections","errors","field","required","Object","values","reduce","acc","nbOfSelections","nbOfShapes","selections"],"sources":["../src/index.js"],"sourcesContent":["import debug from 'debug';\nimport { isEmpty } from 'lodash-es';\nimport { partialScoring } from '@pie-lib/controller-utils';\n\nimport { isResponseCorrect } from './utils';\n\nimport defaults from './defaults';\n\nconst log = debug('pie-elements:hotspot:controller');\n\nexport const normalize = (question) => ({\n ...defaults,\n ...question,\n});\n\nexport function model(question, session, env) {\n const normalizedQuestion = normalize(question);\n const {\n imageUrl,\n dimensions,\n hotspotColor,\n hoverOutlineColor,\n selectedHotspotColor,\n multipleCorrect,\n outlineColor,\n partialScoring,\n prompt,\n shapes,\n language,\n fontSizeFactor,\n autoplayAudioEnabled,\n completeAudioEnabled,\n customAudioButton,\n } = normalizedQuestion;\n const { rectangles, polygons, circles } = shapes || {};\n\n const shouldIncludeCorrectResponse = env.mode === 'evaluate' || (env.role === 'instructor' && env.mode === 'view');\n\n return new Promise((resolve) => {\n const out = {\n disabled: env.mode !== 'gather',\n mode: env.mode,\n dimensions,\n imageUrl,\n outlineColor,\n hotspotColor,\n hoverOutlineColor,\n selectedHotspotColor,\n multipleCorrect,\n partialScoring,\n language,\n fontSizeFactor,\n autoplayAudioEnabled,\n completeAudioEnabled,\n customAudioButton,\n shapes: {\n ...shapes,\n // eslint-disable-next-line no-unused-vars\n rectangles: (rectangles || []).map(({ index, correct, ...rectProps }) =>\n shouldIncludeCorrectResponse ? { correct, ...rectProps } : { ...rectProps },\n ),\n // eslint-disable-next-line no-unused-vars\n polygons: (polygons || []).map(({ index, correct, ...polyProps }) =>\n shouldIncludeCorrectResponse ? { correct, ...polyProps } : { ...polyProps },\n ),\n // eslint-disable-next-line no-unused-vars\n circles: (circles || []).map(({ index, correct, ...circleProps }) =>\n shouldIncludeCorrectResponse ? { correct, ...circleProps } : { ...circleProps },\n ),\n },\n responseCorrect: env.mode === 'evaluate' ? isResponseCorrect(normalizedQuestion, session) : undefined,\n extraCSSRules: normalizedQuestion.extraCSSRules,\n };\n\n if (env.role === 'instructor' && (env.mode === 'view' || env.mode === 'evaluate')) {\n out.rationale = normalizedQuestion.rationaleEnabled ? normalizedQuestion.rationale : null;\n out.teacherInstructions = normalizedQuestion.teacherInstructionsEnabled\n ? normalizedQuestion.teacherInstructions\n : null;\n } else {\n out.rationale = null;\n out.teacherInstructions = null;\n }\n\n out.prompt = normalizedQuestion.promptEnabled ? prompt : null;\n out.strokeWidth = normalizedQuestion.strokeWidth;\n\n resolve(out);\n });\n}\n\nexport const createDefaultModel = (model = {}) =>\n new Promise((resolve) => {\n resolve({\n ...defaults,\n ...model,\n });\n });\n\nconst getScore = (config, session, env = {}) => {\n const { answers } = session || {};\n\n if (!config.shapes || (!config.shapes.rectangles && !config.shapes.polygons && !config.shapes.circles)) {\n return 0;\n }\n\n const { shapes: { rectangles = [], polygons = [], circles = [] } = {} } = config;\n const partialScoringEnabled = partialScoring.enabled(config, env);\n\n if (!partialScoringEnabled) {\n return isResponseCorrect(config, session) ? 1 : 0;\n }\n\n let correctAnswers = 0;\n let selectedChoices = 0;\n\n const choices = [...rectangles, ...polygons, ...circles];\n\n const correctChoices = choices.filter((choice) => choice.correct);\n\n choices.forEach((shape) => {\n const selected = answers && answers.filter((answer) => answer.id === shape.id)[0];\n const correctlySelected = shape.correct && selected;\n\n if (selected) {\n selectedChoices += 1;\n }\n\n if (correctlySelected) {\n correctAnswers += 1;\n }\n });\n\n const extraAnswers = selectedChoices > correctChoices.length ? selectedChoices - correctChoices.length : 0;\n\n const total = correctChoices.length === 0 ? 1 : correctChoices.length;\n const str = ((correctAnswers - extraAnswers) / total).toFixed(2);\n\n return str < 0 ? 0 : parseFloat(str);\n};\n\n /**\n * Generates detailed trace log for scoring evaluation\n * @param {Object} model - the question model\n * @param {Object} session - the student session\n * @param {Object} env - the environment\n * @returns {Array} traceLog - array of trace messages\n */\nexport const getLogTrace = (model, session, env) => {\n const traceLog = [];\n const { answers } = session || {};\n const { shapes } = model || {};\n \n const allShapes = [];\n if (shapes) {\n if (shapes.rectangles) allShapes.push(...shapes.rectangles);\n if (shapes.polygons) allShapes.push(...shapes.polygons);\n if (shapes.circles) allShapes.push(...shapes.circles);\n }\n \n const correctShapes = allShapes.filter(shape => shape.correct);\n const totalShapes = allShapes.length;\n \n traceLog.push(`Total of ${totalShapes} hotspot(s) defined, ${correctShapes.length} correct.`);\n \n if (answers && answers.length > 0) {\n traceLog.push(`Student selected ${answers.length} hotspot(s).`);\n \n let correctSelections = 0;\n let incorrectSelections = 0;\n \n answers.forEach(answer => {\n const shape = allShapes.find(s => s.id === answer.id);\n if (shape && shape.correct) {\n correctSelections++;\n } else {\n incorrectSelections++;\n }\n });\n \n const missedCorrect = correctShapes.filter(correctShape => \n !answers.some(answer => answer.id === correctShape.id)\n ).length;\n \n if (correctSelections > 0) {\n traceLog.push(`${correctSelections} correct hotspot(s) selected.`);\n }\n if (incorrectSelections > 0) {\n traceLog.push(`${incorrectSelections} incorrect hotspot(s) selected.`);\n }\n if (missedCorrect > 0) {\n traceLog.push(`${missedCorrect} correct hotspot(s) missed.`);\n }\n } else {\n traceLog.push('No hotspots selected.');\n }\n\n const partialScoringEnabled = partialScoring.enabled(model, env);\n \n if (partialScoringEnabled) {\n traceLog.push(`Score calculated using partial scoring.`);\n \n if (answers && answers.length > 0) {\n let correctSelections = 0;\n let incorrectSelections = 0;\n \n answers.forEach(answer => {\n const shape = allShapes.find(s => s.id === answer.id);\n if (shape && shape.correct) {\n correctSelections++;\n } else {\n incorrectSelections++;\n }\n });\n \n const totalCorrectAvailable = correctShapes.length;\n traceLog.push(`Partial scoring calculation: ${correctSelections} correct selections out of ${totalCorrectAvailable} available.`);\n \n if (incorrectSelections > totalCorrectAvailable) {\n const extraSelections = incorrectSelections - (totalCorrectAvailable - correctSelections);\n traceLog.push(`${extraSelections} extra incorrect selection(s) beyond required amount are deducted from score.`);\n }\n }\n } else {\n traceLog.push(`Score calculated using all-or-nothing scoring.`);\n }\n\n const score = getScore(model, session, env);\n traceLog.push(`Score: ${score}.`);\n\n return traceLog;\n}\n\nexport function outcome(config, session, env = {}) {\n return new Promise((resolve) => {\n log('outcome...');\n\n if (!session || isEmpty(session)) {\n resolve({ \n score: 0, \n empty: true, \n traceLog: ['No hotspots selected. Score: 0.'] \n });\n } \n\n if (session.answers) {\n const traceLog = getLogTrace(config, session, env);\n const score = getScore(config, session, env);\n\n resolve({ score, empty: false, traceLog });\n } else {\n resolve({ \n score: 0, \n empty: true, \n traceLog: ['No hotspots selected. Score: 0.'] \n });\n }\n });\n}\n\nconst returnShapesCorrect = (shapes) => {\n let answers = [];\n\n shapes.forEach((i) => {\n const { correct, id } = i;\n if (correct) {\n answers.push({ id });\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 { shapes: { rectangles = [], circles = [], polygons = {} } = {} } = question;\n\n const rectangleCorrect = returnShapesCorrect(rectangles);\n const polygonsCorrect = returnShapesCorrect(polygons);\n const circlesCorrect = returnShapesCorrect(circles);\n\n resolve({\n answers: [...rectangleCorrect, ...polygonsCorrect, ...circlesCorrect],\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\nexport const validate = (model = {}, config = {}) => {\n const { shapes } = model;\n const { minShapes = 2, maxShapes, maxSelections } = config;\n const errors = {};\n\n ['teacherInstructions', 'prompt', 'rationale'].forEach((field) => {\n if (config[field]?.required && !getContent(model[field])) {\n errors[field] = 'This field is required.';\n }\n });\n\n const allShapes = Object.values(shapes || {}).reduce((acc, shape) => [...acc, ...shape], []);\n\n const nbOfSelections = (allShapes || []).reduce((acc, shape) => (shape.correct ? acc + 1 : acc), 0);\n\n const nbOfShapes = (allShapes || []).length;\n\n if (nbOfShapes < minShapes) {\n errors.shapes = `There should be at least ${minShapes} shapes defined.`;\n } else if (nbOfShapes > maxShapes) {\n errors.shapes = `No more than ${maxShapes} shapes should be defined.`;\n }\n\n if (nbOfSelections < 1) {\n errors.selections = 'There should be at least 1 shape selected.';\n } else if (nbOfSelections > maxSelections) {\n errors.selections = `No more than ${maxSelections} shapes should be selected.`;\n }\n\n return errors;\n};\n"],"mappings":"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","ignoreList":[]}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pie-element/hotspot-controller",
3
3
  "private": true,
4
- "version": "7.1.1-next.1",
4
+ "version": "7.2.0-next.0",
5
5
  "description": "",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -50,12 +50,13 @@ describe('controller', () => {
50
50
  describe('hanging controller', () => {
51
51
  it('doesnt hang if answers is missing', async () => {
52
52
  const response = await outcome({}, { id: '1' }, {});
53
- expect(response).toEqual({ score: 0, empty: true });
53
+ expect(response).toEqual({ score: 0, empty: true, traceLog: ['No hotspots selected. Score: 0.'] });
54
54
  });
55
55
 
56
56
  it('doesnt hang if the rest is missing', async () => {
57
57
  const response = await outcome({}, { id: '1', answers: {} }, {});
58
- expect(response).toEqual({ score: 0 });
58
+ const score = response.score;
59
+ expect(score).toEqual(0);
59
60
  });
60
61
  });
61
62
 
@@ -250,7 +251,7 @@ describe('controller', () => {
250
251
  const returnOutcome = (session) => {
251
252
  it(`returns empty: true when session is ${JSON.stringify(session)}`, async () => {
252
253
  const result = await outcome(question, session);
253
- expect(result).toEqual({ score: 0, empty: true });
254
+ expect(result).toEqual({ score: 0, empty: true, traceLog: ['No hotspots selected. Score: 0.'] });
254
255
  });
255
256
  };
256
257
 
@@ -139,19 +139,121 @@ const getScore = (config, session, env = {}) => {
139
139
  return str < 0 ? 0 : parseFloat(str);
140
140
  };
141
141
 
142
+ /**
143
+ * Generates detailed trace log for scoring evaluation
144
+ * @param {Object} model - the question model
145
+ * @param {Object} session - the student session
146
+ * @param {Object} env - the environment
147
+ * @returns {Array} traceLog - array of trace messages
148
+ */
149
+ export const getLogTrace = (model, session, env) => {
150
+ const traceLog = [];
151
+ const { answers } = session || {};
152
+ const { shapes } = model || {};
153
+
154
+ const allShapes = [];
155
+ if (shapes) {
156
+ if (shapes.rectangles) allShapes.push(...shapes.rectangles);
157
+ if (shapes.polygons) allShapes.push(...shapes.polygons);
158
+ if (shapes.circles) allShapes.push(...shapes.circles);
159
+ }
160
+
161
+ const correctShapes = allShapes.filter(shape => shape.correct);
162
+ const totalShapes = allShapes.length;
163
+
164
+ traceLog.push(`Total of ${totalShapes} hotspot(s) defined, ${correctShapes.length} correct.`);
165
+
166
+ if (answers && answers.length > 0) {
167
+ traceLog.push(`Student selected ${answers.length} hotspot(s).`);
168
+
169
+ let correctSelections = 0;
170
+ let incorrectSelections = 0;
171
+
172
+ answers.forEach(answer => {
173
+ const shape = allShapes.find(s => s.id === answer.id);
174
+ if (shape && shape.correct) {
175
+ correctSelections++;
176
+ } else {
177
+ incorrectSelections++;
178
+ }
179
+ });
180
+
181
+ const missedCorrect = correctShapes.filter(correctShape =>
182
+ !answers.some(answer => answer.id === correctShape.id)
183
+ ).length;
184
+
185
+ if (correctSelections > 0) {
186
+ traceLog.push(`${correctSelections} correct hotspot(s) selected.`);
187
+ }
188
+ if (incorrectSelections > 0) {
189
+ traceLog.push(`${incorrectSelections} incorrect hotspot(s) selected.`);
190
+ }
191
+ if (missedCorrect > 0) {
192
+ traceLog.push(`${missedCorrect} correct hotspot(s) missed.`);
193
+ }
194
+ } else {
195
+ traceLog.push('No hotspots selected.');
196
+ }
197
+
198
+ const partialScoringEnabled = partialScoring.enabled(model, env);
199
+
200
+ if (partialScoringEnabled) {
201
+ traceLog.push(`Score calculated using partial scoring.`);
202
+
203
+ if (answers && answers.length > 0) {
204
+ let correctSelections = 0;
205
+ let incorrectSelections = 0;
206
+
207
+ answers.forEach(answer => {
208
+ const shape = allShapes.find(s => s.id === answer.id);
209
+ if (shape && shape.correct) {
210
+ correctSelections++;
211
+ } else {
212
+ incorrectSelections++;
213
+ }
214
+ });
215
+
216
+ const totalCorrectAvailable = correctShapes.length;
217
+ traceLog.push(`Partial scoring calculation: ${correctSelections} correct selections out of ${totalCorrectAvailable} available.`);
218
+
219
+ if (incorrectSelections > totalCorrectAvailable) {
220
+ const extraSelections = incorrectSelections - (totalCorrectAvailable - correctSelections);
221
+ traceLog.push(`${extraSelections} extra incorrect selection(s) beyond required amount are deducted from score.`);
222
+ }
223
+ }
224
+ } else {
225
+ traceLog.push(`Score calculated using all-or-nothing scoring.`);
226
+ }
227
+
228
+ const score = getScore(model, session, env);
229
+ traceLog.push(`Score: ${score}.`);
230
+
231
+ return traceLog;
232
+ }
233
+
142
234
  export function outcome(config, session, env = {}) {
143
235
  return new Promise((resolve) => {
144
236
  log('outcome...');
145
237
 
146
238
  if (!session || isEmpty(session)) {
147
- resolve({ score: 0, empty: true });
148
- }
239
+ resolve({
240
+ score: 0,
241
+ empty: true,
242
+ traceLog: ['No hotspots selected. Score: 0.']
243
+ });
244
+ }
149
245
 
150
246
  if (session.answers) {
247
+ const traceLog = getLogTrace(config, session, env);
151
248
  const score = getScore(config, session, env);
152
- resolve({ score });
249
+
250
+ resolve({ score, empty: false, traceLog });
153
251
  } else {
154
- resolve({ score: 0, empty: true });
252
+ resolve({
253
+ score: 0,
254
+ empty: true,
255
+ traceLog: ['No hotspots selected. Score: 0.']
256
+ });
155
257
  }
156
258
  });
157
259
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pie-element/hotspot",
3
3
  "repository": "pie-framework/pie-elements",
4
- "version": "10.1.2-next.4",
4
+ "version": "10.2.0-next.0",
5
5
  "description": "",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "author": "pie framework developers",
26
26
  "license": "ISC",
27
- "gitHead": "40a7ed35e8d7ede93ebb571fff94e0539294403f",
27
+ "gitHead": "f1b143928e39d7528afbeaec56bb0a0c2295cd4f",
28
28
  "scripts": {
29
29
  "postpublish": "../../scripts/postpublish"
30
30
  },