DocDoeAI / src /lib /teaching /lessonObjectives.ts
asnannp's picture
deploy: sync backend to Space root (learn-lesson HF cache fix)
65d68f5
Raw History Blame Contribute Delete
3.19 kB
import type { TuitionLessonSeed } from "./physicsLessonSeeds";
/**
* A deterministic objective map for a curated lesson seed.
*
* The content author supplies the goal, explanation blocks, formulas, and
* board-answer prompts. This function only gives those authored parts stable
* identifiers so evidence can say exactly what it assessed. It does not ask an
* AI to infer progress or invent a syllabus objective.
*/
export type LessonObjective = {
id: string;
statement: string;
kind: "concept" | "formula" | "application" | "board_answer";
};
function stableObjectiveId(seed: TuitionLessonSeed, index: number): string {
return `${seed.chapterId}:${seed.missionId}:OBJ${index + 1}`;
}
function explainableStatement(value: string): string {
const trimmed = value.trim().replace(/[.]+$/, "");
if (!trimmed) return "Explain the source-backed lesson idea.";
if (/^(explain|define|describe|compare|calculate|derive|write|identify|use|solve)\b/i.test(trimmed)) {
return `${trimmed}.`;
}
if (/^master\b/i.test(trimmed)) return `${trimmed.replace(/^master\b/i, "Explain")}.`;
if (/^understand\b/i.test(trimmed)) return `${trimmed.replace(/^understand\b/i, "Explain")}.`;
return `Explain ${trimmed}.`;
}
function missionIdea(seed: TuitionLessonSeed): string {
return seed.missionTitle.replace(/^M\d+\s*:\s*/i, "").trim() || seed.chapterTitle;
}
function isGenericBlockHeading(value: string): boolean {
return /^(understanding|exam focus|remember|quick recap|key points?|in brief)\b/i.test(value.trim());
}
export function objectivesForLessonSeed(seed: TuitionLessonSeed): LessonObjective[] {
const statements: Array<Omit<LessonObjective, "id">> = [
{ kind: "concept", statement: explainableStatement(seed.goal) },
{ kind: "concept", statement: `Explain the key idea in ${missionIdea(seed)}.` },
];
for (const block of seed.teach.explanationBlocks.slice(0, 3)) {
if (block.heading.trim() && !isGenericBlockHeading(block.heading)) {
statements.push({ kind: "concept", statement: explainableStatement(block.heading) });
}
}
for (const formula of seed.formulas.slice(0, 2)) {
if (formula.formula.trim()) {
statements.push({
kind: "formula",
statement: `Use ${formula.formula.trim()} with the correct symbols and units.`,
});
}
}
if (seed.contentType === "numerical" || seed.quiz.some((question) => question.type === "numerical")) {
statements.push({
kind: "application",
statement: "Solve a similar application question with the method and final unit shown.",
});
}
const boardAnswer = seed.examAnswers[0];
if (boardAnswer) {
statements.push({
kind: "board_answer",
statement: `Write a concise ${boardAnswer.marks}-mark board answer using the required points.`,
});
}
const unique = new Map<string, Omit<LessonObjective, "id">>();
for (const objective of statements) {
const key = objective.statement.toLocaleLowerCase("en-IN");
if (!unique.has(key)) unique.set(key, objective);
}
return [...unique.values()].slice(0, 6).map((objective, index) => ({
...objective,
id: stableObjectiveId(seed, index),
}));
}