moving name out of file, will mirror file system name

This commit is contained in:
2024-12-17 14:09:41 -07:00
parent 068c2b6983
commit c557bbcc28
17 changed files with 127 additions and 96 deletions

View File

@@ -19,7 +19,7 @@ GET https://snow.instructure.com/api/v1/courses?enrollment_term_id=751&per_page=
Authorization: Bearer {{$dotenv CANVAS_TOKEN}} Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
### ###
GET https://snow.instructure.com/api/v1/courses/855351/modules GET https://snow.instructure.com/api/v1/courses/1014136/modules
Authorization: Bearer {{$dotenv CANVAS_TOKEN}} Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
### ###

View File

@@ -21,6 +21,7 @@ import { LocalCourseSettings } from "@/models/local/localCourseSettings";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { AssignmentButtons } from "./AssignmentButtons"; import { AssignmentButtons } from "./AssignmentButtons";
import { useAuthoritativeUpdates } from "@/app/course/[courseName]/utils/useAuthoritativeUpdates"; import { useAuthoritativeUpdates } from "@/app/course/[courseName]/utils/useAuthoritativeUpdates";
import { extractLabelValue } from "@/models/local/assignment/utils/markdownUtils";
export default function EditAssignment({ export default function EditAssignment({
moduleName, moduleName,
@@ -64,8 +65,9 @@ export default function EditAssignment({
return; return;
} }
const name = extractLabelValue(text, "Name");
const updatedAssignment: LocalAssignment = const updatedAssignment: LocalAssignment =
localAssignmentMarkdown.parseMarkdown(text); localAssignmentMarkdown.parseMarkdown(text, name);
if ( if (
localAssignmentMarkdown.toMarkdown(assignment) !== localAssignmentMarkdown.toMarkdown(assignment) !==
localAssignmentMarkdown.toMarkdown(updatedAssignment) localAssignmentMarkdown.toMarkdown(updatedAssignment)

View File

@@ -14,6 +14,7 @@ import {
useUpdateQuizMutation, useUpdateQuizMutation,
} from "@/hooks/localCourse/quizHooks"; } from "@/hooks/localCourse/quizHooks";
import { useAuthoritativeUpdates } from "../../../../utils/useAuthoritativeUpdates"; import { useAuthoritativeUpdates } from "../../../../utils/useAuthoritativeUpdates";
import { extractLabelValue } from "@/models/local/assignment/utils/markdownUtils";
const helpString = `QUESTION REFERENCE const helpString = `QUESTION REFERENCE
--- ---
@@ -85,12 +86,19 @@ export default function EditQuiz({
return; return;
} }
try { try {
const name = extractLabelValue(text, "Name");
if ( if (
quizMarkdownUtils.toMarkdown(quiz) !== quizMarkdownUtils.toMarkdown(quiz) !==
quizMarkdownUtils.toMarkdown(quizMarkdownUtils.parseMarkdown(text)) quizMarkdownUtils.toMarkdown(
quizMarkdownUtils.parseMarkdown(text, name)
)
) { ) {
if (clientIsAuthoritative) { if (clientIsAuthoritative) {
const updatedQuiz = quizMarkdownUtils.parseMarkdown(text); const updatedName = extractLabelValue(text, "Name");
const updatedQuiz = quizMarkdownUtils.parseMarkdown(
text,
updatedName
);
await updateQuizMutation await updateQuizMutation
.mutateAsync({ .mutateAsync({
quiz: updatedQuiz, quiz: updatedQuiz,

View File

@@ -47,7 +47,6 @@ const parseIndividualRubricItemMarkdown = (rawMarkdown: string) => {
}; };
const parseSettings = (input: string) => { const parseSettings = (input: string) => {
const name = extractLabelValue(input, "Name");
const rawLockAt = extractLabelValue(input, "LockAt"); const rawLockAt = extractLabelValue(input, "LockAt");
const rawDueAt = extractLabelValue(input, "DueAt"); const rawDueAt = extractLabelValue(input, "DueAt");
const assignmentGroupName = extractLabelValue(input, "AssignmentGroupName"); const assignmentGroupName = extractLabelValue(input, "AssignmentGroupName");
@@ -58,7 +57,6 @@ const parseSettings = (input: string) => {
const lockAt = verifyDateStringOrUndefined(rawLockAt); const lockAt = verifyDateStringOrUndefined(rawLockAt);
return { return {
name,
assignmentGroupName, assignmentGroupName,
submissionTypes, submissionTypes,
fileUploadExtensions, fileUploadExtensions,
@@ -110,10 +108,10 @@ const parseRubricMarkdown = (rawMarkdown: string) => {
export const assignmentMarkdownParser = { export const assignmentMarkdownParser = {
parseRubricMarkdown, parseRubricMarkdown,
parseMarkdown(input: string): LocalAssignment { parseMarkdown(input: string, name: string): LocalAssignment {
const settingsString = input.split("---")[0]; const settingsString = input.split("---")[0];
const { const {
name, // name,
assignmentGroupName, assignmentGroupName,
submissionTypes, submissionTypes,
fileUploadExtensions, fileUploadExtensions,

View File

@@ -40,8 +40,7 @@ const parseNumberOrThrow = (value: string, label: string): number => {
} }
return parsed; return parsed;
}; };
const getQuizWithOnlySettings = (settings: string): LocalQuiz => { const getQuizWithOnlySettings = (settings: string, name: string): LocalQuiz => {
const name = extractLabelValue(settings, "Name");
const rawShuffleAnswers = extractLabelValue(settings, "ShuffleAnswers"); const rawShuffleAnswers = extractLabelValue(settings, "ShuffleAnswers");
const shuffleAnswers = parseBooleanOrThrow( const shuffleAnswers = parseBooleanOrThrow(
@@ -136,10 +135,10 @@ Description: ${quiz.description}
${questionMarkdown}`; ${questionMarkdown}`;
}, },
parseMarkdown(input: string): LocalQuiz { parseMarkdown(input: string, name: string): LocalQuiz {
const splitInput = input.split("---\n"); const splitInput = input.split("---\n");
const settings = splitInput[0]; const settings = splitInput[0];
const quizWithoutQuestions = getQuizWithOnlySettings(settings); const quizWithoutQuestions = getQuizWithOnlySettings(settings, name);
const rawQuestions = splitInput.slice(1); const rawQuestions = splitInput.slice(1);
const questions = rawQuestions const questions = rawQuestions

View File

@@ -6,8 +6,9 @@ import { assignmentMarkdownParser } from "../assignment/utils/assignmentMarkdown
describe("AssignmentMarkdownTests", () => { describe("AssignmentMarkdownTests", () => {
it("can parse assignment settings", () => { it("can parse assignment settings", () => {
const name = "test assignment";
const assignment: LocalAssignment = { const assignment: LocalAssignment = {
name: "test assignment", name,
description: "here is the description", description: "here is the description",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
@@ -22,15 +23,18 @@ describe("AssignmentMarkdownTests", () => {
const assignmentMarkdown = const assignmentMarkdown =
assignmentMarkdownSerializer.toMarkdown(assignment); assignmentMarkdownSerializer.toMarkdown(assignment);
const parsedAssignment = const parsedAssignment = assignmentMarkdownParser.parseMarkdown(
assignmentMarkdownParser.parseMarkdown(assignmentMarkdown); assignmentMarkdown,
name
);
expect(parsedAssignment).toEqual(assignment); expect(parsedAssignment).toEqual(assignment);
}); });
it("assignment with empty rubric can be parsed", () => { it("assignment with empty rubric can be parsed", () => {
const name = "test assignment";
const assignment: LocalAssignment = { const assignment: LocalAssignment = {
name: "test assignment", name,
description: "here is the description", description: "here is the description",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
@@ -43,14 +47,15 @@ describe("AssignmentMarkdownTests", () => {
const assignmentMarkdown = const assignmentMarkdown =
assignmentMarkdownSerializer.toMarkdown(assignment); assignmentMarkdownSerializer.toMarkdown(assignment);
const parsedAssignment = const parsedAssignment =
assignmentMarkdownParser.parseMarkdown(assignmentMarkdown); assignmentMarkdownParser.parseMarkdown(assignmentMarkdown, name);
expect(parsedAssignment).toEqual(assignment); expect(parsedAssignment).toEqual(assignment);
}); });
it("assignment with empty submission types can be parsed", () => { it("assignment with empty submission types can be parsed", () => {
const name = "test assignment";
const assignment: LocalAssignment = { const assignment: LocalAssignment = {
name: "test assignment", name,
description: "here is the description", description: "here is the description",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
@@ -66,14 +71,15 @@ describe("AssignmentMarkdownTests", () => {
const assignmentMarkdown = const assignmentMarkdown =
assignmentMarkdownSerializer.toMarkdown(assignment); assignmentMarkdownSerializer.toMarkdown(assignment);
const parsedAssignment = const parsedAssignment =
assignmentMarkdownParser.parseMarkdown(assignmentMarkdown); assignmentMarkdownParser.parseMarkdown(assignmentMarkdown, name);
expect(parsedAssignment).toEqual(assignment); expect(parsedAssignment).toEqual(assignment);
}); });
it("assignment without lockAt date can be parsed", () => { it("assignment without lockAt date can be parsed", () => {
const name = "test assignment";
const assignment: LocalAssignment = { const assignment: LocalAssignment = {
name: "test assignment", name,
description: "here is the description", description: "here is the description",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
lockAt: undefined, lockAt: undefined,
@@ -89,14 +95,15 @@ describe("AssignmentMarkdownTests", () => {
const assignmentMarkdown = const assignmentMarkdown =
assignmentMarkdownSerializer.toMarkdown(assignment); assignmentMarkdownSerializer.toMarkdown(assignment);
const parsedAssignment = const parsedAssignment =
assignmentMarkdownParser.parseMarkdown(assignmentMarkdown); assignmentMarkdownParser.parseMarkdown(assignmentMarkdown, name);
expect(parsedAssignment).toEqual(assignment); expect(parsedAssignment).toEqual(assignment);
}); });
it("assignment without description can be parsed", () => { it("assignment without description can be parsed", () => {
const name = "test assignment";
const assignment: LocalAssignment = { const assignment: LocalAssignment = {
name: "test assignment", name,
description: "", description: "",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
@@ -112,14 +119,15 @@ describe("AssignmentMarkdownTests", () => {
const assignmentMarkdown = const assignmentMarkdown =
assignmentMarkdownSerializer.toMarkdown(assignment); assignmentMarkdownSerializer.toMarkdown(assignment);
const parsedAssignment = const parsedAssignment =
assignmentMarkdownParser.parseMarkdown(assignmentMarkdown); assignmentMarkdownParser.parseMarkdown(assignmentMarkdown, name);
expect(parsedAssignment).toEqual(assignment); expect(parsedAssignment).toEqual(assignment);
}); });
it("assignments can have three dashes", () => { it("assignments can have three dashes", () => {
const name = "test assignment";
const assignment: LocalAssignment = { const assignment: LocalAssignment = {
name: "test assignment", name,
description: "test assignment\n---\nsomestuff", description: "test assignment\n---\nsomestuff",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
@@ -132,14 +140,15 @@ describe("AssignmentMarkdownTests", () => {
const assignmentMarkdown = const assignmentMarkdown =
assignmentMarkdownSerializer.toMarkdown(assignment); assignmentMarkdownSerializer.toMarkdown(assignment);
const parsedAssignment = const parsedAssignment =
assignmentMarkdownParser.parseMarkdown(assignmentMarkdown); assignmentMarkdownParser.parseMarkdown(assignmentMarkdown, name);
expect(parsedAssignment).toEqual(assignment); expect(parsedAssignment).toEqual(assignment);
}); });
it("assignments can restrict upload types", () => { it("assignments can restrict upload types", () => {
const name = "test assignment";
const assignment: LocalAssignment = { const assignment: LocalAssignment = {
name: "test assignment", name,
description: "here is the description", description: "here is the description",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
@@ -152,7 +161,7 @@ describe("AssignmentMarkdownTests", () => {
const assignmentMarkdown = const assignmentMarkdown =
assignmentMarkdownSerializer.toMarkdown(assignment); assignmentMarkdownSerializer.toMarkdown(assignment);
const parsedAssignment = const parsedAssignment =
assignmentMarkdownParser.parseMarkdown(assignmentMarkdown); assignmentMarkdownParser.parseMarkdown(assignmentMarkdown, name);
expect(parsedAssignment).toEqual(assignment); expect(parsedAssignment).toEqual(assignment);
}); });

View File

@@ -3,8 +3,8 @@ import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils";
describe("Matching Answer Error Messages", () => { describe("Matching Answer Error Messages", () => {
it("can parse matching question", () => { it("can parse matching question", () => {
const name = "Test Quiz";
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -18,8 +18,8 @@ question without answer
`; `;
expect(() => quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz)).toThrowError( expect(() =>
/question type/ quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name)
); ).toThrowError(/question type/);
}); });
}); });

View File

@@ -5,8 +5,8 @@ import { quizQuestionMarkdownUtils } from "@/models/local/quiz/utils/quizQuestio
describe("MatchingTests", () => { describe("MatchingTests", () => {
it("can parse matching question", () => { it("can parse matching question", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -22,7 +22,7 @@ Match the following terms & definitions
^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.) ^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.)
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
expect(firstQuestion.questionType).toBe(QuestionType.MATCHING); expect(firstQuestion.questionType).toBe(QuestionType.MATCHING);
@@ -33,8 +33,8 @@ Match the following terms & definitions
}); });
it("can create markdown for matching question", () => { it("can create markdown for matching question", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -50,7 +50,7 @@ Match the following terms & definitions
^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.) ^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.)
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const questionMarkdown = quizQuestionMarkdownUtils.toMarkdown( const questionMarkdown = quizQuestionMarkdownUtils.toMarkdown(
quiz.questions[0] quiz.questions[0]
); );
@@ -65,8 +65,8 @@ Match the following terms & definitions
}); });
it("whitespace is optional", () => { it("whitespace is optional", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -80,13 +80,13 @@ Match the following terms & definitions
^statement - a single command to be executed ^statement - a single command to be executed
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
expect(quiz.questions[0].answers[0].text).toBe("statement"); expect(quiz.questions[0].answers[0].text).toBe("statement");
}); });
it("can have distractors", () => { it("can have distractors", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -101,15 +101,15 @@ Match the following terms & definitions
^ - this is the distractor ^ - this is the distractor
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
expect(quiz.questions[0].matchDistractors).toEqual([ expect(quiz.questions[0].matchDistractors).toEqual([
"this is the distractor", "this is the distractor",
]); ]);
}); });
it("can have distractors and be persisted", () => { it("can have distractors and be persisted", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -124,7 +124,7 @@ Match the following terms & definitions
^ - this is the distractor ^ - this is the distractor
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz); const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
expect(quizMarkdown).toContain( expect(quizMarkdown).toContain(

View File

@@ -41,8 +41,8 @@ oneline question
}); });
it("can parse question with multiple answers", () => { it("can parse question with multiple answers", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -64,7 +64,7 @@ Which events are triggered when the user clicks on an input field?
--- ---
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
expect(firstQuestion.points).toBe(1); expect(firstQuestion.points).toBe(1);
@@ -79,8 +79,8 @@ Which events are triggered when the user clicks on an input field?
}); });
it("can parse question with multiple answers without a space in false answers", () => { it("can parse question with multiple answers without a space in false answers", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -96,7 +96,7 @@ Which events are triggered when the user clicks on an input field?
[] submit [] submit
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
expect(firstQuestion.answers.length).toBe(2); expect(firstQuestion.answers.length).toBe(2);
@@ -105,8 +105,8 @@ Which events are triggered when the user clicks on an input field?
}); });
it("can parse question with multiple answers without a space in false answers other example", () => { it("can parse question with multiple answers without a space in false answers other example", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -124,7 +124,7 @@ Which tool(s) will let you: create a database migration or reverse-engineer an e
[*] dotnet ef command line interface [*] dotnet ef command line interface
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
expect(firstQuestion.answers.length).toBe(3); expect(firstQuestion.answers.length).toBe(3);

View File

@@ -6,8 +6,9 @@ import { QuestionType } from "@/models/local/quiz/localQuizQuestion";
// Test suite for deterministic checks on LocalQuiz // Test suite for deterministic checks on LocalQuiz
describe("QuizDeterministicChecks", () => { describe("QuizDeterministicChecks", () => {
it("SerializationIsDeterministic_EmptyQuiz", () => { it("SerializationIsDeterministic_EmptyQuiz", () => {
const name = "Test Quiz";
const quiz: LocalQuiz = { const quiz: LocalQuiz = {
name: "Test Quiz", name,
description: "quiz description", description: "quiz description",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
@@ -20,14 +21,15 @@ describe("QuizDeterministicChecks", () => {
}; };
const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz); const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown); const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown, name);
expect(parsedQuiz).toEqual(quiz); expect(parsedQuiz).toEqual(quiz);
}); });
it("SerializationIsDeterministic_ShowCorrectAnswers", () => { it("SerializationIsDeterministic_ShowCorrectAnswers", () => {
const name = "Test Quiz";
const quiz: LocalQuiz = { const quiz: LocalQuiz = {
name: "Test Quiz", name,
description: "quiz description", description: "quiz description",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
@@ -40,14 +42,15 @@ describe("QuizDeterministicChecks", () => {
}; };
const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz); const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown); const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown, name);
expect(parsedQuiz).toEqual(quiz); expect(parsedQuiz).toEqual(quiz);
}); });
it("SerializationIsDeterministic_ShortAnswer", () => { it("SerializationIsDeterministic_ShortAnswer", () => {
const name = "Test Quiz";
const quiz: LocalQuiz = { const quiz: LocalQuiz = {
name: "Test Quiz", name,
description: "quiz description", description: "quiz description",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
@@ -68,14 +71,15 @@ describe("QuizDeterministicChecks", () => {
}; };
const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz); const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown); const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown, name);
expect(parsedQuiz).toEqual(quiz); expect(parsedQuiz).toEqual(quiz);
}); });
it("SerializationIsDeterministic_Essay", () => { it("SerializationIsDeterministic_Essay", () => {
const name = "Test Quiz";
const quiz: LocalQuiz = { const quiz: LocalQuiz = {
name: "Test Quiz", name,
description: "quiz description", description: "quiz description",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
@@ -96,14 +100,15 @@ describe("QuizDeterministicChecks", () => {
}; };
const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz); const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown); const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown, name);
expect(parsedQuiz).toEqual(quiz); expect(parsedQuiz).toEqual(quiz);
}); });
it("SerializationIsDeterministic_MultipleAnswer", () => { it("SerializationIsDeterministic_MultipleAnswer", () => {
const name = "Test Quiz";
const quiz: LocalQuiz = { const quiz: LocalQuiz = {
name: "Test Quiz", name,
description: "quiz description", description: "quiz description",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
@@ -127,14 +132,15 @@ describe("QuizDeterministicChecks", () => {
}; };
const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz); const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown); const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown, name);
expect(parsedQuiz).toEqual(quiz); expect(parsedQuiz).toEqual(quiz);
}); });
it("SerializationIsDeterministic_MultipleChoice", () => { it("SerializationIsDeterministic_MultipleChoice", () => {
const name = "Test Quiz";
const quiz: LocalQuiz = { const quiz: LocalQuiz = {
name: "Test Quiz", name,
description: "quiz description", description: "quiz description",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
@@ -159,14 +165,15 @@ describe("QuizDeterministicChecks", () => {
}; };
const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz); const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown); const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown, name);
expect(parsedQuiz).toEqual(quiz); expect(parsedQuiz).toEqual(quiz);
}); });
it("SerializationIsDeterministic_Matching", () => { it("SerializationIsDeterministic_Matching", () => {
const name = "Test Quiz";
const quiz: LocalQuiz = { const quiz: LocalQuiz = {
name: "Test Quiz", name,
description: "quiz description", description: "quiz description",
lockAt: "08/21/2023 23:59:00", lockAt: "08/21/2023 23:59:00",
dueAt: "08/21/2023 23:59:00", dueAt: "08/21/2023 23:59:00",
@@ -191,7 +198,7 @@ describe("QuizDeterministicChecks", () => {
}; };
const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz); const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown); const parsedQuiz = quizMarkdownUtils.parseMarkdown(quizMarkdown, name);
expect(parsedQuiz).toEqual(quiz); expect(parsedQuiz).toEqual(quiz);
}); });

View File

@@ -37,8 +37,8 @@ this is my description in markdown
}); });
it("can parse markdown quiz with no questions", () => { it("can parse markdown quiz with no questions", () => {
const name = "Test Quiz";
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -51,7 +51,7 @@ description
--- ---
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const expectedDescription = ` const expectedDescription = `
this is the this is the
@@ -67,8 +67,8 @@ description`;
it("can parse markdown quiz with password", () => { it("can parse markdown quiz with password", () => {
const password = "this-is-the-password"; const password = "this-is-the-password";
const name = "Test Quiz";
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
Password: ${password} Password: ${password}
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
@@ -82,14 +82,14 @@ description
--- ---
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
expect(quiz.password).toBe(password); expect(quiz.password).toBe(password);
}); });
it("can parse markdown quiz and configure to show correct answers", () => { it("can parse markdown quiz and configure to show correct answers", () => {
const name = "Test Quiz";
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
ShowCorrectAnswers: false ShowCorrectAnswers: false
@@ -103,14 +103,14 @@ description
--- ---
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
expect(quiz.showCorrectAnswers).toBe(false); expect(quiz.showCorrectAnswers).toBe(false);
}); });
it("can parse quiz with questions", () => { it("can parse quiz with questions", () => {
const name = "Test Quiz";
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -135,7 +135,7 @@ b) false
endline`; endline`;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
expect(firstQuestion.questionType).toBe(QuestionType.MULTIPLE_CHOICE); expect(firstQuestion.questionType).toBe(QuestionType.MULTIPLE_CHOICE);
@@ -149,8 +149,8 @@ b) false
}); });
it("can parse multiple questions", () => { it("can parse multiple questions", () => {
const name = "Test Quiz";
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -170,7 +170,7 @@ Points: 2
b) false b) false
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
expect(firstQuestion.points).toBe(1); expect(firstQuestion.points).toBe(1);
expect(firstQuestion.questionType).toBe(QuestionType.MULTIPLE_ANSWERS); expect(firstQuestion.questionType).toBe(QuestionType.MULTIPLE_ANSWERS);
@@ -181,8 +181,8 @@ b) false
}); });
it("short answer to markdown is correct", () => { it("short answer to markdown is correct", () => {
const name = "Test Quiz";
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -197,7 +197,7 @@ Which events are triggered when the user clicks on an input field?
short answer short answer
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
const questionMarkdown = const questionMarkdown =
@@ -209,8 +209,8 @@ short_answer`;
}); });
it("negative points is allowed", () => { it("negative points is allowed", () => {
const name = "Test Quiz";
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -226,14 +226,14 @@ Which events are triggered when the user clicks on an input field?
short answer short answer
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
expect(firstQuestion.points).toBe(-4); expect(firstQuestion.points).toBe(-4);
}); });
it("floating point points is allowed", () => { it("floating point points is allowed", () => {
const name = "Test Quiz";
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -249,7 +249,7 @@ Which events are triggered when the user clicks on an input field?
short answer short answer
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
expect(firstQuestion.points).toBe(4.56); expect(firstQuestion.points).toBe(4.56);
}); });

View File

@@ -5,8 +5,8 @@ import { describe, it, expect } from "vitest";
describe("TextAnswerTests", () => { describe("TextAnswerTests", () => {
it("can parse essay", () => { it("can parse essay", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -21,7 +21,7 @@ Which events are triggered when the user clicks on an input field?
essay essay
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
expect(firstQuestion.points).toBe(1); expect(firstQuestion.points).toBe(1);
@@ -30,8 +30,8 @@ essay
}); });
it("can parse short answer", () => { it("can parse short answer", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -46,7 +46,7 @@ Which events are triggered when the user clicks on an input field?
short answer short answer
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
expect(firstQuestion.points).toBe(1); expect(firstQuestion.points).toBe(1);
@@ -55,8 +55,9 @@ short answer
}); });
it("short answer to markdown is correct", () => { it("short answer to markdown is correct", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -71,7 +72,7 @@ Which events are triggered when the user clicks on an input field?
short answer short answer
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
const questionMarkdown = const questionMarkdown =
@@ -83,8 +84,8 @@ short_answer`;
}); });
it("essay question to markdown is correct", () => { it("essay question to markdown is correct", () => {
const name = "Test Quiz"
const rawMarkdownQuiz = ` const rawMarkdownQuiz = `
Name: Test Quiz
ShuffleAnswers: true ShuffleAnswers: true
OneQuestionAtATime: false OneQuestionAtATime: false
DueAt: 08/21/2023 23:59:00 DueAt: 08/21/2023 23:59:00
@@ -99,7 +100,7 @@ Which events are triggered when the user clicks on an input field?
essay essay
`; `;
const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
const firstQuestion = quiz.questions[0]; const firstQuestion = quiz.questions[0];
const questionMarkdown = const questionMarkdown =
@@ -128,7 +129,7 @@ essay`;
// short_answer= // short_answer=
// `; // `;
// const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz); // const quiz = quizMarkdownUtils.parseMarkdown(rawMarkdownQuiz, name);
// const firstQuestion = quiz.questions[0]; // const firstQuestion = quiz.questions[0];

View File

@@ -1,7 +1,7 @@
import { CanvasModuleItem } from "@/models/canvas/modules/canvasModuleItems"; import { CanvasModuleItem } from "@/models/canvas/modules/canvasModuleItems";
import { CanvasPage } from "@/models/canvas/pages/canvasPageModel"; import { CanvasPage } from "@/models/canvas/pages/canvasPageModel";
import { axiosClient } from "../axiosUtils"; import { axiosClient } from "../axiosUtils";
import { canvasApi } from "./canvasServiceUtils"; import { canvasApi, paginatedRequest } from "./canvasServiceUtils";
import { CanvasModule } from "@/models/canvas/modules/canvasModule"; import { CanvasModule } from "@/models/canvas/modules/canvasModule";
export const canvasModuleService = { export const canvasModuleService = {
@@ -49,8 +49,8 @@ export const canvasModuleService = {
async getCourseModules(canvasCourseId: number) { async getCourseModules(canvasCourseId: number) {
const url = `${canvasApi}/courses/${canvasCourseId}/modules`; const url = `${canvasApi}/courses/${canvasCourseId}/modules`;
const response = await axiosClient.get<CanvasModule[]>(url); const response = await paginatedRequest<CanvasModule[]>({ url });
return response.data; return response
}, },
async createModule(canvasCourseId: number, moduleName: string) { async createModule(canvasCourseId: number, moduleName: string) {

View File

@@ -34,8 +34,9 @@ const getAssignment = async (
assignmentName + ".md" assignmentName + ".md"
); );
const rawFile = (await fs.readFile(filePath, "utf-8")).replace(/\r\n/g, "\n"); const rawFile = (await fs.readFile(filePath, "utf-8")).replace(/\r\n/g, "\n");
return localAssignmentMarkdown.parseMarkdown(rawFile); return localAssignmentMarkdown.parseMarkdown(rawFile, assignmentName);
}; };
export const assignmentsFileStorageService = { export const assignmentsFileStorageService = {
getAssignmentNames, getAssignmentNames,
getAssignment, getAssignment,
@@ -72,7 +73,6 @@ export const assignmentsFileStorageService = {
assignmentMarkdownSerializer.toMarkdown(assignment); assignmentMarkdownSerializer.toMarkdown(assignment);
console.log(`Saving assignment ${filePath}`); console.log(`Saving assignment ${filePath}`);
await fs.writeFile(filePath, assignmentMarkdown); await fs.writeFile(filePath, assignmentMarkdown);
}, },
async delete({ async delete({

View File

@@ -56,11 +56,12 @@ const getItem = async <T extends CourseItemType>(
const rawFile = (await fs.readFile(filePath, "utf-8")).replace(/\r\n/g, "\n"); const rawFile = (await fs.readFile(filePath, "utf-8")).replace(/\r\n/g, "\n");
if (type === "Assignment") { if (type === "Assignment") {
return localAssignmentMarkdown.parseMarkdown( return localAssignmentMarkdown.parseMarkdown(
rawFile rawFile,
name
) as CourseItemReturnType<T>; ) as CourseItemReturnType<T>;
} else if (type === "Quiz") { } else if (type === "Quiz") {
return localQuizMarkdownUtils.parseMarkdown( return localQuizMarkdownUtils.parseMarkdown(
rawFile rawFile, name
) as CourseItemReturnType<T>; ) as CourseItemReturnType<T>;
} else if (type === "Page") { } else if (type === "Page") {
return localPageMarkdownUtils.parseMarkdown( return localPageMarkdownUtils.parseMarkdown(

View File

@@ -98,7 +98,7 @@ SubmissionTypes:
AllowedFileUploadExtensions: AllowedFileUploadExtensions:
- pdf - pdf
--- ---
description this is the test description
## Rubric ## Rubric
- 2pts: animation has at least 5 transition states - 2pts: animation has at least 5 transition states
`; `;

View File

@@ -1,8 +1,14 @@
import { loadEnvConfig } from "@next/env"; import nextEnv from "@next/env";
import { defineConfig } from "vitest/config"; import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react"; import react from "@vitejs/plugin-react";
loadEnvConfig(process.cwd()); // hack from https://github.com/vercel/next.js/issues/68091
if (nextEnv && "loadEnvConfig" in nextEnv) {
nextEnv.loadEnvConfig(process.cwd());
} else {
// eslint-disable-next-line
require("@next/env").loadEnvConfig(process.cwd());
}
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],