feat(evaluasi): feedback kegagalan output & key_text (poin 4)
Tambah missingKeyTextKeys(), reason evaluasi, dan pesan siswa saat output mismatch atau key_text belum dipakai.
This commit is contained in:
parent
c911908316
commit
72d30351e5
|
|
@ -1,4 +1,4 @@
|
|||
import { checkKeyText, validateNodes } from '$services/exercise';
|
||||
import { checkKeyText, missingKeyTextKeys, validateNodes } from '$services/exercise';
|
||||
import type { LessonContent } from '$types/lesson';
|
||||
import type { CircuitJSApi } from '$types/circuitjs';
|
||||
|
||||
|
|
@ -52,6 +52,12 @@ export function evaluateCircuitSubmission(
|
|||
return { pass: allPassed, output: finalOutput };
|
||||
}
|
||||
|
||||
export interface LanguageEvalResult {
|
||||
isCorrect: boolean;
|
||||
reason?: 'output-mismatch' | 'keytext-missing';
|
||||
missingKeys?: string[];
|
||||
}
|
||||
|
||||
export function processLanguageEvaluation(
|
||||
compileOutput: string,
|
||||
code: string,
|
||||
|
|
@ -60,7 +66,7 @@ export function processLanguageEvaluation(
|
|||
cCode: string,
|
||||
pythonCode: string,
|
||||
data: LessonContent
|
||||
): { isCorrect: boolean } {
|
||||
): LanguageEvalResult {
|
||||
if (!data.expected_output && !data.expected_output_python) return { isCorrect: true };
|
||||
|
||||
const currentCCode = (currentLanguage === 'c') ? code : cCode;
|
||||
|
|
@ -71,6 +77,15 @@ export function processLanguageEvaluation(
|
|||
? data.expected_output_python
|
||||
: data.expected_output;
|
||||
|
||||
const isCorrect = compileOutput.trim() === expected.trim() && checkKeyText(mergedCode, data.key_text ?? '');
|
||||
return { isCorrect };
|
||||
const outputMatches = compileOutput.trim() === (expected ?? '').trim();
|
||||
if (!outputMatches) {
|
||||
return { isCorrect: false, reason: 'output-mismatch' };
|
||||
}
|
||||
|
||||
const missingKeys = missingKeyTextKeys(mergedCode, data.key_text ?? '');
|
||||
if (missingKeys.length > 0) {
|
||||
return { isCorrect: false, reason: 'keytext-missing', missingKeys };
|
||||
}
|
||||
|
||||
return { isCorrect: true };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { checkKeyText, missingKeyTextKeys } from './exercise';
|
||||
import { processLanguageEvaluation } from './evaluators';
|
||||
import type { LessonContent } from '$types/lesson';
|
||||
|
||||
describe('missingKeyTextKeys', () => {
|
||||
it('mengembalikan key yang hilang (bukan hanya boolean)', () => {
|
||||
expect(missingKeyTextKeys('int main() {}', 'int\nfloat\n%.2lf')).toEqual(['float', '%.2lf']);
|
||||
});
|
||||
|
||||
it('kompatibel dengan checkKeyText', () => {
|
||||
expect(checkKeyText('int x; float y;', 'int\nfloat')).toBe(true);
|
||||
expect(checkKeyText('int x;', 'int\nfloat')).toBe(false);
|
||||
});
|
||||
|
||||
it('key_text kosong tidak menghasilkan key hilang', () => {
|
||||
expect(missingKeyTextKeys('apa saja', ' \n')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('processLanguageEvaluation feedback', () => {
|
||||
const data = {
|
||||
expected_output: 'Halo',
|
||||
key_text: 'printf\n%.2f',
|
||||
} as unknown as LessonContent;
|
||||
|
||||
it('output beda → reason output-mismatch', () => {
|
||||
const result = processLanguageEvaluation('Halo dunia', 'printf("%.2f", 1);', 'c', 'c', '', '', data);
|
||||
expect(result.isCorrect).toBe(false);
|
||||
expect(result.reason).toBe('output-mismatch');
|
||||
});
|
||||
|
||||
it('output sama tapi key_text kurang → reason keytext-missing + daftar key', () => {
|
||||
const result = processLanguageEvaluation('Halo', 'printf("Halo");', 'c', 'c', '', '', data);
|
||||
expect(result.isCorrect).toBe(false);
|
||||
expect(result.reason).toBe('keytext-missing');
|
||||
expect(result.missingKeys).toEqual(['%.2f']);
|
||||
});
|
||||
|
||||
it('output sama dan key lengkap → correct tanpa reason', () => {
|
||||
const result = processLanguageEvaluation('Halo', 'printf("%.2f", 1);', 'c', 'c', '', '', data);
|
||||
expect(result).toEqual({ isCorrect: true });
|
||||
});
|
||||
});
|
||||
|
|
@ -4,11 +4,16 @@
|
|||
|
||||
import type { CircuitJSApi } from '$types/circuitjs';
|
||||
|
||||
/** Return key_text keywords (one per line) that are missing from the code. */
|
||||
export function missingKeyTextKeys(code: string, keyText: string): string[] {
|
||||
if (!keyText.trim()) return [];
|
||||
const keys = keyText.split('\n').map(k => k.trim()).filter(k => k.length > 0);
|
||||
return keys.filter(key => !code.includes(key));
|
||||
}
|
||||
|
||||
/** Check if code contains all required key_text keywords (one per line). */
|
||||
export function checkKeyText(code: string, keyText: string): boolean {
|
||||
if (!keyText.trim()) return true;
|
||||
const keys = keyText.split('\n').map(k => k.trim()).filter(k => k.length > 0);
|
||||
return keys.every(key => code.includes(key));
|
||||
return missingKeyTextKeys(code, keyText).length === 0;
|
||||
}
|
||||
|
||||
export interface NodeResult {
|
||||
|
|
|
|||
|
|
@ -865,8 +865,8 @@ export class LessonManager {
|
|||
out.output = res.output;
|
||||
out.success = true;
|
||||
if (this.data.expected_output || this.data.expected_output_python) {
|
||||
const { isCorrect } = processLanguageEvaluation(res.output, code, lang, this.currentLanguage, this.cCode, this.pythonCode, this.data);
|
||||
if (isCorrect) {
|
||||
const evaluation = processLanguageEvaluation(res.output, code, lang, this.currentLanguage, this.cCode, this.pythonCode, this.data);
|
||||
if (evaluation.isCorrect) {
|
||||
if (lang === 'c') this.cPassed = true;
|
||||
else if (lang === 'python') this.pythonPassed = true;
|
||||
if (!this.checkAllPassed()) {
|
||||
|
|
@ -883,6 +883,14 @@ export class LessonManager {
|
|||
}
|
||||
setTimeout(() => { this.showCelebration = false; this.activeTab = 'editor'; }, 3000);
|
||||
}
|
||||
} else {
|
||||
out.success = false;
|
||||
if (evaluation.reason === 'keytext-missing') {
|
||||
const missing = (evaluation.missingKeys ?? []).map(k => `\`${k.replace(/\n/g, '\\n')}\``).join(', ');
|
||||
out.output += `\n❌ Output sudah sesuai, tapi kode belum memakai konsep materi. Kata kunci wajib yang belum ada: ${missing}`;
|
||||
} else if (evaluation.reason === 'output-mismatch') {
|
||||
out.output += '\n❌ Output belum sesuai yang diharapkan — periksa format keluaran programmu.';
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue