more refactoring by feature

This commit is contained in:
2025-07-23 09:46:35 -06:00
parent d5a40e52d9
commit 3e371247d6
92 changed files with 159 additions and 158 deletions

View File

@@ -7,7 +7,7 @@ import { CanvasRubricCreationResponse } from "@/models/canvas/assignments/canvas
import { assignmentPoints } from "@/features/local/assignments/models/utils/assignmentPointsUtils";
import { getDateFromString } from "@/models/local/utils/timeUtils";
import { getRubricCriterion } from "./canvasRubricUtils";
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
import { LocalCourseSettings } from "@/features/local/course/localCourseSettings";
export const canvasAssignmentService = {
async getAll(courseId: number): Promise<CanvasAssignment[]> {

View File

@@ -4,7 +4,7 @@ import { canvasApi, paginatedRequest } from "./canvasServiceUtils";
import { markdownToHTMLSafe } from "../htmlMarkdownUtils";
import { axiosClient } from "../axiosUtils";
import { rateLimitAwareDelete } from "./canvasWebRequestor";
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
import { LocalCourseSettings } from "@/features/local/course/localCourseSettings";
export const canvasPageService = {
async getAll(courseId: number): Promise<CanvasPage[]> {

View File

@@ -5,10 +5,10 @@ import { markdownToHTMLSafe } from "../htmlMarkdownUtils";
import { getDateFromStringOrThrow } from "@/models/local/utils/timeUtils";
import { canvasAssignmentService } from "./canvasAssignmentService";
import { CanvasQuizQuestion } from "@/models/canvas/quizzes/canvasQuizQuestionModel";
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
import { escapeMatchingText } from "../utils/questionHtmlUtils";
import { LocalQuiz } from "@/features/local/quizzes/models/localQuiz";
import { LocalQuizQuestion, QuestionType } from "@/features/local/quizzes/models/localQuizQuestion";
import { LocalCourseSettings } from "@/features/local/course/localCourseSettings";
export const getAnswers = (
question: LocalQuizQuestion,

View File

@@ -1,100 +0,0 @@
import {
localAssignmentMarkdown,
LocalAssignment,
} from "@/features/local/assignments/models/localAssignment";
import { assignmentMarkdownSerializer } from "@/features/local/assignments/models/utils/assignmentMarkdownSerializer";
import path from "path";
import { directoryOrFileExists } from "./utils/fileSystemUtils";
import { promises as fs } from "fs";
import { courseItemFileStorageService } from "./courseItemFileStorageService";
import { getCoursePathByName } from "./globalSettingsFileStorageService";
const getAssignmentNames = async (courseName: string, moduleName: string) => {
const courseDirectory = await getCoursePathByName(courseName);
const filePath = path.join(courseDirectory, moduleName, "assignments");
if (!(await directoryOrFileExists(filePath))) {
console.log(
`Error loading course by name, assignments folder does not exist in ${filePath}`
);
// await fs.mkdir(filePath);
return [];
}
const assignmentFiles = await fs.readdir(filePath);
return assignmentFiles.map((f) => f.replace(/\.md$/, ""));
};
const getAssignment = async (
courseName: string,
moduleName: string,
assignmentName: string
) => {
const courseDirectory = await getCoursePathByName(courseName);
const filePath = path.join(
courseDirectory,
moduleName,
"assignments",
assignmentName + ".md"
);
const rawFile = (await fs.readFile(filePath, "utf-8")).replace(/\r\n/g, "\n");
return localAssignmentMarkdown.parseMarkdown(rawFile, assignmentName);
};
export const assignmentsFileStorageService = {
getAssignmentNames,
getAssignment,
async getAssignments(courseName: string, moduleName: string) {
return await courseItemFileStorageService.getItems(
courseName,
moduleName,
"Assignment"
);
},
async updateOrCreateAssignment({
courseName,
moduleName,
assignmentName,
assignment,
}: {
courseName: string;
moduleName: string;
assignmentName: string;
assignment: LocalAssignment;
}) {
const courseDirectory = await getCoursePathByName(courseName);
const folder = path.join(courseDirectory, moduleName, "assignments");
await fs.mkdir(folder, { recursive: true });
const filePath = path.join(
courseDirectory,
moduleName,
"assignments",
assignmentName + ".md"
);
const assignmentMarkdown =
assignmentMarkdownSerializer.toMarkdown(assignment);
console.log(`Saving assignment ${filePath}`);
await fs.writeFile(filePath, assignmentMarkdown);
},
async delete({
courseName,
moduleName,
assignmentName,
}: {
courseName: string;
moduleName: string;
assignmentName: string;
}) {
const courseDirectory = await getCoursePathByName(courseName);
const filePath = path.join(
courseDirectory,
moduleName,
"assignments",
assignmentName + ".md"
);
console.log("removing assignment", filePath);
await fs.unlink(filePath);
},
};

View File

@@ -1,131 +0,0 @@
import path from "path";
import { directoryOrFileExists } from "./utils/fileSystemUtils";
import fs from "fs/promises";
import {
LocalAssignment,
localAssignmentMarkdown,
} from "@/features/local/assignments/models/localAssignment";
import { assignmentMarkdownSerializer } from "@/features/local/assignments/models/utils/assignmentMarkdownSerializer";
import {
CourseItemReturnType,
CourseItemType,
typeToFolder,
} from "@/models/local/courseItemTypes";
import { getCoursePathByName } from "./globalSettingsFileStorageService";
import {
localPageMarkdownUtils,
LocalCoursePage,
} from "@/features/local/pages/localCoursePageModels";
import { LocalQuiz, localQuizMarkdownUtils } from "@/features/local/quizzes/models/localQuiz";
import { quizMarkdownUtils } from "@/features/local/quizzes/models/utils/quizMarkdownUtils";
const getItemFileNames = async (
courseName: string,
moduleName: string,
type: CourseItemType
) => {
const courseDirectory = await getCoursePathByName(courseName);
const folder = typeToFolder[type];
const filePath = path.join(courseDirectory, moduleName, folder);
if (!(await directoryOrFileExists(filePath))) {
console.log(
`Error loading ${type}, ${folder} folder does not exist in ${filePath}`
);
await fs.mkdir(filePath);
}
const itemFiles = await fs.readdir(filePath);
return itemFiles.map((f) => f.replace(/\.md$/, ""));
};
const getItem = async <T extends CourseItemType>(
courseName: string,
moduleName: string,
name: string,
type: T
): Promise<CourseItemReturnType<T>> => {
const courseDirectory = await getCoursePathByName(courseName);
const folder = typeToFolder[type];
const filePath = path.join(courseDirectory, moduleName, folder, name + ".md");
const rawFile = (await fs.readFile(filePath, "utf-8")).replace(/\r\n/g, "\n");
if (type === "Assignment") {
return localAssignmentMarkdown.parseMarkdown(
rawFile,
name
) as CourseItemReturnType<T>;
} else if (type === "Quiz") {
return localQuizMarkdownUtils.parseMarkdown(
rawFile,
name
) as CourseItemReturnType<T>;
} else if (type === "Page") {
return localPageMarkdownUtils.parseMarkdown(
rawFile,
name
) as CourseItemReturnType<T>;
}
throw Error(`cannot read item, invalid type: ${type} in ${filePath}`);
};
export const courseItemFileStorageService = {
getItem,
getItems: async <T extends CourseItemType>(
courseName: string,
moduleName: string,
type: T
): Promise<CourseItemReturnType<T>[]> => {
const fileNames = await getItemFileNames(courseName, moduleName, type);
const items = (
await Promise.all(
fileNames.map(async (name) => {
try {
const item = await getItem(courseName, moduleName, name, type);
return item;
} catch {
return null;
}
})
)
).filter((a) => a !== null);
return items;
},
async updateOrCreateAssignment({
courseName,
moduleName,
name,
item,
type,
}: {
courseName: string;
moduleName: string;
name: string;
item: LocalAssignment | LocalQuiz | LocalCoursePage;
type: CourseItemType;
}) {
const courseDirectory = await getCoursePathByName(courseName);
const typeFolder = typeToFolder[type];
const folder = path.join(courseDirectory, moduleName, typeFolder);
await fs.mkdir(folder, { recursive: true });
const filePath = path.join(
courseDirectory,
moduleName,
typeFolder,
name + ".md"
);
const markdownDictionary: {
[_key in CourseItemType]: () => string;
} = {
Assignment: () =>
assignmentMarkdownSerializer.toMarkdown(item as LocalAssignment),
Quiz: () => quizMarkdownUtils.toMarkdown(item as LocalQuiz),
Page: () => localPageMarkdownUtils.toMarkdown(item as LocalCoursePage),
};
const itemMarkdown = markdownDictionary[type]();
console.log(`Saving ${type} ${filePath}`);
await fs.writeFile(filePath, itemMarkdown);
},
};

View File

@@ -1,12 +1,12 @@
import { promises as fs } from "fs";
import path from "path";
import { basePath, directoryOrFileExists } from "./utils/fileSystemUtils";
import { assignmentsFileStorageService } from "./assignmentsFileStorageService";
import { quizFileStorageService } from "./quizFileStorageService";
import { quizFileStorageService } from "../../features/local/quizzes/quizFileStorageService";
import { pageFileStorageService } from "../../features/local/pages/pageFileStorageService";
import { moduleFileStorageService } from "./moduleFileStorageService";
import { settingsFileStorageService } from "./settingsFileStorageService";
import { moduleFileStorageService } from "../../features/local/modules/moduleFileStorageService";
import { settingsFileStorageService } from "../../features/local/course/settingsFileStorageService";
import { getCoursePathByName } from "./globalSettingsFileStorageService";
import { assignmentsFileStorageService } from "@/features/local/assignments/assignmentsFileStorageService";
export const fileStorageService = {
settings: settingsFileStorageService,

View File

@@ -1,125 +0,0 @@
import path from "path";
import fs from "fs/promises";
import {
getLectureWeekName,
lectureFolderName,
lectureToString,
parseLecture,
} from "./utils/lectureUtils";
import { Lecture } from "@/models/local/lecture";
import {
getDayOfWeek,
LocalCourseSettings,
} from "@/models/local/localCourseSettings";
import { getDateFromStringOrThrow } from "@/models/local/utils/timeUtils";
import { getCoursePathByName } from "./globalSettingsFileStorageService";
export async function getLectures(courseName: string) {
const courseDirectory = await getCoursePathByName(courseName);
const courseLectureRoot = path.join(courseDirectory, lectureFolderName);
if (!(await directoryExists(courseLectureRoot))) {
return [];
}
const entries = await fs.readdir(courseLectureRoot, { withFileTypes: true });
const lectureWeekFolders = entries
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name);
const lecturesByWeek = await Promise.all(
lectureWeekFolders.map(async (weekName) => {
const weekBasePath = path.join(courseLectureRoot, weekName);
const fileNames = await fs.readdir(weekBasePath);
const lectures = await Promise.all(
fileNames.map(async (fileName) => {
const filePath = path.join(weekBasePath, fileName);
const fileContent = await fs.readFile(filePath, "utf-8");
const lecture = parseLecture(fileContent);
return lecture;
})
);
return {
weekName,
lectures,
};
})
);
return lecturesByWeek;
}
export async function updateLecture(
courseName: string,
courseSettings: LocalCourseSettings,
lecture: Lecture
) {
const courseDirectory = await getCoursePathByName(courseName);
const courseLectureRoot = path.join(courseDirectory, lectureFolderName);
const lectureDate = getDateFromStringOrThrow(
lecture.date,
"lecture start date in update lecture"
);
const weekFolderName = getLectureWeekName(
courseSettings.startDate,
lecture.date
);
const weekPath = path.join(courseLectureRoot, weekFolderName);
if (!(await directoryExists(weekPath))) {
await fs.mkdir(weekPath, { recursive: true });
}
const lecturePath = path.join(
weekPath,
`${lectureDate.getDay()}-${getDayOfWeek(lectureDate)}.md`
);
const lectureContents = lectureToString(lecture);
await fs.writeFile(lecturePath, lectureContents);
}
export async function deleteLecture(
courseName: string,
courseSettings: LocalCourseSettings,
dayAsString: string
) {
console.log("deleting lecture", courseName, dayAsString);
const lectureDate = getDateFromStringOrThrow(
dayAsString,
"lecture start date in update lecture"
);
const weekFolderName = getLectureWeekName(
courseSettings.startDate,
dayAsString
);
const courseDirectory = await getCoursePathByName(courseName);
const courseLectureRoot = path.join(courseDirectory, lectureFolderName);
const weekPath = path.join(courseLectureRoot, weekFolderName);
const lecturePath = path.join(
weekPath,
`${lectureDate.getDay()}-${getDayOfWeek(lectureDate)}.md`
);
try {
await fs.access(lecturePath); // throws error if no file
await fs.unlink(lecturePath);
console.log(`File deleted: ${lecturePath}`);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
if (error?.code === "ENOENT") {
console.log(`Cannot delete lecture, file does not exist: ${lecturePath}`);
} else {
throw error;
}
}
}
const directoryExists = async (path: string): Promise<boolean> => {
try {
const stat = await fs.stat(path);
return stat.isDirectory();
} catch {
return false;
}
};

View File

@@ -1,27 +0,0 @@
import { promises as fs } from "fs";
import { lectureFolderName } from "./utils/lectureUtils";
import { getCoursePathByName } from "./globalSettingsFileStorageService";
export const moduleFileStorageService = {
async getModuleNames(courseName: string) {
const courseDirectory = await getCoursePathByName(courseName);
const moduleDirectories = await fs.readdir(courseDirectory, {
withFileTypes: true,
});
const modulePromises = moduleDirectories
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name);
const modules = await Promise.all(modulePromises);
const modulesWithoutLectures = modules.filter(
(m) => m !== lectureFolderName
);
return modulesWithoutLectures.sort((a, b) => a.localeCompare(b));
},
async createModule(courseName: string, moduleName: string) {
const courseDirectory = await getCoursePathByName(courseName);
await fs.mkdir(courseDirectory + "/" + moduleName, { recursive: true });
},
};

View File

@@ -1,63 +0,0 @@
import path from "path";
import { promises as fs } from "fs";
import { courseItemFileStorageService } from "./courseItemFileStorageService";
import { getCoursePathByName } from "./globalSettingsFileStorageService";
import { LocalQuiz } from "@/features/local/quizzes/models/localQuiz";
import { quizMarkdownUtils } from "@/features/local/quizzes/models/utils/quizMarkdownUtils";
export const quizFileStorageService = {
getQuiz: async (courseName: string, moduleName: string, quizName: string) =>
await courseItemFileStorageService.getItem(
courseName,
moduleName,
quizName,
"Quiz"
),
getQuizzes: async (courseName: string, moduleName: string) =>
await courseItemFileStorageService.getItems(courseName, moduleName, "Quiz"),
async updateQuiz({
courseName,
moduleName,
quizName,
quiz,
}: {
courseName: string;
moduleName: string;
quizName: string;
quiz: LocalQuiz;
}) {
const courseDirectory = await getCoursePathByName(courseName);
const folder = path.join(courseDirectory, moduleName, "quizzes");
await fs.mkdir(folder, { recursive: true });
const filePath = path.join(
courseDirectory,
moduleName,
"quizzes",
quizName + ".md"
);
const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
console.log(`Saving quiz ${filePath}`);
await fs.writeFile(filePath, quizMarkdown);
},
async delete({
courseName,
moduleName,
quizName,
}: {
courseName: string;
moduleName: string;
quizName: string;
}) {
const courseDirectory = await getCoursePathByName(courseName);
const filePath = path.join(
courseDirectory,
moduleName,
"quizzes",
quizName + ".md"
);
console.log("removing quiz", filePath);
await fs.unlink(filePath);
},
};

View File

@@ -1,116 +0,0 @@
import {
LocalCourseSettings,
localCourseYamlUtils,
} from "@/models/local/localCourseSettings";
import { promises as fs } from "fs";
import path from "path";
import { basePath, directoryOrFileExists } from "./utils/fileSystemUtils";
import { AssignmentSubmissionType } from "@/features/local/assignments/models/assignmentSubmissionType";
import {
getCoursePathByName,
getGlobalSettings,
} from "./globalSettingsFileStorageService";
import { GlobalSettingsCourse } from "@/models/local/globalSettings";
const getCourseSettings = async (
course: GlobalSettingsCourse
): Promise<LocalCourseSettings> => {
const courseDirectory = await getCoursePathByName(course.name);
const settingsPath = path.join(courseDirectory, "settings.yml");
if (!(await directoryOrFileExists(settingsPath))) {
const errorMessage = `could not find settings for ${course.name}, settings file ${settingsPath}`;
console.log(errorMessage);
throw new Error(errorMessage);
}
const settingsString = await fs.readFile(settingsPath, "utf-8");
const settingsFromFile =
localCourseYamlUtils.parseSettingYaml(settingsString);
const settings: LocalCourseSettings = populateDefaultValues(settingsFromFile);
return { ...settings, name: course.name };
};
const populateDefaultValues = (settingsFromFile: LocalCourseSettings) => {
const defaultSubmissionType = [
AssignmentSubmissionType.ONLINE_TEXT_ENTRY,
AssignmentSubmissionType.ONLINE_UPLOAD,
];
const defaultFileUploadTypes = ["pdf", "jpg", "jpeg"];
const settings: LocalCourseSettings = {
...settingsFromFile,
defaultAssignmentSubmissionTypes:
settingsFromFile.defaultAssignmentSubmissionTypes ||
defaultSubmissionType,
defaultFileUploadTypes:
settingsFromFile.defaultFileUploadTypes || defaultFileUploadTypes,
holidays: Array.isArray(settingsFromFile.holidays)
? settingsFromFile.holidays
: [],
assets: Array.isArray(settingsFromFile.assets)
? settingsFromFile.assets
: [],
};
return settings;
};
export const settingsFileStorageService = {
getCourseSettings,
async getAllCoursesSettings() {
const globalSettings = await getGlobalSettings();
// const courses = await getCourseNames();
const courses = globalSettings.courses;
const courseSettings = await Promise.all(
courses.map(async (c) => await getCourseSettings(c))
);
return courseSettings;
},
async updateCourseSettings(
courseName: string,
settings: LocalCourseSettings
) {
const courseDirectory = await getCoursePathByName(courseName);
const settingsPath = path.join(courseDirectory, "settings.yml");
const { name: _, ...settingsWithoutName } = settings;
const settingsMarkdown =
localCourseYamlUtils.settingsToYaml(settingsWithoutName);
console.log(`Saving settings ${settingsPath}`);
await fs.writeFile(settingsPath, settingsMarkdown);
},
async createCourseSettings(settings: LocalCourseSettings, directory: string) {
const courseDirectory = path.join(basePath, directory);
if (await directoryOrFileExists(courseDirectory)) {
throw new Error(
`Course path "${courseDirectory}" already exists. Create course in a new folder.`
);
}
await fs.mkdir(courseDirectory, { recursive: true });
const settingsPath = path.join(courseDirectory, "settings.yml");
const { name: _, ...settingsWithoutName } = settings;
const settingsMarkdown =
localCourseYamlUtils.settingsToYaml(settingsWithoutName);
console.log(`Saving settings ${settingsPath}`);
await fs.writeFile(settingsPath, settingsMarkdown);
},
async folderIsCourse(folderPath: string) {
const settingsPath = path.join(basePath, folderPath, "settings.yml");
if (!(await directoryOrFileExists(settingsPath))) {
return false;
}
return true;
},
};

View File

@@ -1,6 +1,6 @@
import { getWeekNumber } from "@/app/course/[courseName]/calendar/calendarMonthUtils";
import { extractLabelValue } from "@/features/local/assignments/models/utils/markdownUtils";
import { Lecture } from "@/models/local/lecture";
import { Lecture } from "@/features/local/lectures/lectureModel";
import { getDateFromStringOrThrow } from "@/models/local/utils/timeUtils";
export function parseLecture(fileContent: string): Lecture {

View File

@@ -1,8 +1,8 @@
"use client";
import { marked } from "marked";
import DOMPurify from "isomorphic-dompurify";
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
import markedKatex from "marked-katex-extension";
import { LocalCourseSettings } from "@/features/local/course/localCourseSettings";
const mermaidExtension = {
name: "mermaid",

View File

@@ -4,11 +4,11 @@ import { assignmentRouter } from "../../../features/local/assignments/assignment
import { canvasFileRouter } from "./canvasFileRouter";
import { directoriesRouter } from "./directoriesRouter";
import { globalSettingsRouter } from "./globalSettingsRouter";
import { lectureRouter } from "./lectureRouter";
import { moduleRouter } from "./moduleRouter";
import { lectureRouter } from "../../../features/local/lectures/lectureRouter";
import { pageRouter } from "../../../features/local/pages/pageRouter";
import { quizRouter } from "./quizRouter";
import { quizRouter } from "../../../features/local/quizzes/quizRouter";
import { settingsRouter } from "./settingsRouter";
import { moduleRouter } from "@/features/local/modules/moduleRouter";
export const trpcAppRouter = router({
assignment: assignmentRouter,

View File

@@ -1,51 +0,0 @@
import { z } from "zod";
import publicProcedure from "../procedures/public";
import { router } from "../trpcSetup";
import {
deleteLecture,
getLectures,
updateLecture,
} from "@/services/fileStorage/lectureFileStorageService";
import { zodLecture } from "@/models/local/lecture";
import { zodLocalCourseSettings } from "@/models/local/localCourseSettings";
export const lectureRouter = router({
getLectures: publicProcedure
.input(
z.object({
courseName: z.string(),
})
)
.query(async ({ input: { courseName } }) => {
return await getLectures(courseName);
}),
updateLecture: publicProcedure
.input(
z.object({
courseName: z.string(),
lecture: zodLecture,
previousDay: z.string().optional(),
settings: zodLocalCourseSettings,
})
)
.mutation(
async ({ input: { courseName, settings, lecture, previousDay } }) => {
await updateLecture(courseName, settings, lecture);
if (previousDay && previousDay !== lecture.date) {
await deleteLecture(courseName, settings, previousDay);
}
}
),
deleteLecture: publicProcedure
.input(
z.object({
courseName: z.string(),
lectureDay: z.string(),
settings: zodLocalCourseSettings,
})
)
.mutation(async ({ input: { courseName, settings, lectureDay } }) => {
await deleteLecture(courseName, settings, lectureDay);
}),
});

View File

@@ -1,26 +0,0 @@
import { z } from "zod";
import publicProcedure from "../procedures/public";
import { router } from "../trpcSetup";
import { fileStorageService } from "@/services/fileStorage/fileStorageService";
export const moduleRouter = router({
getModuleNames: publicProcedure
.input(
z.object({
courseName: z.string(),
})
)
.query(async ({ input: { courseName } }) => {
return await fileStorageService.modules.getModuleNames(courseName);
}),
createModule: publicProcedure
.input(
z.object({
courseName: z.string(),
moduleName: z.string(),
})
)
.mutation(async ({ input: { courseName, moduleName } }) => {
await fileStorageService.modules.createModule(courseName, moduleName);
}),
});

View File

@@ -1,110 +0,0 @@
import publicProcedure from "../procedures/public";
import { z } from "zod";
import { router } from "../trpcSetup";
import { fileStorageService } from "@/services/fileStorage/fileStorageService";
import { zodLocalQuiz } from "@/features/local/quizzes/models/localQuiz";
export const quizRouter = router({
getQuiz: publicProcedure
.input(
z.object({
courseName: z.string(),
moduleName: z.string(),
quizName: z.string(),
})
)
.query(async ({ input: { courseName, moduleName, quizName } }) => {
return await fileStorageService.quizzes.getQuiz(
courseName,
moduleName,
quizName
);
}),
getAllQuizzes: publicProcedure
.input(
z.object({
courseName: z.string(),
moduleName: z.string(),
})
)
.query(async ({ input: { courseName, moduleName } }) => {
return await fileStorageService.quizzes.getQuizzes(
courseName,
moduleName
);
}),
createQuiz: publicProcedure
.input(
z.object({
courseName: z.string(),
moduleName: z.string(),
quizName: z.string(),
quiz: zodLocalQuiz,
})
)
.mutation(async ({ input: { courseName, moduleName, quizName, quiz } }) => {
await fileStorageService.quizzes.updateQuiz({
courseName,
moduleName,
quizName,
quiz,
});
}),
updateQuiz: publicProcedure
.input(
z.object({
courseName: z.string(),
moduleName: z.string(),
previousModuleName: z.string(),
previousQuizName: z.string(),
quizName: z.string(),
quiz: zodLocalQuiz,
})
)
.mutation(
async ({
input: {
courseName,
moduleName,
quizName,
quiz,
previousModuleName,
previousQuizName,
},
}) => {
await fileStorageService.quizzes.updateQuiz({
courseName,
moduleName,
quizName,
quiz,
});
if (
quizName !== previousQuizName ||
moduleName !== previousModuleName
) {
await fileStorageService.quizzes.delete({
courseName,
moduleName: previousModuleName,
quizName: previousQuizName,
});
}
}
),
deleteQuiz: publicProcedure
.input(
z.object({
courseName: z.string(),
moduleName: z.string(),
quizName: z.string(),
})
)
.mutation(async ({ input: { courseName, moduleName, quizName } }) => {
await fileStorageService.quizzes.delete({
courseName,
moduleName,
quizName,
});
}),
});

View File

@@ -2,11 +2,6 @@ import publicProcedure from "../procedures/public";
import { z } from "zod";
import { router } from "../trpcSetup";
import { fileStorageService } from "@/services/fileStorage/fileStorageService";
import { zodLocalCourseSettings } from "@/models/local/localCourseSettings";
import {
getLectures,
updateLecture,
} from "@/services/fileStorage/lectureFileStorageService";
import {
prepAssignmentForNewSemester,
prepLectureForNewSemester,
@@ -17,6 +12,8 @@ import {
getGlobalSettings,
updateGlobalSettings,
} from "@/services/fileStorage/globalSettingsFileStorageService";
import { getLectures, updateLecture } from "@/features/local/lectures/lectureFileStorageService";
import { zodLocalCourseSettings } from "@/features/local/course/localCourseSettings";
export const settingsRouter = router({
allCoursesSettings: publicProcedure.query(async () => {

View File

@@ -1,10 +1,7 @@
import { describe, it, expect, beforeEach } from "vitest";
import { promises as fs } from "fs";
import {
DayOfWeek,
LocalCourseSettings,
} from "@/models/local/localCourseSettings";
import { fileStorageService } from "../fileStorage/fileStorageService";
import { LocalCourseSettings, DayOfWeek } from "@/features/local/course/localCourseSettings";
describe("FileStorageTests", () => {
beforeEach(async () => {

View File

@@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import { lectureToString } from "../fileStorage/utils/lectureUtils";
import { parseLecture } from "../fileStorage/utils/lectureUtils";
import { Lecture } from "@/models/local/lecture";
import { Lecture } from "@/features/local/lectures/lectureModel";
describe("can parse and stringify lectures", () => {
it("can parse lecture", () => {