adding feedback

This commit is contained in:
2025-10-10 14:15:01 -06:00
parent e7e244222e
commit bf835caa37
6 changed files with 488 additions and 16 deletions

View File

@@ -30,6 +30,9 @@ export interface LocalQuizQuestion {
points: number;
answers: LocalQuizQuestionAnswer[];
matchDistractors: string[];
correctComments?: string;
incorrectComments?: string;
neutralComments?: string;
}
export const zodLocalQuizQuestion = z.object({
text: z.string(),
@@ -37,4 +40,7 @@ export const zodLocalQuizQuestion = z.object({
points: z.number(),
answers: zodLocalQuizQuestionAnswer.array(),
matchDistractors: z.array(z.string()),
correctComments: z.string().optional(),
incorrectComments: z.string().optional(),
neutralComments: z.string().optional(),
});

View File

@@ -14,6 +14,98 @@ const _validFirstAnswerDelimiters = [
];
const _multipleChoicePrefix = ["a)", "*a)", "*)", ")"];
const _multipleAnswerPrefix = ["[ ]", "[*]", "[]"];
const _feedbackPrefixes = ["+", "-", "..."];
const extractFeedback = (
linesWithoutPoints: string[]
): {
correctComments?: string;
incorrectComments?: string;
neutralComments?: string;
linesWithoutFeedback: string[];
} => {
let correctComments: string | undefined;
let incorrectComments: string | undefined;
let neutralComments: string | undefined;
const linesWithoutFeedback: string[] = [];
let currentFeedbackType: "+" | "-" | "..." | null = null;
let currentFeedbackLines: string[] = [];
for (const line of linesWithoutPoints) {
const trimmed = line.trim();
// Check if this is a new feedback line
if (trimmed.startsWith("+ ") || trimmed === "+") {
// Save previous feedback if any
if (currentFeedbackType && currentFeedbackLines.length > 0) {
const feedbackText = currentFeedbackLines.join("\n");
if (currentFeedbackType === "+") correctComments = feedbackText;
else if (currentFeedbackType === "-") incorrectComments = feedbackText;
else if (currentFeedbackType === "...") neutralComments = feedbackText;
}
currentFeedbackType = "+";
currentFeedbackLines = trimmed === "+" ? [] : [trimmed.substring(2)]; // Remove "+ " or handle standalone "+"
} else if (trimmed.startsWith("- ") || trimmed === "-") {
// Save previous feedback if any
if (currentFeedbackType && currentFeedbackLines.length > 0) {
const feedbackText = currentFeedbackLines.join("\n");
if (currentFeedbackType === "+") correctComments = feedbackText;
else if (currentFeedbackType === "-") incorrectComments = feedbackText;
else if (currentFeedbackType === "...") neutralComments = feedbackText;
}
currentFeedbackType = "-";
currentFeedbackLines = trimmed === "-" ? [] : [trimmed.substring(2)]; // Remove "- " or handle standalone "-"
} else if (trimmed.startsWith("... ") || trimmed === "...") {
// Save previous feedback if any
if (currentFeedbackType && currentFeedbackLines.length > 0) {
const feedbackText = currentFeedbackLines.join("\n");
if (currentFeedbackType === "+") correctComments = feedbackText;
else if (currentFeedbackType === "-") incorrectComments = feedbackText;
else if (currentFeedbackType === "...") neutralComments = feedbackText;
}
currentFeedbackType = "...";
currentFeedbackLines = trimmed === "..." ? [] : [trimmed.substring(4)]; // Remove "... " or handle standalone "..."
} else if (
currentFeedbackType &&
!_validFirstAnswerDelimiters.some((prefix) => trimmed.startsWith(prefix))
) {
// This is a continuation of the current feedback
currentFeedbackLines.push(line);
} else {
// Save any pending feedback
if (currentFeedbackType && currentFeedbackLines.length > 0) {
const feedbackText = currentFeedbackLines.join("\n");
if (currentFeedbackType === "+") correctComments = feedbackText;
else if (currentFeedbackType === "-") incorrectComments = feedbackText;
else if (currentFeedbackType === "...") neutralComments = feedbackText;
currentFeedbackType = null;
currentFeedbackLines = [];
}
// This is a regular line
linesWithoutFeedback.push(line);
}
}
// Save any remaining feedback
if (currentFeedbackType && currentFeedbackLines.length > 0) {
const feedbackText = currentFeedbackLines.join("\n");
if (currentFeedbackType === "+") correctComments = feedbackText;
else if (currentFeedbackType === "-") incorrectComments = feedbackText;
else if (currentFeedbackType === "...") neutralComments = feedbackText;
}
return {
correctComments,
incorrectComments,
neutralComments,
linesWithoutFeedback,
};
};
const getAnswerStringsWithMultilineSupport = (
linesWithoutPoints: string[],
@@ -153,6 +245,18 @@ export const quizQuestionMarkdownUtils = {
? question.matchDistractors?.map((d) => `\n^ - ${d}`).join("") ?? ""
: "";
// Build feedback lines
let feedbackText = "";
if (question.correctComments) {
feedbackText += `+ ${question.correctComments}\n`;
}
if (question.incorrectComments) {
feedbackText += `- ${question.incorrectComments}\n`;
}
if (question.neutralComments) {
feedbackText += `... ${question.neutralComments}\n`;
}
const answersText = answerArray.join("\n");
const questionTypeIndicator =
question.questionType === "essay" ||
@@ -162,7 +266,7 @@ export const quizQuestionMarkdownUtils = {
? `\n${QuestionType.SHORT_ANSWER_WITH_ANSWERS}`
: "";
return `Points: ${question.points}\n${question.text}\n${answersText}${distractorText}${questionTypeIndicator}`;
return `Points: ${question.points}\n${question.text}\n${feedbackText}${answersText}${distractorText}${questionTypeIndicator}`;
},
parseMarkdown(input: string, questionIndex: number): LocalQuizQuestion {
@@ -182,7 +286,15 @@ export const quizQuestionMarkdownUtils = {
const linesWithoutPoints = firstLineIsPoints ? lines.slice(1) : lines;
const { linesWithoutAnswers } = linesWithoutPoints.reduce(
// Extract feedback comments first
const {
correctComments,
incorrectComments,
neutralComments,
linesWithoutFeedback,
} = extractFeedback(linesWithoutPoints);
const { linesWithoutAnswers } = linesWithoutFeedback.reduce(
({ linesWithoutAnswers, taking }, currentLine) => {
if (!taking)
return { linesWithoutAnswers: linesWithoutAnswers, taking: false };
@@ -200,7 +312,7 @@ export const quizQuestionMarkdownUtils = {
},
{ linesWithoutAnswers: [] as string[], taking: true }
);
const questionType = getQuestionType(linesWithoutPoints, questionIndex);
const questionType = getQuestionType(linesWithoutFeedback, questionIndex);
const questionTypesWithoutAnswers = [
"essay",
@@ -212,10 +324,9 @@ export const quizQuestionMarkdownUtils = {
questionType.toLowerCase()
)
? linesWithoutAnswers
.slice(0, linesWithoutPoints.length)
.slice(0, linesWithoutFeedback.length)
.filter(
(line) =>
!questionTypesWithoutAnswers.includes(line.toLowerCase())
(line) => !questionTypesWithoutAnswers.includes(line.toLowerCase())
)
: linesWithoutAnswers;
@@ -228,7 +339,7 @@ export const quizQuestionMarkdownUtils = {
"short_answer=",
];
const answers = typesWithAnswers.includes(questionType)
? getAnswers(linesWithoutPoints, questionIndex, questionType)
? getAnswers(linesWithoutFeedback, questionIndex, questionType)
: [];
const answersWithoutDistractors =
@@ -247,6 +358,9 @@ export const quizQuestionMarkdownUtils = {
points,
answers: answersWithoutDistractors,
matchDistractors: distractors,
correctComments,
incorrectComments,
neutralComments,
};
return question;
},