matched tests with distractors

This commit is contained in:
2024-08-24 14:13:14 -06:00
parent f8e1990f09
commit 87742f1768
4 changed files with 160 additions and 10 deletions

View File

@@ -5,6 +5,7 @@ export interface LocalQuizQuestion {
questionType: QuestionType; questionType: QuestionType;
points: number; points: number;
answers: LocalQuizQuestionAnswer[]; answers: LocalQuizQuestionAnswer[];
matchDistractors: string[];
} }
export enum QuestionType { export enum QuestionType {

View File

@@ -126,6 +126,12 @@ export const quizQuestionMarkdownUtils = {
const answerArray = question.answers.map((a, i) => const answerArray = question.answers.map((a, i) =>
getAnswerMarkdown(question, a, i) getAnswerMarkdown(question, a, i)
); );
const distractorText =
question.questionType === QuestionType.MATCHING
? question.matchDistractors?.map((d) => `\n^ - ${d}`).join("") ?? ""
: "";
const answersText = answerArray.join("\n"); const answersText = answerArray.join("\n");
const questionTypeIndicator = const questionTypeIndicator =
question.questionType === "essay" || question.questionType === "essay" ||
@@ -133,7 +139,7 @@ export const quizQuestionMarkdownUtils = {
? question.questionType ? question.questionType
: ""; : "";
return `Points: ${question.points}\n${question.text}\n${answersText}${questionTypeIndicator}`; return `Points: ${question.points}\n${question.text}\n${answersText}${distractorText}${questionTypeIndicator}`;
}, },
parseMarkdown(input: string, questionIndex: number): LocalQuizQuestion { parseMarkdown(input: string, questionIndex: number): LocalQuizQuestion {
@@ -153,13 +159,6 @@ export const quizQuestionMarkdownUtils = {
const linesWithoutPoints = firstLineIsPoints ? lines.slice(1) : lines; const linesWithoutPoints = firstLineIsPoints ? lines.slice(1) : lines;
// const linesWithoutAnswers = linesWithoutPoints.filter(
// (line, index) =>
// !_validFirstAnswerDelimiters.some((prefix) =>
// line.trimStart().startsWith(prefix)
// )
// );
const { linesWithoutAnswers } = linesWithoutPoints.reduce( const { linesWithoutAnswers } = linesWithoutPoints.reduce(
({ linesWithoutAnswers, taking }, currentLine) => { ({ linesWithoutAnswers, taking }, currentLine) => {
if (!taking) if (!taking)
@@ -208,11 +207,22 @@ export const quizQuestionMarkdownUtils = {
? getAnswers(linesWithoutPoints, questionIndex, questionType) ? getAnswers(linesWithoutPoints, questionIndex, questionType)
: []; : [];
const answersWithoutDistractors =
questionType === QuestionType.MATCHING
? answers.filter((a) => a.text)
: answers;
const distractors =
questionType === QuestionType.MATCHING
? answers.filter((a) => !a.text).map((a) => a.matchedText ?? "")
: [];
const question: LocalQuizQuestion = { const question: LocalQuizQuestion = {
text: description, text: description,
questionType, questionType,
points, points,
answers, answers: answersWithoutDistractors,
matchDistractors: distractors,
}; };
return question; return question;
}, },

View File

@@ -0,0 +1,134 @@
import { describe, it, expect } from "vitest";
import { QuestionType } from "../../../../../models/local/quiz/localQuizQuestion";
import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils";
import { quizQuestionMarkdownUtils } from "@/models/local/quiz/utils/quizQuestionMarkdownUtils";
describe("MatchingTests", () => {
it("can parse matching question", () => {
const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true
OneQuestionAtATime: false
DueAt: 21/08/2023 23:59:00
LockAt: 21/08/2023 23:59:00
AssignmentGroup: Assignments
AllowedAttempts: -1
Description:
---
Match the following terms & definitions
^ statement - a single command to be executed
^ identifier - name of a variable
^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.)
`;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz);
const firstQuestion = quiz.questions[0];
expect(firstQuestion.questionType).toBe(QuestionType.MATCHING);
expect(firstQuestion.text).not.toContain("statement");
expect(firstQuestion.answers[0].matchedText).toBe(
"a single command to be executed"
);
});
it("can create markdown for matching question", () => {
const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true
OneQuestionAtATime: false
DueAt: 21/08/2023 23:59:00
LockAt: 21/08/2023 23:59:00
AssignmentGroup: Assignments
AllowedAttempts: -1
Description:
---
Match the following terms & definitions
^ statement - a single command to be executed
^ identifier - name of a variable
^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.)
`;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz);
const questionMarkdown = quizQuestionMarkdownUtils.toMarkdown(
quiz.questions[0]
);
const expectedMarkdown = `Points: 1
Match the following terms & definitions
^ statement - a single command to be executed
^ identifier - name of a variable
^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.)`;
expect(questionMarkdown).toContain(expectedMarkdown);
});
it("whitespace is optional", () => {
const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true
OneQuestionAtATime: false
DueAt: 21/08/2023 23:59:00
LockAt: 21/08/2023 23:59:00
AssignmentGroup: Assignments
AllowedAttempts: -1
Description:
---
Match the following terms & definitions
^statement - a single command to be executed
`;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz);
expect(quiz.questions[0].answers[0].text).toBe("statement");
});
it("can have distractors", () => {
const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true
OneQuestionAtATime: false
DueAt: 21/08/2023 23:59:00
LockAt: 21/08/2023 23:59:00
AssignmentGroup: Assignments
AllowedAttempts: -1
Description:
---
Match the following terms & definitions
^ statement - a single command to be executed
^ - this is the distractor
`;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz);
expect(quiz.questions[0].matchDistractors).toEqual([
"this is the distractor",
]);
});
it("can have distractors and be persisted", () => {
const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true
OneQuestionAtATime: false
DueAt: 21/08/2023 23:59:00
LockAt: 21/08/2023 23:59:00
AssignmentGroup: Assignments
AllowedAttempts: -1
Description:
---
Match the following terms & definitions
^ statement - a single command to be executed
^ - this is the distractor
`;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz);
const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
expect(quizMarkdown).toContain(
"^ statement - a single command to be executed\n^ - this is the distractor"
);
});
});

View File

@@ -59,7 +59,8 @@ describe("QuizDeterministicChecks", () => {
text: "test short answer", text: "test short answer",
questionType: QuestionType.SHORT_ANSWER, questionType: QuestionType.SHORT_ANSWER,
points: 1, points: 1,
answers: [] answers: [],
matchDistractors: [],
}, },
], ],
allowedAttempts: -1, allowedAttempts: -1,
@@ -86,6 +87,7 @@ describe("QuizDeterministicChecks", () => {
text: "test essay", text: "test essay",
questionType: QuestionType.ESSAY, questionType: QuestionType.ESSAY,
points: 1, points: 1,
matchDistractors: [],
answers: [] answers: []
}, },
], ],
@@ -113,6 +115,7 @@ describe("QuizDeterministicChecks", () => {
text: "test multiple answer", text: "test multiple answer",
questionType: QuestionType.MULTIPLE_ANSWERS, questionType: QuestionType.MULTIPLE_ANSWERS,
points: 1, points: 1,
matchDistractors: [],
answers: [ answers: [
{ text: "yes", correct: true }, { text: "yes", correct: true },
{ text: "no", correct: true }, { text: "no", correct: true },
@@ -144,6 +147,7 @@ describe("QuizDeterministicChecks", () => {
text: "test multiple choice", text: "test multiple choice",
questionType: QuestionType.MULTIPLE_CHOICE, questionType: QuestionType.MULTIPLE_CHOICE,
points: 1, points: 1,
matchDistractors: [],
answers: [ answers: [
{ text: "yes", correct: true }, { text: "yes", correct: true },
{ text: "no", correct: false }, { text: "no", correct: false },
@@ -175,6 +179,7 @@ describe("QuizDeterministicChecks", () => {
text: "test matching", text: "test matching",
questionType: QuestionType.MATCHING, questionType: QuestionType.MATCHING,
points: 1, points: 1,
matchDistractors: [],
answers: [ answers: [
{ text: "yes", correct: true, matchedText: "testing yes" }, { text: "yes", correct: true, matchedText: "testing yes" },
{ text: "no", correct: true, matchedText: "testing no" }, { text: "no", correct: true, matchedText: "testing no" },