diff --git a/packages/math-inline/controller/src/__tests__/index.test.jsx b/packages/math-inline/controller/src/__tests__/index.test.jsx index a5480cd328..9fd85d53c4 100644 --- a/packages/math-inline/controller/src/__tests__/index.test.jsx +++ b/packages/math-inline/controller/src/__tests__/index.test.jsx @@ -396,7 +396,7 @@ describe('outcome', () => { it(`returns score: 0 and empty: true if session is ${JSON.stringify(session)}`, async () => { let outcomeResult = await outcome(question, session, env); - expect(outcomeResult).toEqual({ score: 0, empty: true }); + expect(outcomeResult).toEqual({ score: 0, empty: true, logTrace: ['Student did not enter a response.'] }); }); }; @@ -656,7 +656,7 @@ describe('6456 - outcome', () => { latexEqual.mockReturnValueOnce(false); const env = { mode: 'evaluate' }; const result = await outcome(question, session, env); - expect(result).toEqual({ score: 0 }); + expect(result.score).toEqual(0); }); it('scores 1', async () => { @@ -668,7 +668,7 @@ describe('6456 - outcome', () => { const env = { mode: 'evaluate' }; const result = await outcome(question, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); }); @@ -701,7 +701,7 @@ describe('6371', () => { const env = { mode: 'evaluate' }; const result = await outcome(question, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); }); @@ -734,7 +734,7 @@ describe('3826', () => { const env = { mode: 'evaluate' }; const result = await outcome(question, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); }); @@ -790,7 +790,7 @@ describe('PD-66', () => { const env = { mode: 'evaluate' }; const result = await outcome(question, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); }); @@ -865,7 +865,7 @@ describe('PD-205', () => { const env = { mode: 'evaluate' }; const result = await outcome(question, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); }); @@ -902,7 +902,7 @@ describe('PD-610', () => { const env = { mode: 'evaluate' }; const result = await outcome(equation_01, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); it('scores 0', async () => { @@ -915,7 +915,7 @@ describe('PD-610', () => { const env = { mode: 'evaluate' }; const result = await outcome(equation_01, session, env); - expect(result).toEqual({ score: 0 }); + expect(result.score).toEqual(0); }); const equation_02 = { @@ -950,7 +950,7 @@ describe('PD-610', () => { const env = { mode: 'evaluate' }; const result = await outcome(equation_02, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); it('scores 0', async () => { @@ -963,7 +963,7 @@ describe('PD-610', () => { const env = { mode: 'evaluate' }; const result = await outcome(equation_02, session, env); - expect(result).toEqual({ score: 0 }); + expect(result.score).toEqual(0); }); const equation_03 = { @@ -998,7 +998,7 @@ describe('PD-610', () => { const env = { mode: 'evaluate' }; const result = await outcome(equation_03, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); it('scores 0', async () => { @@ -1011,7 +1011,7 @@ describe('PD-610', () => { const env = { mode: 'evaluate' }; const result = await outcome(equation_03, session, env); - expect(result).toEqual({ score: 0 }); + expect(result.score).toEqual(0); }); }); @@ -1048,7 +1048,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_01, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); const expression_02 = { @@ -1083,7 +1083,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_02, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); const expression_03 = { @@ -1118,7 +1118,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_03, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); const expression_04 = { @@ -1153,7 +1153,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_04, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); const expression_05 = { @@ -1188,7 +1188,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_05, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); it('scores 1', async () => { @@ -1200,7 +1200,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_05, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); it('scores 1', async () => { @@ -1212,7 +1212,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_05, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); const expression_06 = { @@ -1247,7 +1247,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_06, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); it('scores 1', async () => { @@ -1259,7 +1259,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_06, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); it('scores 1', async () => { @@ -1271,7 +1271,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_06, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); const expression_07 = { @@ -1306,7 +1306,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_07, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); const expression_08 = { @@ -1341,7 +1341,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(expression_08, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); const equation_01 = { @@ -1376,7 +1376,7 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(equation_01, session, env); - expect(result).toEqual({ score: 1 }); + expect(result.score).toEqual(1); }); it('scores 0', async () => { @@ -1389,6 +1389,6 @@ describe('PD-1031', () => { const env = { mode: 'evaluate' }; const result = await outcome(equation_01, session, env); - expect(result).toEqual({ score: 0 }); + expect(result.score).toEqual(0); }); }); diff --git a/packages/math-inline/controller/src/index.js b/packages/math-inline/controller/src/index.js index 27524c51f1..411a62b804 100644 --- a/packages/math-inline/controller/src/index.js +++ b/packages/math-inline/controller/src/index.js @@ -103,17 +103,86 @@ export function createDefaultModel(model = {}) { }); } +/** + * Generates detailed trace log for math-inline scoring evaluation + * @param {Object} question + * @param {Object} session + * @param {Object} env + * @returns {Array} traceLog + */ +export const getLogTrace = (question, session, env) => { + const traceLog = []; + + if (!session || (!session.response && !session.completeAnswer)) { + traceLog.push('Student did not enter a response.'); + return traceLog; + } + + const responseType = question?.responseType; + const isAdvanced = responseType === ResponseTypes.advanced; + const studentAnswer = isAdvanced ? session.completeAnswer : session.response; + + const cleanStudentAnswer = studentAnswer ? studentAnswer.replace(/\\/g, '') : ''; + + if (!cleanStudentAnswer) { + traceLog.push('Student entered a response, but then it was removed.'); + return traceLog; + } + + traceLog.push(`Student entered response: ${cleanStudentAnswer}.`); + traceLog.push(`Response type: ${isAdvanced ? 'advanced' : 'simple'}.`); + + const responses = question.responses || []; + const responsesToCheck = isAdvanced ? responses : responses.slice(0, 1); + + responsesToCheck.forEach((response, index) => { + const validation = response.validation || question.validationDefault; + traceLog.push(`Response ${index + 1} validation mode: ${validation}.`); + + if (validation === 'literal') { + const allowTrailingZeros = response.allowTrailingZeros || false; + const ignoreOrder = response.ignoreOrder || false; + + traceLog.push(`Allow trailing zeros: ${allowTrailingZeros ? 'enabled' : 'disabled'}.`); + traceLog.push(`Ignore order: ${ignoreOrder ? 'enabled' : 'disabled'}.`); + } + + const acceptedValues = [response.answer].concat( + Object.keys(response.alternates || {}).map((alternateId) => response.alternates[alternateId]) + ); + + if (acceptedValues.length > 1) { + traceLog.push(`Accepted answers: ${acceptedValues.length} (including alternates).`); + } + }); + + const correctness = getCorrectness(question, env, session, true); + + if (correctness.correctness === 'correct') { + traceLog.push('Student answer matches one of the accepted responses.'); + } else if (correctness.correctness === 'incorrect') { + traceLog.push('Student answer does not match any of the accepted responses.'); + } else { + traceLog.push('Student answer could not be evaluated.'); + } + + traceLog.push(`Response evaluated as ${correctness.correctness}.`); + traceLog.push(`Final score: ${correctness.score}.`); + + return traceLog; +}; + export const outcome = (question, session, env) => { return new Promise((resolve) => { if (env.mode !== 'evaluate') { - resolve({ score: undefined, completed: undefined }); + resolve({ score: undefined, completed: undefined, logTrace: [] }); } else { if (!session || isEmpty(session)) { - resolve({ score: 0, empty: true }); + resolve({ score: 0, empty: true, logTrace: ['Student did not enter a response.'] }); } else { const correctness = getCorrectness(question, env, session, true); - resolve({ score: correctness.score }); + resolve({ score: correctness.score, logTrace: getLogTrace(question, session, env) }); } } });