mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
adding storage for lectures
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
dateToMarkdownString,
|
||||
getDateFromStringOrThrow,
|
||||
|
||||
@@ -1,10 +1,53 @@
|
||||
"use client";
|
||||
|
||||
import { MonacoEditor } from "@/components/editor/MonacoEditor";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
useLecturesByWeekQuery,
|
||||
useLectureUpdateMutation,
|
||||
} from "@/hooks/localCourse/lectureHooks";
|
||||
import { Lecture } from "@/models/local/lecture";
|
||||
import {
|
||||
lectureToString,
|
||||
parseLecture,
|
||||
} from "@/services/fileStorage/utils/lectureUtils";
|
||||
import { useEffect, useState } from "react";
|
||||
import LecturePreview from "./LecturePreview";
|
||||
|
||||
export default function EditLecture({ lectureDay }: { lectureDay: string }) {
|
||||
const [text, setText] = useState("");
|
||||
const { data: weeks } = useLecturesByWeekQuery();
|
||||
const updateLecture = useLectureUpdateMutation();
|
||||
const lecture = weeks
|
||||
.flatMap(({ lectures }) => lectures.map((lecture) => lecture))
|
||||
.find((l) => l.date === lectureDay);
|
||||
|
||||
const startingText = lecture ? lectureToString(lecture) : `Name: Name Here
|
||||
Date: ${lectureDay}
|
||||
---
|
||||
`;
|
||||
|
||||
const [text, setText] = useState(startingText);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const delay = 500;
|
||||
const handler = setTimeout(() => {
|
||||
try {
|
||||
const parsed = parseLecture(text);
|
||||
if (!lecture || lectureToString(parsed) !== lectureToString(lecture)) {
|
||||
console.log("updating lecture");
|
||||
updateLecture.mutate(parsed);
|
||||
}
|
||||
setError("");
|
||||
} catch (e: any) {
|
||||
setError(e.toString());
|
||||
}
|
||||
}, delay);
|
||||
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [lecture, text, updateLecture]);
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="columns-2 min-h-0 flex-1">
|
||||
@@ -12,8 +55,8 @@ export default function EditLecture({ lectureDay }: { lectureDay: string }) {
|
||||
<MonacoEditor value={text} onChange={setText} />
|
||||
</div>
|
||||
<div className="h-full">
|
||||
{/* <div className="text-red-300">{error && error}</div> */}
|
||||
{/* <AssignmentPreview assignment={assignment} /> */}
|
||||
<div className="text-red-300">{error && error}</div>
|
||||
{lecture && <LecturePreview lecture={lecture} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Lecture } from "@/models/local/lecture";
|
||||
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
|
||||
|
||||
export default function LecturePreview({ lecture }: { lecture: Lecture }) {
|
||||
return (
|
||||
<div>
|
||||
<section>
|
||||
<div className="flex">
|
||||
<div className="flex-1 text-end pe-3">Name</div>
|
||||
<div className="flex-1">{lecture.name}</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex-1 text-end pe-3">Date</div>
|
||||
<div className="flex-1">{lecture.date}</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div
|
||||
className="markdownPreview"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: markdownToHTMLSafe(lecture.content),
|
||||
}}
|
||||
></div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react";
|
||||
import EditLecture from "./EditLecture";
|
||||
import { getDateFromStringOrThrow, getDateOnlyMarkdownString } from "@/models/local/timeUtils";
|
||||
|
||||
export default function page({
|
||||
params: { lectureDay },
|
||||
@@ -8,5 +9,7 @@ export default function page({
|
||||
}) {
|
||||
const decodedLectureDay = decodeURIComponent(lectureDay);
|
||||
console.log(decodedLectureDay);
|
||||
return <EditLecture lectureDay={decodedLectureDay} />;
|
||||
const lectureDate = getDateFromStringOrThrow(decodedLectureDay, "lecture day in lecture page")
|
||||
const lectureDayOnly = getDateOnlyMarkdownString(lectureDate)
|
||||
return <EditLecture lectureDay={lectureDayOnly} />;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||
import {
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
useSuspenseQuery,
|
||||
} from "@tanstack/react-query";
|
||||
import { lectureKeys } from "./lectureKeys";
|
||||
import { useCourseContext } from "@/app/course/[courseName]/context/courseContext";
|
||||
import { getLectures } from "@/services/fileStorage/lectureFileStorageService";
|
||||
import {
|
||||
getLectures,
|
||||
updateLecture,
|
||||
} from "@/services/fileStorage/lectureFileStorageService";
|
||||
import { Lecture } from "@/models/local/lecture";
|
||||
import { useLocalCourseSettingsQuery } from "./localCoursesHooks";
|
||||
|
||||
export const getLecturesQueryConfig = (courseName: string) =>
|
||||
({
|
||||
@@ -9,7 +18,23 @@ export const getLecturesQueryConfig = (courseName: string) =>
|
||||
queryFn: async () => await getLectures(courseName),
|
||||
} as const);
|
||||
|
||||
export const useLecturesQuery = () => {
|
||||
export const useLecturesByWeekQuery = () => {
|
||||
const { courseName } = useCourseContext();
|
||||
return useSuspenseQuery(getLecturesQueryConfig(courseName));
|
||||
};
|
||||
|
||||
export const useLectureUpdateMutation = () => {
|
||||
const { courseName } = useCourseContext();
|
||||
const { data: settings } = useLocalCourseSettingsQuery();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (lecture: Lecture) => {
|
||||
await updateLecture(courseName, settings, lecture);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: lectureKeys.allLectures(courseName),
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -14,7 +14,8 @@ const getAssignmentNames = async (courseName: string, moduleName: string) => {
|
||||
console.log(
|
||||
`Error loading course by name, assignments folder does not exist in ${filePath}`
|
||||
);
|
||||
await fs.mkdir(filePath);
|
||||
// await fs.mkdir(filePath);
|
||||
return [];
|
||||
}
|
||||
|
||||
const assignmentFiles = await fs.readdir(filePath);
|
||||
|
||||
@@ -2,12 +2,18 @@
|
||||
import path from "path";
|
||||
import { basePath } from "./utils/fileSystemUtils";
|
||||
import fs from "fs/promises";
|
||||
import {
|
||||
lectureFolderName,
|
||||
lectureToString,
|
||||
parseLecture,
|
||||
} from "./utils/lectureUtils";
|
||||
import { Lecture } from "@/models/local/lecture";
|
||||
import { extractLabelValue } from "@/models/local/assignment/utils/markdownUtils";
|
||||
import { getDateOnlyMarkdownString } from "@/models/local/timeUtils";
|
||||
import { getDayOfWeek, LocalCourseSettings } from "@/models/local/localCourse";
|
||||
import { getWeekNumber } from "@/app/course/[courseName]/calendar/calendarMonthUtils";
|
||||
import { getDateFromStringOrThrow } from "@/models/local/timeUtils";
|
||||
|
||||
export async function getLectures(courseName: string) {
|
||||
const courseLectureRoot = path.join(basePath, courseName, "lectures");
|
||||
const courseLectureRoot = path.join(basePath, courseName, lectureFolderName);
|
||||
if (!(await directoryExists(courseLectureRoot))) {
|
||||
return [];
|
||||
}
|
||||
@@ -39,30 +45,36 @@ export async function getLectures(courseName: string) {
|
||||
return lecturesByWeek;
|
||||
}
|
||||
|
||||
export function parseLecture(fileContent: string): Lecture {
|
||||
try {
|
||||
const settings = fileContent.split("---\n")[0];
|
||||
const name = extractLabelValue(settings, "Name");
|
||||
const date = extractLabelValue(settings, "Date");
|
||||
export async function updateLecture(
|
||||
courseName: string,
|
||||
courseSettings: LocalCourseSettings,
|
||||
lecture: Lecture
|
||||
) {
|
||||
const courseLectureRoot = path.join(basePath, courseName, lectureFolderName);
|
||||
const startDate = getDateFromStringOrThrow(
|
||||
courseSettings.startDate,
|
||||
"semester start date in update lecture"
|
||||
);
|
||||
const lectureDate = getDateFromStringOrThrow(
|
||||
lecture.date,
|
||||
"lecture start date in update lecture"
|
||||
);
|
||||
const weekNumber = getWeekNumber(startDate, lectureDate)
|
||||
.toString()
|
||||
.padStart(2, "0");
|
||||
|
||||
const content = fileContent.split("---\n")[1].trim();
|
||||
|
||||
return {
|
||||
name,
|
||||
date,
|
||||
content,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error parsing lecture", fileContent);
|
||||
throw error;
|
||||
}
|
||||
const weekFolderName = `week-${weekNumber}`;
|
||||
const weekPath = path.join(courseLectureRoot, weekFolderName);
|
||||
if (!(await directoryExists(weekPath))) {
|
||||
await fs.mkdir(weekPath, { recursive: true });
|
||||
}
|
||||
|
||||
export function lectureToString(lecture: Lecture) {
|
||||
return `Name: ${lecture.name}
|
||||
Date: ${lecture.date}
|
||||
---
|
||||
${lecture.content}`;
|
||||
const lecturePath = path.join(
|
||||
weekPath,
|
||||
`${lectureDate.getDay()}-${getDayOfWeek(lectureDate)}.md`
|
||||
);
|
||||
const lectureContents = lectureToString(lecture);
|
||||
await fs.writeFile(lecturePath, lectureContents);
|
||||
}
|
||||
|
||||
const directoryExists = async (path: string): Promise<boolean> => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { promises as fs } from "fs";
|
||||
import path from "path";
|
||||
import { basePath } from "./utils/fileSystemUtils";
|
||||
import { lectureFolderName } from "./utils/lectureUtils";
|
||||
|
||||
export const moduleFileStorageService = {
|
||||
async getModuleNames(courseName: string) {
|
||||
@@ -14,9 +15,10 @@ export const moduleFileStorageService = {
|
||||
.map((dirent) => dirent.name);
|
||||
|
||||
const modules = await Promise.all(modulePromises);
|
||||
return modules
|
||||
.filter((m) => m !== "lectures")
|
||||
.sort((a, b) => a.localeCompare(b));
|
||||
const modulesWithoutLectures = modules.filter(
|
||||
(m) => m !== lectureFolderName
|
||||
);
|
||||
return modulesWithoutLectures.sort((a, b) => a.localeCompare(b));
|
||||
},
|
||||
async createModule(courseName: string, moduleName: string) {
|
||||
const courseDirectory = path.join(basePath, courseName);
|
||||
|
||||
@@ -1,177 +0,0 @@
|
||||
import {
|
||||
LocalAssignment,
|
||||
localAssignmentMarkdown,
|
||||
} from "@/models/local/assignment/localAssignment";
|
||||
import {
|
||||
LocalCourse,
|
||||
LocalCourseSettings,
|
||||
localCourseYamlUtils,
|
||||
} from "@/models/local/localCourse";
|
||||
import { LocalModule } from "@/models/local/localModules";
|
||||
import {
|
||||
LocalCoursePage,
|
||||
localPageMarkdownUtils,
|
||||
} from "@/models/local/page/localCoursePage";
|
||||
import {
|
||||
LocalQuiz,
|
||||
localQuizMarkdownUtils,
|
||||
} from "@/models/local/quiz/localQuiz";
|
||||
import { promises as fs } from "fs";
|
||||
import path from "path";
|
||||
import { directoryOrFileExists } from "./fileSystemUtils";
|
||||
|
||||
const basePath = process.env.STORAGE_DIRECTORY ?? "./storage";
|
||||
|
||||
export const courseMarkdownLoader = {
|
||||
// async loadSavedCourses(): Promise<LocalCourse[]> {
|
||||
// const courseDirectories = await fs.readdir(basePath, {
|
||||
// withFileTypes: true,
|
||||
// });
|
||||
// const coursePromises = courseDirectories
|
||||
// .filter((dirent) => dirent.isDirectory())
|
||||
// .map(async (dirent) => {
|
||||
// const coursePath = path.join(basePath, dirent.name);
|
||||
// const settingsPath = path.join(coursePath, "settings.yml");
|
||||
// if (await directoryOrFileExists(settingsPath)) {
|
||||
// return this.loadCourseByPath(coursePath);
|
||||
// }
|
||||
// return null;
|
||||
// });
|
||||
|
||||
// const courses = (await Promise.all(coursePromises)).filter(
|
||||
// (course) => course !== null
|
||||
// ) as LocalCourse[];
|
||||
// return courses.sort((a, b) =>
|
||||
// a.settings.name.localeCompare(b.settings.name)
|
||||
// );
|
||||
// },
|
||||
|
||||
// async loadCourseByPath(courseDirectory: string): Promise<LocalCourse> {
|
||||
// if (!(await directoryOrFileExists(courseDirectory))) {
|
||||
// const errorMessage = `Error loading course by name, could not find folder ${courseDirectory}`;
|
||||
// console.log(errorMessage);
|
||||
// throw new Error(errorMessage);
|
||||
// }
|
||||
|
||||
// const settings = await this.loadCourseSettings(courseDirectory);
|
||||
// const modules = await this.loadCourseModules(courseDirectory);
|
||||
|
||||
// return {
|
||||
// settings,
|
||||
// modules,
|
||||
// };
|
||||
// },
|
||||
|
||||
// async loadCourseSettings(
|
||||
// courseDirectory: string
|
||||
// ): Promise<LocalCourseSettings> {
|
||||
// const settingsPath = path.join(courseDirectory, "settings.yml");
|
||||
// if (!(await directoryOrFileExists(settingsPath))) {
|
||||
// const errorMessage = `Error loading course by name, settings file ${settingsPath}`;
|
||||
// console.log(errorMessage);
|
||||
// throw new Error(errorMessage);
|
||||
// }
|
||||
|
||||
// const settingsString = await fs.readFile(settingsPath, "utf-8");
|
||||
// const settings = localCourseYamlUtils.parseSettingYaml(settingsString);
|
||||
|
||||
// const folderName = path.basename(courseDirectory);
|
||||
// return { ...settings, name: folderName };
|
||||
// },
|
||||
|
||||
// async loadCourseModules(courseDirectory: string): Promise<LocalModule[]> {
|
||||
// const moduleDirectories = await fs.readdir(courseDirectory, {
|
||||
// withFileTypes: true,
|
||||
// });
|
||||
// const modulePromises = moduleDirectories
|
||||
// .filter((dirent) => dirent.isDirectory())
|
||||
// .map((dirent) =>
|
||||
// this.loadModuleFromPath(path.join(courseDirectory, dirent.name))
|
||||
// );
|
||||
|
||||
// const modules = await Promise.all(modulePromises);
|
||||
// return modules.sort((a, b) => a.name.localeCompare(b.name));
|
||||
// },
|
||||
|
||||
// async loadModuleFromPath(modulePath: string): Promise<LocalModule> {
|
||||
// const moduleName = path.basename(modulePath);
|
||||
// const assignments = await this.loadAssignmentsFromPath(modulePath);
|
||||
// const quizzes = await this.loadQuizzesFromPath(modulePath);
|
||||
// const pages = await this.loadModulePagesFromPath(modulePath);
|
||||
|
||||
// return {
|
||||
// name: moduleName,
|
||||
// assignments,
|
||||
// quizzes,
|
||||
// pages,
|
||||
// };
|
||||
// },
|
||||
|
||||
// async loadAssignmentsFromPath(
|
||||
// modulePath: string
|
||||
// ): Promise<LocalAssignment[]> {
|
||||
// const assignmentsPath = path.join(modulePath, "assignments");
|
||||
// if (!(await directoryOrFileExists(assignmentsPath))) {
|
||||
// console.log(
|
||||
// `Error loading course by name, assignments folder does not exist in ${modulePath}`
|
||||
// );
|
||||
// await fs.mkdir(assignmentsPath);
|
||||
// }
|
||||
|
||||
// const assignmentFiles = await fs.readdir(assignmentsPath);
|
||||
// const assignmentPromises = assignmentFiles.map(async (file) => {
|
||||
// const filePath = path.join(assignmentsPath, file);
|
||||
// const rawFile = (await fs.readFile(filePath, "utf-8")).replace(
|
||||
// /\r\n/g,
|
||||
// "\n"
|
||||
// );
|
||||
// return localAssignmentMarkdown.parseMarkdown(rawFile);
|
||||
// });
|
||||
|
||||
// return await Promise.all(assignmentPromises);
|
||||
// },
|
||||
|
||||
// async loadQuizzesFromPath(modulePath: string): Promise<LocalQuiz[]> {
|
||||
// const quizzesPath = path.join(modulePath, "quizzes");
|
||||
// if (!(await directoryOrFileExists(quizzesPath))) {
|
||||
// console.log(
|
||||
// `Quizzes folder does not exist in ${modulePath}, creating now`
|
||||
// );
|
||||
// await fs.mkdir(quizzesPath);
|
||||
// }
|
||||
|
||||
// const quizFiles = await fs.readdir(quizzesPath);
|
||||
// const quizPromises = quizFiles.map(async (file) => {
|
||||
// const filePath = path.join(quizzesPath, file);
|
||||
// const rawQuiz = (await fs.readFile(filePath, "utf-8")).replace(
|
||||
// /\r\n/g,
|
||||
// "\n"
|
||||
// );
|
||||
// return localQuizMarkdownUtils.parseMarkdown(rawQuiz);
|
||||
// });
|
||||
|
||||
// return await Promise.all(quizPromises);
|
||||
// },
|
||||
|
||||
// async loadModulePagesFromPath(
|
||||
// modulePath: string
|
||||
// ): Promise<LocalCoursePage[]> {
|
||||
// const pagesPath = path.join(modulePath, "pages");
|
||||
// if (!(await directoryOrFileExists(pagesPath))) {
|
||||
// console.log(`Pages folder does not exist in ${modulePath}, creating now`);
|
||||
// await fs.mkdir(pagesPath);
|
||||
// }
|
||||
|
||||
// const pageFiles = await fs.readdir(pagesPath);
|
||||
// const pagePromises = pageFiles.map(async (file) => {
|
||||
// const filePath = path.join(pagesPath, file);
|
||||
// const rawPage = (await fs.readFile(filePath, "utf-8")).replace(
|
||||
// /\r\n/g,
|
||||
// "\n"
|
||||
// );
|
||||
// return localPageMarkdownUtils.parseMarkdown(rawPage);
|
||||
// });
|
||||
|
||||
// return await Promise.all(pagePromises);
|
||||
// },
|
||||
};
|
||||
@@ -1,245 +0,0 @@
|
||||
import { localAssignmentMarkdown } from "@/models/local/assignment/localAssignment";
|
||||
import { LocalCourse, localCourseYamlUtils } from "@/models/local/localCourse";
|
||||
import { LocalModule } from "@/models/local/localModules";
|
||||
import { localPageMarkdownUtils } from "@/models/local/page/localCoursePage";
|
||||
import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils";
|
||||
import { promises as fs } from "fs";
|
||||
import path from "path";
|
||||
|
||||
const basePath = process.env.STORAGE_DIRECTORY ?? "./storage";
|
||||
|
||||
const directoryExists = async (directoryPath: string): Promise<boolean> => {
|
||||
try {
|
||||
await fs.access(directoryPath);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const saveSettings = async (course: LocalCourse, courseDirectory: string) => {
|
||||
const settingsFilePath = path.join(courseDirectory, "settings.yml");
|
||||
const settingsYaml = localCourseYamlUtils.settingsToYaml(course.settings);
|
||||
await fs.writeFile(settingsFilePath, settingsYaml);
|
||||
};
|
||||
|
||||
// const saveModules = async (
|
||||
// course: LocalCourse,
|
||||
// courseDirectory: string,
|
||||
// previouslyStoredCourse?: LocalCourse
|
||||
// ) => {
|
||||
// for (const localModule of course.modules) {
|
||||
// const moduleDirectory = path.join(courseDirectory, localModule.name);
|
||||
// if (!(await directoryExists(moduleDirectory))) {
|
||||
// await fs.mkdir(moduleDirectory, { recursive: true });
|
||||
// }
|
||||
|
||||
// await saveQuizzes(course, localModule, previouslyStoredCourse);
|
||||
// await saveAssignments(course, localModule, previouslyStoredCourse);
|
||||
// await savePages(course, localModule, previouslyStoredCourse);
|
||||
// }
|
||||
|
||||
// const moduleNames = course.modules.map((m) => m.name);
|
||||
// const moduleDirectories = await fs.readdir(courseDirectory, {
|
||||
// withFileTypes: true,
|
||||
// });
|
||||
|
||||
// for (const dirent of moduleDirectories) {
|
||||
// if (dirent.isDirectory() && !moduleNames.includes(dirent.name)) {
|
||||
// const moduleDirPath = path.join(courseDirectory, dirent.name);
|
||||
// console.log(
|
||||
// `Deleting extra module directory, it was probably renamed ${moduleDirPath}`
|
||||
// );
|
||||
// await fs.rmdir(moduleDirPath, { recursive: true });
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
// const saveQuizzes = async (
|
||||
// course: LocalCourse,
|
||||
// module: LocalModule,
|
||||
// previouslyStoredCourse?: LocalCourse
|
||||
// ) => {
|
||||
// const quizzesDirectory = path.join(
|
||||
// basePath,
|
||||
// course.settings.name,
|
||||
// module.name,
|
||||
// "quizzes"
|
||||
// );
|
||||
// if (!(await directoryExists(quizzesDirectory))) {
|
||||
// await fs.mkdir(quizzesDirectory, { recursive: true });
|
||||
// }
|
||||
|
||||
// for (const quiz of module.quizzes) {
|
||||
// const previousModule = previouslyStoredCourse?.modules.find(
|
||||
// (m) => m.name === module.name
|
||||
// );
|
||||
// const previousQuiz = previousModule?.quizzes.find((q) => q === quiz);
|
||||
|
||||
// if (!previousQuiz) {
|
||||
// const markdownPath = path.join(quizzesDirectory, `${quiz.name}.md`);
|
||||
// const quizMarkdown = quizMarkdownUtils.toMarkdown(quiz);
|
||||
// console.log(`Saving quiz ${markdownPath}`);
|
||||
// await fs.writeFile(markdownPath, quizMarkdown);
|
||||
// }
|
||||
// }
|
||||
|
||||
// await removeOldQuizzes(quizzesDirectory, module);
|
||||
// };
|
||||
|
||||
const saveAssignments = async (
|
||||
course: LocalCourse,
|
||||
module: LocalModule,
|
||||
previouslyStoredCourse?: LocalCourse
|
||||
) => {
|
||||
const assignmentsDirectory = path.join(
|
||||
basePath,
|
||||
course.settings.name,
|
||||
module.name,
|
||||
"assignments"
|
||||
);
|
||||
if (!(await directoryExists(assignmentsDirectory))) {
|
||||
await fs.mkdir(assignmentsDirectory, { recursive: true });
|
||||
}
|
||||
|
||||
for (const assignment of module.assignments) {
|
||||
const previousModule = previouslyStoredCourse?.modules.find(
|
||||
(m) => m.name === module.name
|
||||
);
|
||||
const previousAssignment = previousModule?.assignments.find(
|
||||
(a) => a === assignment
|
||||
);
|
||||
|
||||
if (!previousAssignment) {
|
||||
const assignmentMarkdown = localAssignmentMarkdown.toMarkdown(assignment);
|
||||
const filePath = path.join(assignmentsDirectory, `${assignment.name}.md`);
|
||||
console.log(`Saving assignment ${filePath}`);
|
||||
await fs.writeFile(filePath, assignmentMarkdown);
|
||||
}
|
||||
}
|
||||
|
||||
await removeOldAssignments(assignmentsDirectory, module);
|
||||
};
|
||||
|
||||
const savePages = async (
|
||||
course: LocalCourse,
|
||||
module: LocalModule,
|
||||
previouslyStoredCourse?: LocalCourse
|
||||
) => {
|
||||
const pagesDirectory = path.join(
|
||||
basePath,
|
||||
course.settings.name,
|
||||
module.name,
|
||||
"pages"
|
||||
);
|
||||
if (!(await directoryExists(pagesDirectory))) {
|
||||
await fs.mkdir(pagesDirectory, { recursive: true });
|
||||
}
|
||||
|
||||
for (const page of module.pages) {
|
||||
const previousModule = previouslyStoredCourse?.modules.find(
|
||||
(m) => m.name === module.name
|
||||
);
|
||||
const previousPage = previousModule?.pages.find((p) => p === page);
|
||||
|
||||
if (!previousPage) {
|
||||
const pageMarkdown = localPageMarkdownUtils.toMarkdown(page);
|
||||
const filePath = path.join(pagesDirectory, `${page.name}.md`);
|
||||
console.log(`Saving page ${filePath}`);
|
||||
await fs.writeFile(filePath, pageMarkdown);
|
||||
}
|
||||
}
|
||||
|
||||
await removeOldPages(pagesDirectory, module);
|
||||
};
|
||||
|
||||
const removeOldQuizzes = async (
|
||||
quizzesDirectory: string,
|
||||
module: LocalModule
|
||||
) => {
|
||||
const existingFiles = await fs.readdir(quizzesDirectory);
|
||||
const quizFilesToDelete = existingFiles.filter((file) => {
|
||||
const quizMarkdownPath = path.join(
|
||||
quizzesDirectory,
|
||||
`${file.replace(".md", "")}.md`
|
||||
);
|
||||
return !module.quizzes.some(
|
||||
(quiz) =>
|
||||
path.join(quizzesDirectory, `${quiz.name}.md`) === quizMarkdownPath
|
||||
);
|
||||
});
|
||||
|
||||
for (const file of quizFilesToDelete) {
|
||||
console.log(
|
||||
`Removing old quiz, it has probably been renamed ${path.join(
|
||||
quizzesDirectory,
|
||||
file
|
||||
)}`
|
||||
);
|
||||
await fs.unlink(path.join(quizzesDirectory, file));
|
||||
}
|
||||
};
|
||||
|
||||
const removeOldAssignments = async (
|
||||
assignmentsDirectory: string,
|
||||
module: LocalModule
|
||||
) => {
|
||||
const existingFiles = await fs.readdir(assignmentsDirectory);
|
||||
const assignmentFilesToDelete = existingFiles.filter((file) => {
|
||||
const assignmentMarkdownPath = path.join(
|
||||
assignmentsDirectory,
|
||||
`${file.replace(".md", "")}.md`
|
||||
);
|
||||
return !module.assignments.some(
|
||||
(assignment) =>
|
||||
path.join(assignmentsDirectory, `${assignment.name}.md`) ===
|
||||
assignmentMarkdownPath
|
||||
);
|
||||
});
|
||||
|
||||
for (const file of assignmentFilesToDelete) {
|
||||
console.log(
|
||||
`Removing old assignment, it has probably been renamed ${path.join(
|
||||
assignmentsDirectory,
|
||||
file
|
||||
)}`
|
||||
);
|
||||
await fs.unlink(path.join(assignmentsDirectory, file));
|
||||
}
|
||||
};
|
||||
|
||||
const removeOldPages = async (pagesDirectory: string, module: LocalModule) => {
|
||||
const existingFiles = await fs.readdir(pagesDirectory);
|
||||
const pageFilesToDelete = existingFiles.filter((file) => {
|
||||
const pageMarkdownPath = path.join(
|
||||
pagesDirectory,
|
||||
`${file.replace(".md", "")}.md`
|
||||
);
|
||||
return !module.pages.some(
|
||||
(page) =>
|
||||
path.join(pagesDirectory, `${page.name}.md`) === pageMarkdownPath
|
||||
);
|
||||
});
|
||||
|
||||
for (const file of pageFilesToDelete) {
|
||||
console.log(
|
||||
`Removing old page, it has probably been renamed ${path.join(
|
||||
pagesDirectory,
|
||||
file
|
||||
)}`
|
||||
);
|
||||
await fs.unlink(path.join(pagesDirectory, file));
|
||||
}
|
||||
};
|
||||
|
||||
// export const courseMarkdownSaver = {
|
||||
// async save(course: LocalCourse, previouslyStoredCourse?: LocalCourse) {
|
||||
// const courseDirectory = path.join(basePath, course.settings.name);
|
||||
// if (!(await directoryExists(courseDirectory))) {
|
||||
// await fs.mkdir(courseDirectory, { recursive: true });
|
||||
// }
|
||||
|
||||
// await saveSettings(course, courseDirectory);
|
||||
// await saveModules(course, courseDirectory, previouslyStoredCourse);
|
||||
// },
|
||||
// };
|
||||
30
nextjs/src/services/fileStorage/utils/lectureUtils.ts
Normal file
30
nextjs/src/services/fileStorage/utils/lectureUtils.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { extractLabelValue } from "@/models/local/assignment/utils/markdownUtils";
|
||||
import { Lecture } from "@/models/local/lecture";
|
||||
|
||||
export function parseLecture(fileContent: string): Lecture {
|
||||
try {
|
||||
const settings = fileContent.split("---\n")[0];
|
||||
const name = extractLabelValue(settings, "Name");
|
||||
const date = extractLabelValue(settings, "Date");
|
||||
|
||||
const content = fileContent.split("---\n")[1].trim();
|
||||
|
||||
return {
|
||||
name,
|
||||
date,
|
||||
content,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error parsing lecture", fileContent);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function lectureToString(lecture: Lecture) {
|
||||
return `Name: ${lecture.name}
|
||||
Date: ${lecture.date}
|
||||
---
|
||||
${lecture.content}`;
|
||||
}
|
||||
|
||||
export const lectureFolderName = "00 - lectures"
|
||||
@@ -1,8 +1,6 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
lectureToString,
|
||||
parseLecture,
|
||||
} from "../fileStorage/lectureFileStorageService";
|
||||
import { lectureToString } from "../fileStorage/utils/lectureUtils";
|
||||
import { parseLecture } from "../fileStorage/utils/lectureUtils";
|
||||
import { Lecture } from "@/models/local/lecture";
|
||||
|
||||
describe("can parse and stringify lectures", () => {
|
||||
|
||||
Reference in New Issue
Block a user