more code refactor to colocate feature code

This commit is contained in:
2025-07-23 11:25:12 -06:00
parent c37ad0708e
commit 815f929c2d
30 changed files with 535 additions and 646 deletions

View File

@@ -2,10 +2,8 @@ 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,
@@ -14,19 +12,20 @@ import {
import { getCoursePathByName } from "../globalSettings/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 getItemFileNames = async ({
courseName,
moduleName,
type,
}: {
courseName: string;
moduleName: string;
type: CourseItemType;
}) => {
const courseDirectory = await getCoursePathByName(courseName);
const folder = typeToFolder[type];
const filePath = path.join(courseDirectory, moduleName, folder);
@@ -41,12 +40,17 @@ const getItemFileNames = async (
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 getItem = async <T extends CourseItemType>({
courseName,
moduleName,
name,
type,
}: {
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");
@@ -73,17 +77,21 @@ const getItem = async <T extends CourseItemType>(
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);
getItems: async <T extends CourseItemType>({
courseName,
moduleName,
type,
}: {
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);
const item = await getItem({ courseName, moduleName, name, type });
return item;
} catch {
return null;
@@ -93,42 +101,4 @@ export const courseItemFileStorageService = {
).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

@@ -13,10 +13,18 @@ import {
updateGlobalSettings,
} from "@/features/local/globalSettings/globalSettingsFileStorageService";
import {
getLectures,
updateLecture,
} from "@/features/local/lectures/lectureFileStorageService";
import { zodLocalCourseSettings } from "@/features/local/course/localCourseSettings";
LocalCourseSettings,
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({
allCoursesSettings: publicProcedure.query(async () => {
@@ -71,90 +79,7 @@ export const settingsRouter = router({
});
if (settingsFromCourseToImport) {
const oldCourseName = settingsFromCourseToImport.name;
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);
})
),
]);
})
);
await migrateCourseContent(settingsFromCourseToImport, settings);
}
}
),
@@ -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,
]);
})
);
}