mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
starting to handle feedback parsing bug
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { QuestionType } from "@/features/local/quizzes/models/localQuizQuestion";
|
||||
import { quizMarkdownUtils } from "@/features/local/quizzes/models/utils/quizMarkdownUtils";
|
||||
|
||||
describe("Question Feedback options", () => {
|
||||
it("essay questions can have feedback", () => {
|
||||
const name = "Test Quiz";
|
||||
const rawMarkdownQuiz = `
|
||||
ShuffleAnswers: true
|
||||
OneQuestionAtATime: false
|
||||
DueAt: 08/21/2023 23:59:00
|
||||
LockAt: 08/21/2023 23:59:00
|
||||
AssignmentGroup: Assignments
|
||||
AllowedAttempts: -1
|
||||
Description:
|
||||
---
|
||||
this is the description
|
||||
... this is general feedback
|
||||
essay
|
||||
`;
|
||||
|
||||
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
|
||||
const firstQuestion = quiz.questions[0];
|
||||
|
||||
expect(firstQuestion.questionType).toBe(QuestionType.ESSAY);
|
||||
expect(firstQuestion.text).not.toContain("this is general feedback");
|
||||
expect(firstQuestion.neutralComments).toBe("this is general feedback");
|
||||
});
|
||||
});
|
||||
@@ -5,7 +5,6 @@ import { QuestionType } from "@/features/local/quizzes/models/localQuizQuestion"
|
||||
import { quizMarkdownUtils } from "@/features/local/quizzes/models/utils/quizMarkdownUtils";
|
||||
import { quizQuestionMarkdownUtils } from "@/features/local/quizzes/models/utils/quizQuestionMarkdownUtils";
|
||||
|
||||
// Test suite for QuizMarkdown
|
||||
describe("QuizMarkdownTests", () => {
|
||||
it("can serialize quiz to markdown", () => {
|
||||
const quiz: LocalQuiz = {
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
type FeedbackType = "+" | "-" | "...";
|
||||
|
||||
const isFeedbackStart = (
|
||||
trimmedLine: string,
|
||||
feedbackType: FeedbackType
|
||||
): boolean => {
|
||||
const prefix = feedbackType === "..." ? "... " : `${feedbackType} `;
|
||||
return trimmedLine.startsWith(prefix) || trimmedLine === feedbackType;
|
||||
};
|
||||
|
||||
const extractFeedbackContent = (
|
||||
trimmedLine: string,
|
||||
feedbackType: FeedbackType
|
||||
): string => {
|
||||
if (trimmedLine === feedbackType) return "";
|
||||
|
||||
const prefixLength = feedbackType === "..." ? 4 : 2; // "... " is 4 chars, "+ " and "- " are 2
|
||||
return trimmedLine.substring(prefixLength);
|
||||
};
|
||||
|
||||
const saveFeedback = (
|
||||
feedbackType: FeedbackType | null,
|
||||
feedbackLines: string[],
|
||||
comments: {
|
||||
correct?: string;
|
||||
incorrect?: string;
|
||||
neutral?: string;
|
||||
}
|
||||
): void => {
|
||||
if (!feedbackType || feedbackLines.length === 0) return;
|
||||
|
||||
const feedbackText = feedbackLines.join("\n");
|
||||
if (feedbackType === "+") {
|
||||
comments.correct = feedbackText;
|
||||
} else if (feedbackType === "-") {
|
||||
comments.incorrect = feedbackText;
|
||||
} else if (feedbackType === "...") {
|
||||
comments.neutral = feedbackText;
|
||||
}
|
||||
};
|
||||
|
||||
export const quizFeedbackMarkdownUtils = {
|
||||
extractFeedback(
|
||||
linesWithoutPoints: string[],
|
||||
isAnswerLine: (trimmedLine: string) => boolean
|
||||
): {
|
||||
correctComments?: string;
|
||||
incorrectComments?: string;
|
||||
neutralComments?: string;
|
||||
linesWithoutFeedback: string[];
|
||||
} {
|
||||
const comments: {
|
||||
correct?: string;
|
||||
incorrect?: string;
|
||||
neutral?: string;
|
||||
} = {};
|
||||
const linesWithoutFeedback: string[] = [];
|
||||
|
||||
let currentFeedbackType: FeedbackType | null = null;
|
||||
let currentFeedbackLines: string[] = [];
|
||||
|
||||
for (const line of linesWithoutPoints) {
|
||||
const trimmed = line.trim();
|
||||
|
||||
// Check if this is a new feedback line
|
||||
let newFeedbackType: FeedbackType | null = null;
|
||||
if (isFeedbackStart(trimmed, "+")) {
|
||||
newFeedbackType = "+";
|
||||
} else if (isFeedbackStart(trimmed, "-")) {
|
||||
newFeedbackType = "-";
|
||||
} else if (isFeedbackStart(trimmed, "...")) {
|
||||
newFeedbackType = "...";
|
||||
}
|
||||
|
||||
if (newFeedbackType) {
|
||||
// Save previous feedback if any
|
||||
saveFeedback(currentFeedbackType, currentFeedbackLines, comments);
|
||||
|
||||
// Start new feedback
|
||||
currentFeedbackType = newFeedbackType;
|
||||
const content = extractFeedbackContent(trimmed, newFeedbackType);
|
||||
currentFeedbackLines = content ? [content] : [];
|
||||
} else if (currentFeedbackType && !isAnswerLine(trimmed)) {
|
||||
// This is a continuation of the current feedback
|
||||
currentFeedbackLines.push(line);
|
||||
} else {
|
||||
// Save any pending feedback
|
||||
saveFeedback(currentFeedbackType, currentFeedbackLines, comments);
|
||||
currentFeedbackType = null;
|
||||
currentFeedbackLines = [];
|
||||
|
||||
// This is a regular line
|
||||
linesWithoutFeedback.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
// Save any remaining feedback
|
||||
saveFeedback(currentFeedbackType, currentFeedbackLines, comments);
|
||||
|
||||
return {
|
||||
correctComments: comments.correct,
|
||||
incorrectComments: comments.incorrect,
|
||||
neutralComments: comments.neutral,
|
||||
linesWithoutFeedback,
|
||||
};
|
||||
},
|
||||
|
||||
formatFeedback(
|
||||
correctComments?: string,
|
||||
incorrectComments?: string,
|
||||
neutralComments?: string
|
||||
): string {
|
||||
let feedbackText = "";
|
||||
if (correctComments) {
|
||||
feedbackText += `+ ${correctComments}\n`;
|
||||
}
|
||||
if (incorrectComments) {
|
||||
feedbackText += `- ${incorrectComments}\n`;
|
||||
}
|
||||
if (neutralComments) {
|
||||
feedbackText += `... ${neutralComments}\n`;
|
||||
}
|
||||
return feedbackText;
|
||||
},
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import { LocalQuizQuestion, QuestionType } from "../localQuizQuestion";
|
||||
import { LocalQuizQuestionAnswer } from "../localQuizQuestionAnswer";
|
||||
import { quizQuestionAnswerMarkdownUtils } from "./quizQuestionAnswerMarkdownUtils";
|
||||
import { quizFeedbackMarkdownUtils } from "./quizFeedbackMarkdownUtils";
|
||||
|
||||
const _validFirstAnswerDelimiters = [
|
||||
"*a)",
|
||||
@@ -14,97 +15,11 @@ 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 isAnswerLine = (trimmedLine: string): boolean => {
|
||||
return _validFirstAnswerDelimiters.some((prefix) =>
|
||||
trimmedLine.startsWith(prefix)
|
||||
);
|
||||
};
|
||||
|
||||
const getAnswerStringsWithMultilineSupport = (
|
||||
@@ -246,16 +161,11 @@ export const quizQuestionMarkdownUtils = {
|
||||
: "";
|
||||
|
||||
// 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 feedbackText = quizFeedbackMarkdownUtils.formatFeedback(
|
||||
question.correctComments,
|
||||
question.incorrectComments,
|
||||
question.neutralComments
|
||||
);
|
||||
|
||||
const answersText = answerArray.join("\n");
|
||||
const questionTypeIndicator =
|
||||
@@ -292,7 +202,10 @@ export const quizQuestionMarkdownUtils = {
|
||||
incorrectComments,
|
||||
neutralComments,
|
||||
linesWithoutFeedback,
|
||||
} = extractFeedback(linesWithoutPoints);
|
||||
} = quizFeedbackMarkdownUtils.extractFeedback(
|
||||
linesWithoutPoints,
|
||||
isAnswerLine
|
||||
);
|
||||
|
||||
const { linesWithoutAnswers } = linesWithoutFeedback.reduce(
|
||||
({ linesWithoutAnswers, taking }, currentLine) => {
|
||||
|
||||
Reference in New Issue
Block a user