mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
more code refactor to colocate feature code
This commit is contained in:
@@ -1,9 +1,11 @@
|
|||||||
import { assignmentMarkdownSerializer } from "@/features/local/assignments/models/utils/assignmentMarkdownSerializer";
|
import { assignmentMarkdownSerializer } from "@/features/local/assignments/models/utils/assignmentMarkdownSerializer";
|
||||||
import { groupByStartDate } from "@/features/local/utils/timeUtils";
|
import { groupByStartDate } from "@/features/local/utils/timeUtils";
|
||||||
import { fileStorageService } from "@/features/local/utils/fileStorageService";
|
|
||||||
import { createMcpHandler } from "mcp-handler";
|
import { createMcpHandler } from "mcp-handler";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { githubClassroomUrlPrompt } from "./github-classroom-prompt";
|
import { githubClassroomUrlPrompt } from "./github-classroom-prompt";
|
||||||
|
import { courseItemFileStorageService } from "@/features/local/course/courseItemFileStorageService";
|
||||||
|
import { fileStorageService } from "@/features/local/utils/fileStorageService";
|
||||||
|
import { getModuleNamesFromFiles } from "@/features/local/modules/moduleRouter";
|
||||||
|
|
||||||
const handler = createMcpHandler(
|
const handler = createMcpHandler(
|
||||||
(server) => {
|
(server) => {
|
||||||
@@ -41,17 +43,17 @@ const handler = createMcpHandler(
|
|||||||
courseName: z.string(),
|
courseName: z.string(),
|
||||||
},
|
},
|
||||||
async ({ courseName }) => {
|
async ({ courseName }) => {
|
||||||
const modules = await fileStorageService.modules.getModuleNames(
|
const modules = await getModuleNamesFromFiles(
|
||||||
courseName
|
courseName
|
||||||
);
|
);
|
||||||
const assignments = (
|
const assignments = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
modules.map(async (moduleName) => {
|
modules.map(async (moduleName) => {
|
||||||
const assignments =
|
const assignments = await courseItemFileStorageService.getItems({
|
||||||
await fileStorageService.assignments.getAssignments(
|
courseName,
|
||||||
courseName,
|
moduleName,
|
||||||
moduleName
|
type: "Assignment",
|
||||||
);
|
});
|
||||||
return assignments.map((assignment) => ({
|
return assignments.map((assignment) => ({
|
||||||
assignmentName: assignment.name,
|
assignmentName: assignment.name,
|
||||||
moduleName,
|
moduleName,
|
||||||
@@ -100,11 +102,12 @@ const handler = createMcpHandler(
|
|||||||
"courseName, moduleName, and assignmentName must be strings"
|
"courseName, moduleName, and assignmentName must be strings"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const assignment = await fileStorageService.assignments.getAssignment(
|
const assignment = await courseItemFileStorageService.getItem({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
assignmentName
|
name: assignmentName,
|
||||||
);
|
type: "Assignment",
|
||||||
|
});
|
||||||
|
|
||||||
console.log("mcp assignment", assignment);
|
console.log("mcp assignment", assignment);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
import publicProcedure from "../../../services/serverFunctions/publicProcedure";
|
import publicProcedure from "../../../services/serverFunctions/publicProcedure";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { router } from "../../../services/serverFunctions/trpcSetup";
|
import { router } from "../../../services/serverFunctions/trpcSetup";
|
||||||
import { fileStorageService } from "@/features/local/utils/fileStorageService";
|
import {
|
||||||
import { zodLocalAssignment } from "@/features/local/assignments/models/localAssignment";
|
LocalAssignment,
|
||||||
|
zodLocalAssignment,
|
||||||
|
} from "@/features/local/assignments/models/localAssignment";
|
||||||
|
import path from "path";
|
||||||
|
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
||||||
|
import { promises as fs } from "fs";
|
||||||
|
import { courseItemFileStorageService } from "../course/courseItemFileStorageService";
|
||||||
|
import { assignmentMarkdownSerializer } from "./models/utils/assignmentMarkdownSerializer";
|
||||||
|
|
||||||
export const assignmentRouter = router({
|
export const assignmentRouter = router({
|
||||||
getAssignment: publicProcedure
|
getAssignment: publicProcedure
|
||||||
@@ -14,13 +21,12 @@ export const assignmentRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.query(async ({ input: { courseName, moduleName, assignmentName } }) => {
|
.query(async ({ input: { courseName, moduleName, assignmentName } }) => {
|
||||||
const assignment = await fileStorageService.assignments.getAssignment(
|
return await courseItemFileStorageService.getItem({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
assignmentName
|
name: assignmentName,
|
||||||
);
|
type: "Assignment",
|
||||||
// console.log(assignment);
|
});
|
||||||
return assignment;
|
|
||||||
}),
|
}),
|
||||||
getAllAssignments: publicProcedure
|
getAllAssignments: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -30,10 +36,11 @@ export const assignmentRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.query(async ({ input: { courseName, moduleName } }) => {
|
.query(async ({ input: { courseName, moduleName } }) => {
|
||||||
const assignments = await fileStorageService.assignments.getAssignments(
|
const assignments = await courseItemFileStorageService.getItems({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName
|
moduleName,
|
||||||
);
|
type: "Assignment",
|
||||||
|
});
|
||||||
return assignments;
|
return assignments;
|
||||||
}),
|
}),
|
||||||
createAssignment: publicProcedure
|
createAssignment: publicProcedure
|
||||||
@@ -49,7 +56,7 @@ export const assignmentRouter = router({
|
|||||||
async ({
|
async ({
|
||||||
input: { courseName, moduleName, assignmentName, assignment },
|
input: { courseName, moduleName, assignmentName, assignment },
|
||||||
}) => {
|
}) => {
|
||||||
await fileStorageService.assignments.updateOrCreateAssignment({
|
await updateOrCreateAssignmentFile({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
assignmentName,
|
assignmentName,
|
||||||
@@ -79,7 +86,7 @@ export const assignmentRouter = router({
|
|||||||
previousAssignmentName,
|
previousAssignmentName,
|
||||||
},
|
},
|
||||||
}) => {
|
}) => {
|
||||||
await fileStorageService.assignments.updateOrCreateAssignment({
|
await updateOrCreateAssignmentFile({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
assignmentName,
|
assignmentName,
|
||||||
@@ -90,7 +97,7 @@ export const assignmentRouter = router({
|
|||||||
assignmentName !== previousAssignmentName ||
|
assignmentName !== previousAssignmentName ||
|
||||||
moduleName !== previousModuleName
|
moduleName !== previousModuleName
|
||||||
) {
|
) {
|
||||||
await fileStorageService.assignments.delete({
|
await deleteAssignment({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName: previousModuleName,
|
moduleName: previousModuleName,
|
||||||
assignmentName: previousAssignmentName,
|
assignmentName: previousAssignmentName,
|
||||||
@@ -107,10 +114,59 @@ export const assignmentRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ input: { courseName, moduleName, assignmentName } }) => {
|
.mutation(async ({ input: { courseName, moduleName, assignmentName } }) => {
|
||||||
await fileStorageService.assignments.delete({
|
await deleteAssignment({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
assignmentName,
|
assignmentName,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export async function updateOrCreateAssignmentFile({
|
||||||
|
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 function deleteAssignment({
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 { promises as fs } from "fs";
|
|
||||||
import { courseItemFileStorageService } from "@/features/local/course/courseItemFileStorageService";
|
|
||||||
import { getCoursePathByName } from "@/features/local/globalSettings/globalSettingsFileStorageService";
|
|
||||||
import { directoryOrFileExists } from "@/features/local/utils/fileSystemUtils";
|
|
||||||
|
|
||||||
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);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -2,10 +2,8 @@ import path from "path";
|
|||||||
import { directoryOrFileExists } from "../utils/fileSystemUtils";
|
import { directoryOrFileExists } from "../utils/fileSystemUtils";
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
import {
|
import {
|
||||||
LocalAssignment,
|
|
||||||
localAssignmentMarkdown,
|
localAssignmentMarkdown,
|
||||||
} from "@/features/local/assignments/models/localAssignment";
|
} from "@/features/local/assignments/models/localAssignment";
|
||||||
import { assignmentMarkdownSerializer } from "@/features/local/assignments/models/utils/assignmentMarkdownSerializer";
|
|
||||||
import {
|
import {
|
||||||
CourseItemReturnType,
|
CourseItemReturnType,
|
||||||
CourseItemType,
|
CourseItemType,
|
||||||
@@ -14,19 +12,20 @@ import {
|
|||||||
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
||||||
import {
|
import {
|
||||||
localPageMarkdownUtils,
|
localPageMarkdownUtils,
|
||||||
LocalCoursePage,
|
|
||||||
} from "@/features/local/pages/localCoursePageModels";
|
} from "@/features/local/pages/localCoursePageModels";
|
||||||
import {
|
import {
|
||||||
LocalQuiz,
|
|
||||||
localQuizMarkdownUtils,
|
localQuizMarkdownUtils,
|
||||||
} from "@/features/local/quizzes/models/localQuiz";
|
} from "@/features/local/quizzes/models/localQuiz";
|
||||||
import { quizMarkdownUtils } from "@/features/local/quizzes/models/utils/quizMarkdownUtils";
|
|
||||||
|
|
||||||
const getItemFileNames = async (
|
const getItemFileNames = async ({
|
||||||
courseName: string,
|
courseName,
|
||||||
moduleName: string,
|
moduleName,
|
||||||
type: CourseItemType
|
type,
|
||||||
) => {
|
}: {
|
||||||
|
courseName: string;
|
||||||
|
moduleName: string;
|
||||||
|
type: CourseItemType;
|
||||||
|
}) => {
|
||||||
const courseDirectory = await getCoursePathByName(courseName);
|
const courseDirectory = await getCoursePathByName(courseName);
|
||||||
const folder = typeToFolder[type];
|
const folder = typeToFolder[type];
|
||||||
const filePath = path.join(courseDirectory, moduleName, folder);
|
const filePath = path.join(courseDirectory, moduleName, folder);
|
||||||
@@ -41,12 +40,17 @@ const getItemFileNames = async (
|
|||||||
return itemFiles.map((f) => f.replace(/\.md$/, ""));
|
return itemFiles.map((f) => f.replace(/\.md$/, ""));
|
||||||
};
|
};
|
||||||
|
|
||||||
const getItem = async <T extends CourseItemType>(
|
const getItem = async <T extends CourseItemType>({
|
||||||
courseName: string,
|
courseName,
|
||||||
moduleName: string,
|
moduleName,
|
||||||
name: string,
|
name,
|
||||||
type: T
|
type,
|
||||||
): Promise<CourseItemReturnType<T>> => {
|
}: {
|
||||||
|
courseName: string;
|
||||||
|
moduleName: string;
|
||||||
|
name: string;
|
||||||
|
type: T;
|
||||||
|
}): Promise<CourseItemReturnType<T>> => {
|
||||||
const courseDirectory = await getCoursePathByName(courseName);
|
const courseDirectory = await getCoursePathByName(courseName);
|
||||||
const folder = typeToFolder[type];
|
const folder = typeToFolder[type];
|
||||||
const filePath = path.join(courseDirectory, moduleName, folder, name + ".md");
|
const filePath = path.join(courseDirectory, moduleName, folder, name + ".md");
|
||||||
@@ -73,17 +77,21 @@ const getItem = async <T extends CourseItemType>(
|
|||||||
|
|
||||||
export const courseItemFileStorageService = {
|
export const courseItemFileStorageService = {
|
||||||
getItem,
|
getItem,
|
||||||
getItems: async <T extends CourseItemType>(
|
getItems: async <T extends CourseItemType>({
|
||||||
courseName: string,
|
courseName,
|
||||||
moduleName: string,
|
moduleName,
|
||||||
type: T
|
type,
|
||||||
): Promise<CourseItemReturnType<T>[]> => {
|
}: {
|
||||||
const fileNames = await getItemFileNames(courseName, moduleName, type);
|
courseName: string;
|
||||||
|
moduleName: string;
|
||||||
|
type: T;
|
||||||
|
}): Promise<CourseItemReturnType<T>[]> => {
|
||||||
|
const fileNames = await getItemFileNames({ courseName, moduleName, type });
|
||||||
const items = (
|
const items = (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
fileNames.map(async (name) => {
|
fileNames.map(async (name) => {
|
||||||
try {
|
try {
|
||||||
const item = await getItem(courseName, moduleName, name, type);
|
const item = await getItem({ courseName, moduleName, name, type });
|
||||||
return item;
|
return item;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
@@ -93,42 +101,4 @@ export const courseItemFileStorageService = {
|
|||||||
).filter((a) => a !== null);
|
).filter((a) => a !== null);
|
||||||
return items;
|
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);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,10 +13,18 @@ import {
|
|||||||
updateGlobalSettings,
|
updateGlobalSettings,
|
||||||
} from "@/features/local/globalSettings/globalSettingsFileStorageService";
|
} from "@/features/local/globalSettings/globalSettingsFileStorageService";
|
||||||
import {
|
import {
|
||||||
getLectures,
|
LocalCourseSettings,
|
||||||
updateLecture,
|
zodLocalCourseSettings,
|
||||||
} from "@/features/local/lectures/lectureFileStorageService";
|
} from "@/features/local/course/localCourseSettings";
|
||||||
import { zodLocalCourseSettings } from "@/features/local/course/localCourseSettings";
|
import { courseItemFileStorageService } from "./courseItemFileStorageService";
|
||||||
|
import { updateOrCreateAssignmentFile } from "../assignments/assignmentRouter";
|
||||||
|
import { updateQuizFile } from "../quizzes/quizRouter";
|
||||||
|
import { updatePageFile } from "../pages/pageRouter";
|
||||||
|
import { getLectures, updateLecture } from "../lectures/lectureRouter";
|
||||||
|
import {
|
||||||
|
createModuleFile,
|
||||||
|
getModuleNamesFromFiles,
|
||||||
|
} from "../modules/moduleRouter";
|
||||||
|
|
||||||
export const settingsRouter = router({
|
export const settingsRouter = router({
|
||||||
allCoursesSettings: publicProcedure.query(async () => {
|
allCoursesSettings: publicProcedure.query(async () => {
|
||||||
@@ -71,90 +79,7 @@ export const settingsRouter = router({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (settingsFromCourseToImport) {
|
if (settingsFromCourseToImport) {
|
||||||
const oldCourseName = settingsFromCourseToImport.name;
|
await migrateCourseContent(settingsFromCourseToImport, settings);
|
||||||
const newCourseName = settings.name;
|
|
||||||
const oldModules = await fileStorageService.modules.getModuleNames(
|
|
||||||
oldCourseName
|
|
||||||
);
|
|
||||||
await Promise.all(
|
|
||||||
oldModules.map(async (moduleName) => {
|
|
||||||
await fileStorageService.modules.createModule(
|
|
||||||
newCourseName,
|
|
||||||
moduleName
|
|
||||||
);
|
|
||||||
|
|
||||||
const [oldAssignments, oldQuizzes, oldPages, oldLecturesByWeek] =
|
|
||||||
await Promise.all([
|
|
||||||
fileStorageService.assignments.getAssignments(
|
|
||||||
oldCourseName,
|
|
||||||
moduleName
|
|
||||||
),
|
|
||||||
await fileStorageService.quizzes.getQuizzes(
|
|
||||||
oldCourseName,
|
|
||||||
moduleName
|
|
||||||
),
|
|
||||||
await fileStorageService.pages.getPages(
|
|
||||||
oldCourseName,
|
|
||||||
moduleName
|
|
||||||
),
|
|
||||||
await getLectures(oldCourseName),
|
|
||||||
]);
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
...oldAssignments.map(async (oldAssignment) => {
|
|
||||||
const newAssignment = prepAssignmentForNewSemester(
|
|
||||||
oldAssignment,
|
|
||||||
settingsFromCourseToImport.startDate,
|
|
||||||
settings.startDate
|
|
||||||
);
|
|
||||||
await fileStorageService.assignments.updateOrCreateAssignment(
|
|
||||||
{
|
|
||||||
courseName: newCourseName,
|
|
||||||
moduleName,
|
|
||||||
assignmentName: newAssignment.name,
|
|
||||||
assignment: newAssignment,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
...oldQuizzes.map(async (oldQuiz) => {
|
|
||||||
const newQuiz = prepQuizForNewSemester(
|
|
||||||
oldQuiz,
|
|
||||||
settingsFromCourseToImport.startDate,
|
|
||||||
settings.startDate
|
|
||||||
);
|
|
||||||
await fileStorageService.quizzes.updateQuiz({
|
|
||||||
courseName: newCourseName,
|
|
||||||
moduleName,
|
|
||||||
quizName: newQuiz.name,
|
|
||||||
quiz: newQuiz,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
...oldPages.map(async (oldPage) => {
|
|
||||||
const newPage = prepPageForNewSemester(
|
|
||||||
oldPage,
|
|
||||||
settingsFromCourseToImport.startDate,
|
|
||||||
settings.startDate
|
|
||||||
);
|
|
||||||
await fileStorageService.pages.updatePage({
|
|
||||||
courseName: newCourseName,
|
|
||||||
moduleName,
|
|
||||||
pageName: newPage.name,
|
|
||||||
page: newPage,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
...oldLecturesByWeek.flatMap(async (oldLectureByWeek) =>
|
|
||||||
oldLectureByWeek.lectures.map(async (oldLecture) => {
|
|
||||||
const newLecture = prepLectureForNewSemester(
|
|
||||||
oldLecture,
|
|
||||||
settingsFromCourseToImport.startDate,
|
|
||||||
settings.startDate
|
|
||||||
);
|
|
||||||
await updateLecture(newCourseName, settings, newLecture);
|
|
||||||
})
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@@ -171,3 +96,96 @@ export const settingsRouter = router({
|
|||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function migrateCourseContent(
|
||||||
|
settingsFromCourseToImport: LocalCourseSettings,
|
||||||
|
settings: LocalCourseSettings
|
||||||
|
) {
|
||||||
|
const oldCourseName = settingsFromCourseToImport.name;
|
||||||
|
const newCourseName = settings.name;
|
||||||
|
const oldModules = await getModuleNamesFromFiles(oldCourseName);
|
||||||
|
await Promise.all(
|
||||||
|
oldModules.map(async (moduleName) => {
|
||||||
|
await createModuleFile(newCourseName, moduleName);
|
||||||
|
const [oldAssignments, oldQuizzes, oldPages, oldLecturesByWeek] =
|
||||||
|
await Promise.all([
|
||||||
|
await courseItemFileStorageService.getItems({
|
||||||
|
courseName: oldCourseName,
|
||||||
|
moduleName,
|
||||||
|
type: "Assignment",
|
||||||
|
}),
|
||||||
|
await courseItemFileStorageService.getItems({
|
||||||
|
courseName: oldCourseName,
|
||||||
|
moduleName,
|
||||||
|
type: "Quiz",
|
||||||
|
}),
|
||||||
|
await courseItemFileStorageService.getItems({
|
||||||
|
courseName: oldCourseName,
|
||||||
|
moduleName,
|
||||||
|
type: "Page",
|
||||||
|
}),
|
||||||
|
await getLectures(oldCourseName),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const updateAssignmentPromises = oldAssignments.map(
|
||||||
|
async (oldAssignment) => {
|
||||||
|
const newAssignment = prepAssignmentForNewSemester(
|
||||||
|
oldAssignment,
|
||||||
|
settingsFromCourseToImport.startDate,
|
||||||
|
settings.startDate
|
||||||
|
);
|
||||||
|
await updateOrCreateAssignmentFile({
|
||||||
|
courseName: newCourseName,
|
||||||
|
moduleName,
|
||||||
|
assignmentName: newAssignment.name,
|
||||||
|
assignment: newAssignment,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const updateQuizzesPromises = oldQuizzes.map(async (oldQuiz) => {
|
||||||
|
const newQuiz = prepQuizForNewSemester(
|
||||||
|
oldQuiz,
|
||||||
|
settingsFromCourseToImport.startDate,
|
||||||
|
settings.startDate
|
||||||
|
);
|
||||||
|
await updateQuizFile({
|
||||||
|
courseName: newCourseName,
|
||||||
|
moduleName,
|
||||||
|
quizName: newQuiz.name,
|
||||||
|
quiz: newQuiz,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const updatePagesPromises = oldPages.map(async (oldPage) => {
|
||||||
|
const newPage = prepPageForNewSemester(
|
||||||
|
oldPage,
|
||||||
|
settingsFromCourseToImport.startDate,
|
||||||
|
settings.startDate
|
||||||
|
);
|
||||||
|
await updatePageFile({
|
||||||
|
courseName: newCourseName,
|
||||||
|
moduleName,
|
||||||
|
pageName: newPage.name,
|
||||||
|
page: newPage,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const updateLecturePromises = oldLecturesByWeek.flatMap(
|
||||||
|
async (oldLectureByWeek) =>
|
||||||
|
oldLectureByWeek.lectures.map(async (oldLecture) => {
|
||||||
|
const newLecture = prepLectureForNewSemester(
|
||||||
|
oldLecture,
|
||||||
|
settingsFromCourseToImport.startDate,
|
||||||
|
settings.startDate
|
||||||
|
);
|
||||||
|
await updateLecture(newCourseName, settings, newLecture);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
...updateAssignmentPromises,
|
||||||
|
...updateQuizzesPromises,
|
||||||
|
...updatePagesPromises,
|
||||||
|
...updateLecturePromises,
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
import path from "path";
|
|
||||||
import fs from "fs/promises";
|
|
||||||
import { Lecture } from "@/features/local/lectures/lectureModel";
|
|
||||||
import { getDateFromStringOrThrow } from "@/features/local/utils/timeUtils";
|
|
||||||
import { getCoursePathByName } from "@/features/local/globalSettings/globalSettingsFileStorageService";
|
|
||||||
import {
|
|
||||||
lectureFolderName,
|
|
||||||
parseLecture,
|
|
||||||
getLectureWeekName,
|
|
||||||
lectureToString,
|
|
||||||
} from "@/features/local/lectures/lectureUtils";
|
|
||||||
import {
|
|
||||||
LocalCourseSettings,
|
|
||||||
getDayOfWeek,
|
|
||||||
} from "../course/localCourseSettings";
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,13 +1,22 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import publicProcedure from "../../../services/serverFunctions/publicProcedure";
|
import publicProcedure from "../../../services/serverFunctions/publicProcedure";
|
||||||
import { router } from "../../../services/serverFunctions/trpcSetup";
|
import { router } from "../../../services/serverFunctions/trpcSetup";
|
||||||
import { zodLecture } from "@/features/local/lectures/lectureModel";
|
import { Lecture, zodLecture } from "@/features/local/lectures/lectureModel";
|
||||||
import {
|
import {
|
||||||
getLectures,
|
getDayOfWeek,
|
||||||
updateLecture,
|
LocalCourseSettings,
|
||||||
deleteLecture,
|
zodLocalCourseSettings,
|
||||||
} from "./lectureFileStorageService";
|
} from "../course/localCourseSettings";
|
||||||
import { zodLocalCourseSettings } from "../course/localCourseSettings";
|
import path from "path";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
||||||
|
import { getDateFromStringOrThrow } from "../utils/timeUtils";
|
||||||
|
import {
|
||||||
|
lectureFolderName,
|
||||||
|
parseLecture,
|
||||||
|
getLectureWeekName,
|
||||||
|
lectureToString,
|
||||||
|
} from "./lectureUtils";
|
||||||
|
|
||||||
export const lectureRouter = router({
|
export const lectureRouter = router({
|
||||||
getLectures: publicProcedure
|
getLectures: publicProcedure
|
||||||
@@ -49,3 +58,112 @@ export const lectureRouter = router({
|
|||||||
await deleteLecture(courseName, settings, lectureDay);
|
await deleteLecture(courseName, settings, lectureDay);
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import { promises as fs } from "fs";
|
|
||||||
import { lectureFolderName } from "../lectures/lectureUtils";
|
|
||||||
import { getCoursePathByName } from "../globalSettings/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 });
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { fileStorageService } from "@/features/local/utils/fileStorageService";
|
|
||||||
import { router } from "@/services/serverFunctions/trpcSetup";
|
import { router } from "@/services/serverFunctions/trpcSetup";
|
||||||
import publicProcedure from "@/services/serverFunctions/publicProcedure";
|
import publicProcedure from "@/services/serverFunctions/publicProcedure";
|
||||||
|
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
||||||
|
import { promises as fs } from "fs";
|
||||||
|
import { lectureFolderName } from "../lectures/lectureUtils";
|
||||||
|
|
||||||
export const moduleRouter = router({
|
export const moduleRouter = router({
|
||||||
getModuleNames: publicProcedure
|
getModuleNames: publicProcedure
|
||||||
@@ -11,7 +13,7 @@ export const moduleRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.query(async ({ input: { courseName } }) => {
|
.query(async ({ input: { courseName } }) => {
|
||||||
return await fileStorageService.modules.getModuleNames(courseName);
|
return await getModuleNamesFromFiles(courseName);
|
||||||
}),
|
}),
|
||||||
createModule: publicProcedure
|
createModule: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -21,6 +23,27 @@ export const moduleRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ input: { courseName, moduleName } }) => {
|
.mutation(async ({ input: { courseName, moduleName } }) => {
|
||||||
await fileStorageService.modules.createModule(courseName, moduleName);
|
await createModuleFile(courseName, moduleName);
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export async function createModuleFile(courseName: string, moduleName: string) {
|
||||||
|
const courseDirectory = await getCoursePathByName(courseName);
|
||||||
|
|
||||||
|
await fs.mkdir(courseDirectory + "/" + moduleName, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getModuleNamesFromFiles(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));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
import { promises as fs } from "fs";
|
|
||||||
import path from "path";
|
|
||||||
import { courseItemFileStorageService } from "../course/courseItemFileStorageService";
|
|
||||||
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
|
||||||
import {
|
|
||||||
LocalCoursePage,
|
|
||||||
localPageMarkdownUtils,
|
|
||||||
} from "@/features/local/pages/localCoursePageModels";
|
|
||||||
|
|
||||||
export const pageFileStorageService = {
|
|
||||||
getPage: async (courseName: string, moduleName: string, name: string) =>
|
|
||||||
await courseItemFileStorageService.getItem(
|
|
||||||
courseName,
|
|
||||||
moduleName,
|
|
||||||
name,
|
|
||||||
"Page"
|
|
||||||
),
|
|
||||||
getPages: async (courseName: string, moduleName: string) =>
|
|
||||||
await courseItemFileStorageService.getItems(courseName, moduleName, "Page"),
|
|
||||||
|
|
||||||
async updatePage({
|
|
||||||
courseName,
|
|
||||||
moduleName,
|
|
||||||
pageName,
|
|
||||||
page,
|
|
||||||
}: {
|
|
||||||
courseName: string;
|
|
||||||
moduleName: string;
|
|
||||||
pageName: string;
|
|
||||||
page: LocalCoursePage;
|
|
||||||
}) {
|
|
||||||
const courseDirectory = await getCoursePathByName(courseName);
|
|
||||||
const folder = path.join(courseDirectory, moduleName, "pages");
|
|
||||||
await fs.mkdir(folder, { recursive: true });
|
|
||||||
|
|
||||||
const filePath = path.join(
|
|
||||||
courseDirectory,
|
|
||||||
moduleName,
|
|
||||||
"pages",
|
|
||||||
pageName + ".md"
|
|
||||||
);
|
|
||||||
|
|
||||||
const pageMarkdown = localPageMarkdownUtils.toMarkdown(page);
|
|
||||||
console.log(`Saving page ${filePath}`);
|
|
||||||
await fs.writeFile(filePath, pageMarkdown);
|
|
||||||
},
|
|
||||||
async delete({
|
|
||||||
courseName,
|
|
||||||
moduleName,
|
|
||||||
pageName,
|
|
||||||
}: {
|
|
||||||
courseName: string;
|
|
||||||
moduleName: string;
|
|
||||||
pageName: string;
|
|
||||||
}) {
|
|
||||||
const courseDirectory = await getCoursePathByName(courseName);
|
|
||||||
const filePath = path.join(
|
|
||||||
courseDirectory,
|
|
||||||
moduleName,
|
|
||||||
"pages",
|
|
||||||
pageName + ".md"
|
|
||||||
);
|
|
||||||
console.log("removing page", filePath);
|
|
||||||
await fs.unlink(filePath);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
import publicProcedure from "../../../services/serverFunctions/publicProcedure";
|
import publicProcedure from "../../../services/serverFunctions/publicProcedure";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { router } from "../../../services/serverFunctions/trpcSetup";
|
import { router } from "../../../services/serverFunctions/trpcSetup";
|
||||||
import { fileStorageService } from "@/features/local/utils/fileStorageService";
|
import { LocalCoursePage, localPageMarkdownUtils, zodLocalCoursePage } from "@/features/local/pages/localCoursePageModels";
|
||||||
import { zodLocalCoursePage } from "@/features/local/pages/localCoursePageModels";
|
import { courseItemFileStorageService } from "../course/courseItemFileStorageService";
|
||||||
|
import { promises as fs } from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
||||||
|
|
||||||
export const pageRouter = router({
|
export const pageRouter = router({
|
||||||
getPage: publicProcedure
|
getPage: publicProcedure
|
||||||
@@ -14,11 +17,12 @@ export const pageRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.query(async ({ input: { courseName, moduleName, pageName } }) => {
|
.query(async ({ input: { courseName, moduleName, pageName } }) => {
|
||||||
return await fileStorageService.pages.getPage(
|
return await courseItemFileStorageService.getItem({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
pageName
|
name: pageName,
|
||||||
);
|
type: "Page",
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getAllPages: publicProcedure
|
getAllPages: publicProcedure
|
||||||
@@ -29,7 +33,11 @@ export const pageRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.query(async ({ input: { courseName, moduleName } }) => {
|
.query(async ({ input: { courseName, moduleName } }) => {
|
||||||
return await fileStorageService.pages.getPages(courseName, moduleName);
|
return await courseItemFileStorageService.getItems({
|
||||||
|
courseName,
|
||||||
|
moduleName,
|
||||||
|
type: "Page",
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
createPage: publicProcedure
|
createPage: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -41,7 +49,7 @@ export const pageRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ input: { courseName, moduleName, pageName, page } }) => {
|
.mutation(async ({ input: { courseName, moduleName, pageName, page } }) => {
|
||||||
await fileStorageService.pages.updatePage({
|
await updatePageFile({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
pageName,
|
pageName,
|
||||||
@@ -70,7 +78,7 @@ export const pageRouter = router({
|
|||||||
previousPageName,
|
previousPageName,
|
||||||
},
|
},
|
||||||
}) => {
|
}) => {
|
||||||
await fileStorageService.pages.updatePage({
|
await updatePageFile({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
pageName,
|
pageName,
|
||||||
@@ -81,7 +89,7 @@ export const pageRouter = router({
|
|||||||
pageName !== previousPageName ||
|
pageName !== previousPageName ||
|
||||||
moduleName !== previousModuleName
|
moduleName !== previousModuleName
|
||||||
) {
|
) {
|
||||||
await fileStorageService.pages.delete({
|
await deletePageFile({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName: previousModuleName,
|
moduleName: previousModuleName,
|
||||||
pageName: previousPageName,
|
pageName: previousPageName,
|
||||||
@@ -98,10 +106,56 @@ export const pageRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ input: { courseName, moduleName, pageName } }) => {
|
.mutation(async ({ input: { courseName, moduleName, pageName } }) => {
|
||||||
await fileStorageService.pages.delete({
|
await deletePageFile({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
pageName,
|
pageName,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export async function updatePageFile({
|
||||||
|
courseName,
|
||||||
|
moduleName,
|
||||||
|
pageName,
|
||||||
|
page,
|
||||||
|
}: {
|
||||||
|
courseName: string;
|
||||||
|
moduleName: string;
|
||||||
|
pageName: string;
|
||||||
|
page: LocalCoursePage;
|
||||||
|
}) {
|
||||||
|
const courseDirectory = await getCoursePathByName(courseName);
|
||||||
|
const folder = path.join(courseDirectory, moduleName, "pages");
|
||||||
|
await fs.mkdir(folder, { recursive: true });
|
||||||
|
|
||||||
|
const filePath = path.join(
|
||||||
|
courseDirectory,
|
||||||
|
moduleName,
|
||||||
|
"pages",
|
||||||
|
pageName + ".md"
|
||||||
|
);
|
||||||
|
|
||||||
|
const pageMarkdown = localPageMarkdownUtils.toMarkdown(page);
|
||||||
|
console.log(`Saving page ${filePath}`);
|
||||||
|
await fs.writeFile(filePath, pageMarkdown);
|
||||||
|
}
|
||||||
|
async function deletePageFile({
|
||||||
|
courseName,
|
||||||
|
moduleName,
|
||||||
|
pageName,
|
||||||
|
}: {
|
||||||
|
courseName: string;
|
||||||
|
moduleName: string;
|
||||||
|
pageName: string;
|
||||||
|
}) {
|
||||||
|
const courseDirectory = await getCoursePathByName(courseName);
|
||||||
|
const filePath = path.join(
|
||||||
|
courseDirectory,
|
||||||
|
moduleName,
|
||||||
|
"pages",
|
||||||
|
pageName + ".md"
|
||||||
|
);
|
||||||
|
console.log("removing page", filePath);
|
||||||
|
await fs.unlink(filePath);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import { LocalAssignment } from "../../../features/local/assignments/models/localAssignment";
|
import { LocalAssignment } from "../assignments/models/localAssignment";
|
||||||
import { AssignmentSubmissionType } from "../../../features/local/assignments/models/assignmentSubmissionType";
|
import { AssignmentSubmissionType } from "../assignments/models/assignmentSubmissionType";
|
||||||
import { assignmentMarkdownSerializer } from "../../../features/local/assignments/models/utils/assignmentMarkdownSerializer";
|
import { assignmentMarkdownSerializer } from "../assignments/models/utils/assignmentMarkdownSerializer";
|
||||||
import { assignmentMarkdownParser } from "../../../features/local/assignments/models/utils/assignmentMarkdownParser";
|
import { assignmentMarkdownParser } from "../assignments/models/utils/assignmentMarkdownParser";
|
||||||
|
|
||||||
describe("AssignmentMarkdownTests", () => {
|
describe("AssignmentMarkdownTests", () => {
|
||||||
it("can parse assignment settings", () => {
|
it("can parse assignment settings", () => {
|
||||||
@@ -2,8 +2,8 @@ import { describe, it, expect } from "vitest";
|
|||||||
import {
|
import {
|
||||||
RubricItem,
|
RubricItem,
|
||||||
rubricItemIsExtraCredit,
|
rubricItemIsExtraCredit,
|
||||||
} from "../../../features/local/assignments/models/rubricItem";
|
} from "../assignments/models/rubricItem";
|
||||||
import { assignmentMarkdownParser } from "../../../features/local/assignments/models/utils/assignmentMarkdownParser";
|
import { assignmentMarkdownParser } from "../assignments/models/utils/assignmentMarkdownParser";
|
||||||
|
|
||||||
describe("RubricMarkdownTests", () => {
|
describe("RubricMarkdownTests", () => {
|
||||||
it("can parse one item", () => {
|
it("can parse one item", () => {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import { parseHolidays } from "../../../features/local/utils/settingsUtils";
|
import { parseHolidays } from "../utils/settingsUtils";
|
||||||
|
|
||||||
describe("can parse holiday string", () => {
|
describe("can parse holiday string", () => {
|
||||||
it("can parse empty list", () => {
|
it("can parse empty list", () => {
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import { LocalAssignment } from "../../../features/local/assignments/models/localAssignment";
|
import { LocalAssignment } from "../assignments/models/localAssignment";
|
||||||
import {
|
import {
|
||||||
prepAssignmentForNewSemester,
|
prepAssignmentForNewSemester,
|
||||||
prepLectureForNewSemester,
|
prepLectureForNewSemester,
|
||||||
prepPageForNewSemester,
|
prepPageForNewSemester,
|
||||||
prepQuizForNewSemester,
|
prepQuizForNewSemester,
|
||||||
} from "../../../features/local/utils/semesterTransferUtils";
|
} from "../utils/semesterTransferUtils";
|
||||||
import { Lecture } from "../../../features/local/lectures/lectureModel";
|
import { Lecture } from "../lectures/lectureModel";
|
||||||
import { LocalCoursePage } from "@/features/local/pages/localCoursePageModels";
|
import { LocalCoursePage } from "@/features/local/pages/localCoursePageModels";
|
||||||
import { LocalQuiz } from "@/features/local/quizzes/models/localQuiz";
|
import { LocalQuiz } from "@/features/local/quizzes/models/localQuiz";
|
||||||
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
import { describe, it, expect } from "vitest";
|
import { describe, it, expect } from "vitest";
|
||||||
import {
|
import { dateToMarkdownString, getDateFromString } from "../utils/timeUtils";
|
||||||
dateToMarkdownString,
|
|
||||||
getDateFromString,
|
|
||||||
} from "../../../features/local/utils/timeUtils";
|
|
||||||
|
|
||||||
describe("Can properly handle expected date formats", () => {
|
describe("Can properly handle expected date formats", () => {
|
||||||
it("can use AM/PM dates", () => {
|
it("can use AM/PM dates", () => {
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
import path from "path";
|
|
||||||
import { promises as fs } from "fs";
|
|
||||||
import { courseItemFileStorageService } from "../course/courseItemFileStorageService";
|
|
||||||
import { getCoursePathByName } from "../globalSettings/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);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,8 +1,15 @@
|
|||||||
import publicProcedure from "../../../services/serverFunctions/publicProcedure";
|
import publicProcedure from "../../../services/serverFunctions/publicProcedure";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { router } from "../../../services/serverFunctions/trpcSetup";
|
import { router } from "../../../services/serverFunctions/trpcSetup";
|
||||||
import { fileStorageService } from "@/features/local/utils/fileStorageService";
|
import {
|
||||||
import { zodLocalQuiz } from "@/features/local/quizzes/models/localQuiz";
|
LocalQuiz,
|
||||||
|
zodLocalQuiz,
|
||||||
|
} from "@/features/local/quizzes/models/localQuiz";
|
||||||
|
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
||||||
|
import path from "path";
|
||||||
|
import { promises as fs } from "fs";
|
||||||
|
import { quizMarkdownUtils } from "./models/utils/quizMarkdownUtils";
|
||||||
|
import { courseItemFileStorageService } from "../course/courseItemFileStorageService";
|
||||||
|
|
||||||
export const quizRouter = router({
|
export const quizRouter = router({
|
||||||
getQuiz: publicProcedure
|
getQuiz: publicProcedure
|
||||||
@@ -14,11 +21,12 @@ export const quizRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.query(async ({ input: { courseName, moduleName, quizName } }) => {
|
.query(async ({ input: { courseName, moduleName, quizName } }) => {
|
||||||
return await fileStorageService.quizzes.getQuiz(
|
return await courseItemFileStorageService.getItem({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
quizName
|
name: quizName,
|
||||||
);
|
type: "Quiz",
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getAllQuizzes: publicProcedure
|
getAllQuizzes: publicProcedure
|
||||||
@@ -29,10 +37,11 @@ export const quizRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.query(async ({ input: { courseName, moduleName } }) => {
|
.query(async ({ input: { courseName, moduleName } }) => {
|
||||||
return await fileStorageService.quizzes.getQuizzes(
|
return await courseItemFileStorageService.getItems({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName
|
moduleName,
|
||||||
);
|
type: "Quiz",
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
createQuiz: publicProcedure
|
createQuiz: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
@@ -44,7 +53,7 @@ export const quizRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ input: { courseName, moduleName, quizName, quiz } }) => {
|
.mutation(async ({ input: { courseName, moduleName, quizName, quiz } }) => {
|
||||||
await fileStorageService.quizzes.updateQuiz({
|
await updateQuizFile({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
quizName,
|
quizName,
|
||||||
@@ -73,7 +82,7 @@ export const quizRouter = router({
|
|||||||
previousQuizName,
|
previousQuizName,
|
||||||
},
|
},
|
||||||
}) => {
|
}) => {
|
||||||
await fileStorageService.quizzes.updateQuiz({
|
await updateQuizFile({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
quizName,
|
quizName,
|
||||||
@@ -84,7 +93,7 @@ export const quizRouter = router({
|
|||||||
quizName !== previousQuizName ||
|
quizName !== previousQuizName ||
|
||||||
moduleName !== previousModuleName
|
moduleName !== previousModuleName
|
||||||
) {
|
) {
|
||||||
await fileStorageService.quizzes.delete({
|
await deleteQuizFile({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName: previousModuleName,
|
moduleName: previousModuleName,
|
||||||
quizName: previousQuizName,
|
quizName: previousQuizName,
|
||||||
@@ -101,10 +110,56 @@ export const quizRouter = router({
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.mutation(async ({ input: { courseName, moduleName, quizName } }) => {
|
.mutation(async ({ input: { courseName, moduleName, quizName } }) => {
|
||||||
await fileStorageService.quizzes.delete({
|
await deleteQuizFile({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName,
|
moduleName,
|
||||||
quizName,
|
quizName,
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export async function deleteQuizFile({
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateQuizFile({
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,19 +1,11 @@
|
|||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { basePath, directoryOrFileExists } from "./fileSystemUtils";
|
import { basePath, directoryOrFileExists } from "./fileSystemUtils";
|
||||||
import { quizFileStorageService } from "../quizzes/quizFileStorageService";
|
|
||||||
import { pageFileStorageService } from "../pages/pageFileStorageService";
|
|
||||||
import { moduleFileStorageService } from "../modules/moduleFileStorageService";
|
|
||||||
import { settingsFileStorageService } from "../course/settingsFileStorageService";
|
import { settingsFileStorageService } from "../course/settingsFileStorageService";
|
||||||
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
import { getCoursePathByName } from "../globalSettings/globalSettingsFileStorageService";
|
||||||
import { assignmentsFileStorageService } from "@/features/local/assignments/assignmentsFileStorageService";
|
|
||||||
|
|
||||||
export const fileStorageService = {
|
export const fileStorageService = {
|
||||||
settings: settingsFileStorageService,
|
settings: settingsFileStorageService,
|
||||||
modules: moduleFileStorageService,
|
|
||||||
assignments: assignmentsFileStorageService,
|
|
||||||
quizzes: quizFileStorageService,
|
|
||||||
pages: pageFileStorageService,
|
|
||||||
|
|
||||||
async getEmptyDirectories(): Promise<string[]> {
|
async getEmptyDirectories(): Promise<string[]> {
|
||||||
if (!(await directoryOrFileExists(basePath))) {
|
if (!(await directoryOrFileExists(basePath))) {
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import {
|
|||||||
LocalCourseSettings,
|
LocalCourseSettings,
|
||||||
DayOfWeek,
|
DayOfWeek,
|
||||||
} from "@/features/local/course/localCourseSettings";
|
} from "@/features/local/course/localCourseSettings";
|
||||||
|
import {
|
||||||
|
createModuleFile,
|
||||||
|
getModuleNamesFromFiles,
|
||||||
|
} from "@/features/local/modules/moduleRouter";
|
||||||
|
|
||||||
describe("FileStorageTests", () => {
|
describe("FileStorageTests", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -51,11 +55,9 @@ describe("FileStorageTests", () => {
|
|||||||
const courseName = "test empty course";
|
const courseName = "test empty course";
|
||||||
const moduleName = "test module 1";
|
const moduleName = "test module 1";
|
||||||
|
|
||||||
await fileStorageService.modules.createModule(courseName, moduleName);
|
await createModuleFile(courseName, moduleName);
|
||||||
|
|
||||||
const moduleNames = await fileStorageService.modules.getModuleNames(
|
const moduleNames = await getModuleNamesFromFiles(courseName);
|
||||||
courseName
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(moduleNames).toContain(moduleName);
|
expect(moduleNames).toContain(moduleName);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { describe, it, expect, beforeEach } from "vitest";
|
|||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs";
|
||||||
import { fileStorageService } from "../../features/local/utils/fileStorageService";
|
import { fileStorageService } from "../../features/local/utils/fileStorageService";
|
||||||
import { basePath } from "../../features/local/utils/fileSystemUtils";
|
import { basePath } from "../../features/local/utils/fileSystemUtils";
|
||||||
|
import { courseItemFileStorageService } from "@/features/local/course/courseItemFileStorageService";
|
||||||
|
import { createModuleFile } from "@/features/local/modules/moduleRouter";
|
||||||
|
|
||||||
describe("FileStorageTests", () => {
|
describe("FileStorageTests", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -40,7 +42,7 @@ a) truthy
|
|||||||
`;
|
`;
|
||||||
const invalidQuizMarkdown = "name: testQuiz\n---\nnot a quiz";
|
const invalidQuizMarkdown = "name: testQuiz\n---\nnot a quiz";
|
||||||
await fileStorageService.createCourseFolderForTesting(courseName);
|
await fileStorageService.createCourseFolderForTesting(courseName);
|
||||||
await fileStorageService.modules.createModule(courseName, moduleName);
|
await createModuleFile(courseName, moduleName);
|
||||||
|
|
||||||
await fs.mkdir(`${basePath}/${courseName}/${moduleName}/quizzes`, {
|
await fs.mkdir(`${basePath}/${courseName}/${moduleName}/quizzes`, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
@@ -54,40 +56,17 @@ a) truthy
|
|||||||
validQuizMarkdown
|
validQuizMarkdown
|
||||||
);
|
);
|
||||||
|
|
||||||
const quizzes = await fileStorageService.quizzes.getQuizzes(
|
const quizzes = await courseItemFileStorageService.getItems({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName
|
moduleName,
|
||||||
);
|
type: "Quiz",
|
||||||
|
});
|
||||||
const quizNames = quizzes.map((q) => q.name);
|
const quizNames = quizzes.map((q) => q.name);
|
||||||
|
|
||||||
expect(quizNames).not.includes("testQuiz");
|
expect(quizNames).not.includes("testQuiz");
|
||||||
expect(quizNames).include("validQuiz");
|
expect(quizNames).include("validQuiz");
|
||||||
});
|
});
|
||||||
|
|
||||||
// it("invalid quizes give error messages", async () => {
|
|
||||||
// const courseName = "testCourse";
|
|
||||||
// const moduleName = "testModule";
|
|
||||||
// const invalidQuizMarkdown = "name: testQuiz\n---\nnot a quiz";
|
|
||||||
// await fileStorageService.createCourseFolderForTesting(courseName);
|
|
||||||
// await fileStorageService.modules.createModule(courseName, moduleName);
|
|
||||||
|
|
||||||
// await fs.mkdir(`${basePath}/${courseName}/${moduleName}/quizzes`, {
|
|
||||||
// recursive: true,
|
|
||||||
// });
|
|
||||||
// await fs.writeFile(
|
|
||||||
// `${basePath}/${courseName}/${moduleName}/quizzes/testQuiz.md`,
|
|
||||||
// invalidQuizMarkdown
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const invalidReasons = await fileStorageService.quizzes.getInvalidQuizzes(
|
|
||||||
// courseName,
|
|
||||||
// moduleName
|
|
||||||
// );
|
|
||||||
// const invalidQuiz = invalidReasons.filter((q) => q.quizName === "testQuiz");
|
|
||||||
|
|
||||||
// expect(invalidQuiz.reason).is("testQuiz");
|
|
||||||
// });
|
|
||||||
|
|
||||||
it("invalid assignments dont get loaded", async () => {
|
it("invalid assignments dont get loaded", async () => {
|
||||||
const courseName = "testCourse";
|
const courseName = "testCourse";
|
||||||
const moduleName = "testModule";
|
const moduleName = "testModule";
|
||||||
@@ -107,7 +86,7 @@ this is the test description
|
|||||||
`;
|
`;
|
||||||
const invalidAssignment = "name: invalidAssignment\n---\nnot an assignment";
|
const invalidAssignment = "name: invalidAssignment\n---\nnot an assignment";
|
||||||
await fileStorageService.createCourseFolderForTesting(courseName);
|
await fileStorageService.createCourseFolderForTesting(courseName);
|
||||||
await fileStorageService.modules.createModule(courseName, moduleName);
|
await createModuleFile(courseName, moduleName);
|
||||||
|
|
||||||
await fs.mkdir(`${basePath}/${courseName}/${moduleName}/assignments`, {
|
await fs.mkdir(`${basePath}/${courseName}/${moduleName}/assignments`, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
@@ -121,10 +100,11 @@ this is the test description
|
|||||||
invalidAssignment
|
invalidAssignment
|
||||||
);
|
);
|
||||||
|
|
||||||
const assignments = await fileStorageService.assignments.getAssignments(
|
const assignments = await courseItemFileStorageService.getItems({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName
|
moduleName,
|
||||||
);
|
type: "Assignment",
|
||||||
|
});
|
||||||
const assignmentNames = assignments.map((q) => q.name);
|
const assignmentNames = assignments.map((q) => q.name);
|
||||||
|
|
||||||
expect(assignmentNames).not.includes("invalidAssignment");
|
expect(assignmentNames).not.includes("invalidAssignment");
|
||||||
@@ -144,7 +124,7 @@ DueDateFo59:00
|
|||||||
---
|
---
|
||||||
# Deploying React`;
|
# Deploying React`;
|
||||||
await fileStorageService.createCourseFolderForTesting(courseName);
|
await fileStorageService.createCourseFolderForTesting(courseName);
|
||||||
await fileStorageService.modules.createModule(courseName, moduleName);
|
await createModuleFile(courseName, moduleName);
|
||||||
|
|
||||||
await fs.mkdir(`${basePath}/${courseName}/${moduleName}/pages`, {
|
await fs.mkdir(`${basePath}/${courseName}/${moduleName}/pages`, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
@@ -158,10 +138,11 @@ DueDateFo59:00
|
|||||||
invalidPageMarkdown
|
invalidPageMarkdown
|
||||||
);
|
);
|
||||||
|
|
||||||
const pages = await fileStorageService.pages.getPages(
|
const pages = await courseItemFileStorageService.getItems({
|
||||||
courseName,
|
courseName,
|
||||||
moduleName
|
moduleName,
|
||||||
);
|
type: "Page",
|
||||||
|
});
|
||||||
const assignmentNames = pages.map((q) => q.name);
|
const assignmentNames = pages.map((q) => q.name);
|
||||||
|
|
||||||
expect(assignmentNames).include("validPage");
|
expect(assignmentNames).include("validPage");
|
||||||
|
|||||||
Reference in New Issue
Block a user