mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
matched tests with distractors
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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" },
|
||||||
|
|||||||
Reference in New Issue
Block a user