From 6e3dcd6cb1c5439171b0d64f08ab9cf043acd287 Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Wed, 25 Sep 2024 17:00:22 -0600 Subject: [PATCH] updates --- nextjs/Dockerfile | 22 +- nextjs/build.sh | 2 +- nextjs/docker-compose.yml | 4 +- nextjs/package.json | 14 +- .../[assignmentName]/AssignmentPreview.tsx | 19 +- .../fileStorage/fileStorageService.ts | 5 + nextjs/src/services/htmlMarkdownUtils.ts | 2 +- nextjs/src/services/tests/fileStorage.test.ts | 215 ++---------------- 8 files changed, 62 insertions(+), 221 deletions(-) diff --git a/nextjs/Dockerfile b/nextjs/Dockerfile index af4dbad..a3d08e6 100644 --- a/nextjs/Dockerfile +++ b/nextjs/Dockerfile @@ -1,17 +1,25 @@ -FROM node:22-alpine +# Stage 1: Build the application +FROM node:22-alpine AS builder WORKDIR /app -COPY package*.json . - +COPY package*.json ./ RUN npm i - COPY . . RUN mkdir -p storage - RUN rm -rf /app/storage/* - RUN npm run build -CMD [ "npm", "run", "start" ] \ No newline at end of file +FROM node:22-alpine AS production + +WORKDIR /app +COPY --from=builder /app/package*.json ./ +RUN npm install --omit=dev + +COPY --from=builder /app/.next ./.next +COPY --from=builder /app/public ./public + +RUN mkdir -p storage && rm -rf /app/storage/* + +CMD [ "npm", "run", "start" ] diff --git a/nextjs/build.sh b/nextjs/build.sh index 6a9923e..c5e2989 100755 --- a/nextjs/build.sh +++ b/nextjs/build.sh @@ -1,7 +1,7 @@ #!/bin/bash MAJOR_VERSION="2" -MINOR_VERSION="0" +MINOR_VERSION="1" VERSION="$MAJOR_VERSION.$MINOR_VERSION" docker build -t canvas_management:$VERSION . diff --git a/nextjs/docker-compose.yml b/nextjs/docker-compose.yml index 0a80cfd..94ee14b 100644 --- a/nextjs/docker-compose.yml +++ b/nextjs/docker-compose.yml @@ -14,5 +14,5 @@ services: - ~/projects/faculty/1430/2024-fall-alex/modules:/app/storage/UX - ~/projects/faculty/4850_AdvancedFE/2024-fall-alex/modules:/app/storage/advanced_frontend - ~/projects/faculty/1810/2024-fall-alex/modules:/app/storage/intro_to_web - # - ~/projects/faculty/1420/2024-fall/Modules:/app/storage/1420 - # - ~/projects/faculty/1425/2024-fall/Modules:/app/storage/1425 \ No newline at end of file + - ~/projects/faculty/1420/2024-fall/Modules:/app/storage/1420 + - ~/projects/faculty/1425/2024-fall/Modules:/app/storage/1425 \ No newline at end of file diff --git a/nextjs/package.json b/nextjs/package.json index 970c2ee..cef4c97 100644 --- a/nextjs/package.json +++ b/nextjs/package.json @@ -10,21 +10,19 @@ "test": "vitest" }, "dependencies": { - "@monaco-editor/react": "^4.6.0", - "@tanstack/react-query": "^5.54.1", "axios": "^1.7.5", - "isomorphic-dompurify": "^2.15.0", - "katex": "^0.16.11", - "marked": "^14.1.2", - "marked-katex-extension": "^5.1.2", "next": "^14.2.7", "react": "^18", "react-dom": "^18", - "react-error-boundary": "^4.0.13", - "react-hot-toast": "^2.4.1", "yaml": "^2.5.0" }, "devDependencies": { + "marked": "^14.1.2", + "@monaco-editor/react": "^4.6.0", + "@tanstack/react-query": "^5.54.1", + "isomorphic-dompurify": "^2.15.0", + "react-error-boundary": "^4.0.13", + "react-hot-toast": "^2.4.1", "@tanstack/react-query-devtools": "^5.54.1", "@testing-library/dom": "^10.4.0", "@testing-library/react": "^16.0.0", diff --git a/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/AssignmentPreview.tsx b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/AssignmentPreview.tsx index 29fe082..1b2f1bc 100644 --- a/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/AssignmentPreview.tsx +++ b/nextjs/src/app/course/[courseName]/modules/[moduleName]/assignment/[assignmentName]/AssignmentPreview.tsx @@ -1,6 +1,7 @@ import { LocalAssignment } from "@/models/local/assignment/localAssignment"; +import { rubricItemIsExtraCredit } from "@/models/local/assignment/rubricItem"; import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils"; -import React from "react"; +import React, { Fragment } from "react"; export default function AssignmentPreview({ assignment, @@ -54,6 +55,22 @@ export default function AssignmentPreview({ }} > +
+
+

Rubric

+
+ {assignment.rubric.map((rubricItem, i) => ( + +
{rubricItem.label}
+
+ {rubricItem.points} + + {rubricItemIsExtraCredit(rubricItem) ? " - Extra Credit" : ""} +
+
+ ))} +
+
); } diff --git a/nextjs/src/services/fileStorage/fileStorageService.ts b/nextjs/src/services/fileStorage/fileStorageService.ts index 67c8b26..2e8dce4 100644 --- a/nextjs/src/services/fileStorage/fileStorageService.ts +++ b/nextjs/src/services/fileStorage/fileStorageService.ts @@ -45,6 +45,11 @@ export const fileStorageService = { async createCourseFolderForTesting(courseName: string) { const courseDirectory = path.join(basePath, courseName); + await fs.mkdir(courseDirectory, { recursive: true }); + }, + async createModuleFolderForTesting(courseName: string, moduleName: string) { + const courseDirectory = path.join(basePath, courseName); + await fs.mkdir(courseDirectory, { recursive: true }); }, }; diff --git a/nextjs/src/services/htmlMarkdownUtils.ts b/nextjs/src/services/htmlMarkdownUtils.ts index 7a77f5d..1e295d3 100644 --- a/nextjs/src/services/htmlMarkdownUtils.ts +++ b/nextjs/src/services/htmlMarkdownUtils.ts @@ -1,6 +1,6 @@ "use client"; import { marked } from "marked"; -import markedKatex from "marked-katex-extension"; +// import markedKatex from "marked-katex-extension"; import * as DOMPurify from "isomorphic-dompurify"; export function markdownToHTMLSafe(markdownString: string) { diff --git a/nextjs/src/services/tests/fileStorage.test.ts b/nextjs/src/services/tests/fileStorage.test.ts index 0cf78f7..7bbbde3 100644 --- a/nextjs/src/services/tests/fileStorage.test.ts +++ b/nextjs/src/services/tests/fileStorage.test.ts @@ -8,6 +8,7 @@ import { } from "@/models/local/localCourse"; import { QuestionType } from "@/models/local/quiz/localQuizQuestion"; import { fileStorageService } from "../fileStorage/fileStorageService"; +import { basePath } from "../fileStorage/utils/fileSystemUtils"; describe("FileStorageTests", () => { beforeEach(() => { @@ -34,7 +35,9 @@ describe("FileStorageTests", () => { await fileStorageService.settings.updateCourseSettings(name, settings); - const loadedSettings = await fileStorageService.settings.getCourseSettings(name); + const loadedSettings = await fileStorageService.settings.getCourseSettings( + name + ); expect(loadedSettings).toEqual(settings); }); @@ -45,208 +48,18 @@ describe("FileStorageTests", () => { await fileStorageService.modules.createModule(courseName, moduleName); - const moduleNames = await fileStorageService.modules.getModuleNames(courseName); + const moduleNames = await fileStorageService.modules.getModuleNames( + courseName + ); expect(moduleNames).toContain(moduleName); }); - // it("course modules with assignments can be saved and loaded", async () => { - // const testCourse: LocalCourse = { - // settings: { - // name: "Test Course with modules and assignments", - // assignmentGroups: [], - // daysOfWeek: [], - // startDate: "07/09/2024 23:59:00", - // endDate: "07/09/2024 23:59:00", - // defaultDueTime: { hour: 1, minute: 59 }, - // }, - // modules: [ - // { - // name: "test module 1 with assignments", - // assignments: [ - // { - // name: "test assignment", - // description: "here is the description", - // dueAt: "07/09/2024 23:59:00", - // lockAt: "07/09/2024 23:59:00", - // submissionTypes: [AssignmentSubmissionType.ONLINE_UPLOAD], - // localAssignmentGroupName: "Final Project", - // rubric: [ - // { points: 4, label: "do task 1" }, - // { points: 2, label: "do task 2" }, - // ], - // allowedFileUploadExtensions: [], - // }, - // ], - // quizzes: [], - // pages: [], - // }, - // ], - // }; - - // await fileStorageService.saveCourseAsync(testCourse); - - // const loadedCourses = await fileStorageService.loadSavedCourses(); - // const loadedCourse = loadedCourses.find( - // (c) => c.settings.name === testCourse.settings.name - // ); - - // expect(loadedCourse?.modules[0].assignments).toEqual( - // testCourse.modules[0].assignments - // ); - // }); - - // it("course modules with quizzes can be saved and loaded", async () => { - // const testCourse: LocalCourse = { - // settings: { - // name: "Test Course with modules and quiz", - // assignmentGroups: [], - // daysOfWeek: [], - // startDate: "07/09/2024 23:59:00", - // endDate: "07/09/2024 23:59:00", - // defaultDueTime: { hour: 1, minute: 59 }, - // }, - // modules: [ - // { - // name: "test module 1 with quiz", - // assignments: [], - // quizzes: [ - // { - // name: "Test Quiz", - // description: "quiz description", - // lockAt: "07/09/2024 12:05:00", - // dueAt: "07/09/2024 12:05:00", - // shuffleAnswers: true, - // oneQuestionAtATime: true, - // localAssignmentGroupName: "Assignments", - // questions: [ - // { - // text: "test essay", - // questionType: QuestionType.ESSAY, - // points: 1, - // answers: [], - // matchDistractors: [], - // }, - // ], - // showCorrectAnswers: false, - // allowedAttempts: 0, - // }, - // ], - // pages: [], - // }, - // ], - // }; - - // await fileStorageService.saveCourseAsync(testCourse); - - // const loadedCourses = await fileStorageService.loadSavedCourses(); - // const loadedCourse = loadedCourses.find( - // (c) => c.settings.name === testCourse.settings.name - // ); - - // expect(loadedCourse?.modules[0].quizzes).toEqual( - // testCourse.modules[0].quizzes - // ); - // }); - - // it("markdown storage fully populated does not lose data", async () => { - // const testCourse: LocalCourse = { - // settings: { - // name: "Test Course with lots of data", - // assignmentGroups: [], - // daysOfWeek: [DayOfWeek.Monday, DayOfWeek.Wednesday], - // startDate: "07/09/2024 23:59:00", - // endDate: "07/09/2024 23:59:00", - // defaultDueTime: { hour: 1, minute: 59 }, - // }, - // modules: [ - // { - // name: "new test module", - // assignments: [ - // { - // name: "test assignment", - // description: "here is the description", - // dueAt: "07/09/2024 23:59:00", - // lockAt: "07/09/2024 23:59:00", - // submissionTypes: [AssignmentSubmissionType.ONLINE_UPLOAD], - // localAssignmentGroupName: "Final Project", - // rubric: [ - // { points: 4, label: "do task 1" }, - // { points: 2, label: "do task 2" }, - // ], - // allowedFileUploadExtensions: [], - // }, - // ], - // quizzes: [ - // { - // name: "Test Quiz", - // description: "quiz description", - // lockAt: "07/09/2024 23:59:00", - // dueAt: "07/09/2024 23:59:00", - // shuffleAnswers: true, - // oneQuestionAtATime: false, - // localAssignmentGroupName: "someId", - // allowedAttempts: -1, - // questions: [ - // { - // text: "test short answer", - // questionType: QuestionType.SHORT_ANSWER, - // points: 1, - // answers: [], - // matchDistractors: [], - // }, - // ], - // showCorrectAnswers: false, - // }, - // ], - // pages: [], - // }, - // ], - // }; - - // await fileStorageService.saveCourseAsync(testCourse); - - // const loadedCourses = await fileStorageService.loadSavedCourses(); - // const loadedCourse = loadedCourses.find( - // (c) => c.settings.name === testCourse.settings.name - // ); - - // expect(loadedCourse).toEqual(testCourse); - // }); - - // it("markdown storage can persist pages", async () => { - // const testCourse: LocalCourse = { - // settings: { - // name: "Test Course with page", - // assignmentGroups: [], - // daysOfWeek: [DayOfWeek.Monday, DayOfWeek.Wednesday], - // startDate: "07/09/2024 23:59:00", - // endDate: "07/09/2024 23:59:00", - // defaultDueTime: { hour: 1, minute: 59 }, - // }, - // modules: [ - // { - // name: "page test module", - // assignments: [], - // quizzes: [], - // pages: [ - // { - // name: "test page persistence", - // dueAt: "07/09/2024 23:59:00", - // text: "this is some\n## markdown\n", - // }, - // ], - // }, - // ], - // }; - - // await fileStorageService.saveCourseAsync(testCourse); - - // const loadedCourses = await fileStorageService.loadSavedCourses(); - // const loadedCourse = loadedCourses.find( - // (c) => c.settings.name === testCourse.settings.name - // ); - - // expect(loadedCourse).toEqual(testCourse); - // }); + it("invalid quizzes do not get loaded", async () => { + const courseName = "testCourse"; + const invalidQuizMarkdown = "not a quiz"; + await fileStorageService.createCourseFolderForTesting(courseName); + await fileStorageService.modules.createModule(courseName, "testModule"); + // fs.writeFile(`${basePath}/${courseName}/testModule/testQuiz.md`, invalidQuizMarkdown) + }); });