@@ -89,7 +92,9 @@ function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
{question.questionType === QuestionType.MATCHING && (
@@ -98,8 +103,10 @@ function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
key={JSON.stringify(answer)}
className="mx-3 mb-1 bg-dark rounded border border-slate-600 flex flex-row"
>
-
{answer.text} -
-
{answer.matchedText}
+
+ {escapeMatchingText(answer.text)}
+
+
{answer.matchedText}
))}
{question.matchDistractors.map((distractor) => (
diff --git a/src/models/local/quiz/utils/quizQuestionAnswerMarkdownUtils.ts b/src/models/local/quiz/utils/quizQuestionAnswerMarkdownUtils.ts
index cf70862..461c563 100644
--- a/src/models/local/quiz/utils/quizQuestionAnswerMarkdownUtils.ts
+++ b/src/models/local/quiz/utils/quizQuestionAnswerMarkdownUtils.ts
@@ -1,6 +1,18 @@
import { QuestionType } from "../localQuizQuestion";
import { LocalQuizQuestionAnswer } from "../localQuizQuestionAnswer";
+const parseMatchingAnswer = (input: string) => {
+ const matchingPattern = /^\^?/;
+ const textWithoutMatchDelimiter = input.replace(matchingPattern, "");
+ const [text, ...matchedParts] = textWithoutMatchDelimiter.split(" - ");
+ const answer: LocalQuizQuestionAnswer = {
+ correct: true,
+ text: text.trim(),
+ matchedText: matchedParts.join("-").trim(),
+ };
+ return answer;
+};
+
export const quizQuestionAnswerMarkdownUtils = {
// getHtmlText(): string {
// return MarkdownService.render(this.text);
@@ -10,17 +22,7 @@ export const quizQuestionAnswerMarkdownUtils = {
const isCorrect = input.startsWith("*") || input[1] === "*";
if (questionType === QuestionType.MATCHING) {
- const matchingPattern = /^\^ ?/;
- const textWithoutMatchDelimiter = input
- .replace(matchingPattern, "")
- .trim();
- const [text, ...matchedParts] = textWithoutMatchDelimiter.split("-");
- const answer: LocalQuizQuestionAnswer = {
- correct: true,
- text: text.trim(),
- matchedText: matchedParts.join("-").trim(),
- };
- return answer;
+ return parseMatchingAnswer(input);
}
const startingQuestionPattern = /^(\*?[a-z]?\))|\[\s*\]|\[\*\]|\^ /;
diff --git a/src/models/local/quiz/utils/quizQuestionMarkdownUtils.ts b/src/models/local/quiz/utils/quizQuestionMarkdownUtils.ts
index 1efebe7..0f4d10b 100644
--- a/src/models/local/quiz/utils/quizQuestionMarkdownUtils.ts
+++ b/src/models/local/quiz/utils/quizQuestionMarkdownUtils.ts
@@ -69,11 +69,11 @@ const getQuestionType = (
"short_answer"
)
return QuestionType.SHORT_ANSWER;
- if (
+ if (
linesWithoutPoints[linesWithoutPoints.length - 1].toLowerCase().trim() ===
- "short_answer="
- )
- return QuestionType.SHORT_ANSWER_WITH_ANSWERS;
+ "short_answer="
+ )
+ return QuestionType.SHORT_ANSWER_WITH_ANSWERS;
const answerLines = getAnswerStringsWithMultilineSupport(
linesWithoutPoints,
@@ -102,7 +102,11 @@ const getAnswers = (
questionIndex: number,
questionType: string
): LocalQuizQuestionAnswer[] => {
- if (questionType == QuestionType.SHORT_ANSWER_WITH_ANSWERS) linesWithoutPoints = linesWithoutPoints.slice(0, linesWithoutPoints.length - 1);
+ if (questionType == QuestionType.SHORT_ANSWER_WITH_ANSWERS)
+ linesWithoutPoints = linesWithoutPoints.slice(
+ 0,
+ linesWithoutPoints.length - 1
+ );
const answerLines = getAnswerStringsWithMultilineSupport(
linesWithoutPoints,
questionIndex
diff --git a/src/models/local/tests/quizMarkdown/matchingAnswers.test.ts b/src/models/local/tests/quizMarkdown/matchingAnswers.test.ts
index a75bb3e..743347e 100644
--- a/src/models/local/tests/quizMarkdown/matchingAnswers.test.ts
+++ b/src/models/local/tests/quizMarkdown/matchingAnswers.test.ts
@@ -131,4 +131,36 @@ Match the following terms & definitions
"^ statement - a single command to be executed\n^ - this is the distractor"
);
});
+ it("can escape - characters", () => {
+ 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:
+---
+Match the following terms & definitions
+
+^ git add \-\-all - start tracking all files in the current directory and subdirectories
+`;
+
+ const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
+
+
+ const firstQuestion = quiz.questions[0];
+
+ expect(firstQuestion.answers[0].text).toBe("git add --all");
+ expect(firstQuestion.answers[0].matchedText).toBe("start tracking all files in the current directory and subdirectories");
+
+
+
+ const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
+
+ expect(quizMarkdown).toContain(
+ "^ git add \-\-all - start tracking all files in the current directory and subdirectories"
+ );
+ });
});
diff --git a/src/services/canvas/canvasQuizService.ts b/src/services/canvas/canvasQuizService.ts
index 3b92f05..23092a7 100644
--- a/src/services/canvas/canvasQuizService.ts
+++ b/src/services/canvas/canvasQuizService.ts
@@ -11,16 +11,24 @@ import {
} from "@/models/local/quiz/localQuizQuestion";
import { CanvasQuizQuestion } from "@/models/canvas/quizzes/canvasQuizQuestionModel";
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
+import { escapeMatchingText } from "../utils/questionHtmlUtils";
+
export const getAnswers = (
question: LocalQuizQuestion,
settings: LocalCourseSettings
) => {
if (question.questionType === QuestionType.MATCHING)
- return question.answers.map((a) => ({
- answer_match_left: a.text,
- answer_match_right: a.matchedText,
- }));
+ return question.answers.map((a) => {
+ const text =
+ question.questionType === QuestionType.MATCHING
+ ? escapeMatchingText(a.text)
+ : a.text;
+ return {
+ answer_match_left: text,
+ answer_match_right: a.matchedText,
+ };
+ });
return question.answers.map((answer) => ({
answer_html: markdownToHTMLSafe(answer.text, settings),
@@ -29,11 +37,9 @@ export const getAnswers = (
}));
};
-export const getQuestionType = (
- question: LocalQuizQuestion
-) => {
+export const getQuestionType = (question: LocalQuizQuestion) => {
return `${question.questionType.replace("=", "")}_question`;
-}
+};
const createQuestionOnly = async (
canvasCourseId: number,
@@ -45,6 +51,7 @@ const createQuestionOnly = async (
console.log("Creating individual question"); //, question);
const url = `${canvasApi}/courses/${canvasCourseId}/quizzes/${canvasQuizId}/questions`;
+
const body = {
question: {
question_text: markdownToHTMLSafe(question.text, settings),
@@ -179,7 +186,12 @@ export const canvasQuizService = {
};
const { data: canvasQuiz } = await axiosClient.post
(url, body);
- await createQuizQuestions(canvasCourseId, canvasQuiz.id, localQuiz, settings);
+ await createQuizQuestions(
+ canvasCourseId,
+ canvasQuiz.id,
+ localQuiz,
+ settings
+ );
return canvasQuiz.id;
},
async delete(canvasCourseId: number, canvasQuizId: number) {
diff --git a/src/services/utils/questionHtmlUtils.ts b/src/services/utils/questionHtmlUtils.ts
new file mode 100644
index 0000000..e3e35b9
--- /dev/null
+++ b/src/services/utils/questionHtmlUtils.ts
@@ -0,0 +1,4 @@
+
+export function escapeMatchingText(input: string){
+ return input.replaceAll("\\-", "-");
+}
\ No newline at end of file