From 32b59b3975ed0d876ccd7270750793229d4bf321 Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Wed, 11 Sep 2024 21:33:35 -0600 Subject: [PATCH] working on settings --- nextjs/src/app/NewCourseForm.tsx | 57 +++++++++++++++++-- nextjs/src/app/api/canvas/[...rest]/route.ts | 12 +--- nextjs/src/app/api/directories/empty/route.ts | 9 +++ nextjs/src/hooks/canvas/canvasCourseHooks.ts | 9 +++ .../localCourse/storageDirectoryHooks.ts | 16 ++++++ nextjs/src/services/canvas/canvasService.ts | 17 +++--- .../fileStorage/fileStorageService.ts | 45 ++++++++++----- 7 files changed, 131 insertions(+), 34 deletions(-) create mode 100644 nextjs/src/app/api/directories/empty/route.ts create mode 100644 nextjs/src/hooks/localCourse/storageDirectoryHooks.ts diff --git a/nextjs/src/app/NewCourseForm.tsx b/nextjs/src/app/NewCourseForm.tsx index 5ddfd66..7b66a4f 100644 --- a/nextjs/src/app/NewCourseForm.tsx +++ b/nextjs/src/app/NewCourseForm.tsx @@ -1,17 +1,48 @@ import { DayOfWeekInput } from "@/components/form/DayOfWeekInput"; import SelectInput from "@/components/form/SelectInput"; +import { useCourseListInTermQuery } from "@/hooks/canvas/canvasCourseHooks"; import { useCanvasTermsQuery } from "@/hooks/canvas/canvasHooks"; +import { useEmptyDirectoriesQuery } from "@/hooks/localCourse/storageDirectoryHooks"; +import { CanvasCourseModel } from "@/models/canvas/courses/canvasCourseModel"; import { CanvasEnrollmentTermModel } from "@/models/canvas/enrollmentTerms/canvasEnrollmentTermModel"; import { DayOfWeek } from "@/models/local/localCourse"; -import React, { useState } from "react"; +import React, { useMemo, useState } from "react"; + +const sampleCompose = ` +services: + canvas_manager: + image: alexmickelson/canvas_management:2 + user: 1000:1000 # userid:groupid that matches file ownership on host system + ports: + - 8080:8080 # hostPort:containerPort - you can change the first one if you like + env_file: + - .env # needs to have your CANVAS_TOKEN set + environment: + - storageDirectory=/app/storage + - TZ=America/Denver + volumes: + - ~/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 +` export default function NewCourseForm() { - const { data: canvasTerms } = useCanvasTermsQuery(new Date()); - + const today = useMemo(() => new Date(), []); + const { data: canvasTerms } = useCanvasTermsQuery(today); + const { data: emptyDirectories } = useEmptyDirectoriesQuery(); const [selectedTerm, setSelectedTerm] = useState< CanvasEnrollmentTermModel | undefined >(); + const { data: canvasCourses } = useCourseListInTermQuery(selectedTerm?.id); const [selectedDaysOfWeek, setSelectedDaysOfWeek] = useState([]); + const [selectedCanvasCourse, setSelectedCanvasCourse] = useState< + CanvasCourseModel | undefined + >(); + const [selectedDirectory, setSelectedDirectory] = useState< + string | undefined + >(); return (
@@ -23,7 +54,22 @@ export default function NewCourseForm() { getOptionName={(t) => t.name} /> {selectedTerm && ( -
+ <> + c.name} + /> + d} + /> +
{ @@ -36,8 +82,9 @@ export default function NewCourseForm() { }); }} /> -
+ )} +
); } diff --git a/nextjs/src/app/api/canvas/[...rest]/route.ts b/nextjs/src/app/api/canvas/[...rest]/route.ts index 5362ce1..3e90c88 100644 --- a/nextjs/src/app/api/canvas/[...rest]/route.ts +++ b/nextjs/src/app/api/canvas/[...rest]/route.ts @@ -50,12 +50,6 @@ export async function GET( return withErrorHandling(async () => { try { const url = getUrl(params); - // const response = await axiosClient.get(url, { - // headers: { - // // Include other headers from the incoming request if needed: - // "Content-Type": "application/json", - // }, - // }); var requestCount = 1; url.searchParams.set("per_page", "100"); @@ -100,7 +94,7 @@ export async function POST( try { const url = getUrl(params); const body = await req.json(); - const response = await axiosClient.post(url, body); + const response = await axiosClient.post(url.toString(), body); const headers = proxyResponseHeaders(response); return new NextResponse(JSON.stringify(response.data), { headers }); @@ -123,7 +117,7 @@ export async function PUT( try { const url = getUrl(params); const body = await req.json(); - const response = await axiosClient.put(url, body); + const response = await axiosClient.put(url.toString(), body); const headers = proxyResponseHeaders(response); return new NextResponse(JSON.stringify(response.data), { headers }); @@ -143,7 +137,7 @@ export async function DELETE( return withErrorHandling(async () => { try { const url = getUrl(params); - const response = await axiosClient.delete(url); + const response = await axiosClient.delete(url.toString()); const headers = proxyResponseHeaders(response); return new NextResponse(JSON.stringify(response.data), { headers }); diff --git a/nextjs/src/app/api/directories/empty/route.ts b/nextjs/src/app/api/directories/empty/route.ts new file mode 100644 index 0000000..012876f --- /dev/null +++ b/nextjs/src/app/api/directories/empty/route.ts @@ -0,0 +1,9 @@ +import { fileStorageService } from "@/services/fileStorage/fileStorageService"; +import { withErrorHandling } from "@/services/withErrorHandling"; + +export const GET = async () => + await withErrorHandling(async () => { + const directories = await fileStorageService.getEmptyDirectories(); + return Response.json(directories); + }); + \ No newline at end of file diff --git a/nextjs/src/hooks/canvas/canvasCourseHooks.ts b/nextjs/src/hooks/canvas/canvasCourseHooks.ts index 6620067..fbc0f6c 100644 --- a/nextjs/src/hooks/canvas/canvasCourseHooks.ts +++ b/nextjs/src/hooks/canvas/canvasCourseHooks.ts @@ -8,8 +8,17 @@ export const canvasCourseKeys = { ["canvas", canavasId, "course details"] as const, assignmentGroups: (canavasId: number) => ["canvas", canavasId, "assignment groups"] as const, + courseListInTerm: (canvasTermId: number | undefined) => + ["canvas courses in term", canvasTermId] as const, }; +export const useCourseListInTermQuery = (canvasTermId: number | undefined) => + useSuspenseQuery({ + queryKey: canvasCourseKeys.courseListInTerm(canvasTermId), + queryFn: async () => + canvasTermId ? await canvasService.getCourses(canvasTermId) : [], + }); + export const useCanvasCourseQuery = (canvasId: number) => useSuspenseQuery({ queryKey: canvasCourseKeys.courseDetails(canvasId), diff --git a/nextjs/src/hooks/localCourse/storageDirectoryHooks.ts b/nextjs/src/hooks/localCourse/storageDirectoryHooks.ts new file mode 100644 index 0000000..9e7939b --- /dev/null +++ b/nextjs/src/hooks/localCourse/storageDirectoryHooks.ts @@ -0,0 +1,16 @@ +import { axiosClient } from "@/services/axiosUtils"; +import { useSuspenseQuery } from "@tanstack/react-query"; + +export const directoryKeys = { + emptyFolders: ["empty folders"] as const, +}; + +export const useEmptyDirectoriesQuery = () => + useSuspenseQuery({ + queryKey: directoryKeys.emptyFolders, + queryFn: async () => { + const url = "/api/directories/empty"; + const { data } = await axiosClient.get(url); + return data; + }, + }); diff --git a/nextjs/src/services/canvas/canvasService.ts b/nextjs/src/services/canvas/canvasService.ts index 168b870..a42c4e0 100644 --- a/nextjs/src/services/canvas/canvasService.ts +++ b/nextjs/src/services/canvas/canvasService.ts @@ -10,9 +10,11 @@ const baseCanvasUrl = "https://snow.instructure.com/api/v1"; const getAllTerms = async () => { const url = `${baseCanvasUrl}/accounts/10/terms`; - const {data} = await axiosClient.get<{ - enrollment_terms: CanvasEnrollmentTermModel[]; - }[]>(url); + const { data } = await axiosClient.get< + { + enrollment_terms: CanvasEnrollmentTermModel[]; + }[] + >(url); const terms = data.flatMap((r) => r.enrollment_terms); return terms; }; @@ -21,11 +23,12 @@ export const canvasService = { getAllTerms, async getCourses(termId: number) { const url = `${baseCanvasUrl}/courses`; - const coursesResponse = - await canvasServiceUtils.paginatedRequest({ url }); - return coursesResponse - .flat() + const response = await axiosClient.get(url); + const allCourses = response.data; + const coursesInTerm = allCourses + .flatMap((l) => l) .filter((c) => c.enrollment_term_id === termId); + return coursesInTerm; }, async getCourse(courseId: number): Promise { diff --git a/nextjs/src/services/fileStorage/fileStorageService.ts b/nextjs/src/services/fileStorage/fileStorageService.ts index 6680cdb..8ee3377 100644 --- a/nextjs/src/services/fileStorage/fileStorageService.ts +++ b/nextjs/src/services/fileStorage/fileStorageService.ts @@ -32,17 +32,24 @@ export const fileStorageService = { const courseDirectories = await fs.readdir(basePath, { withFileTypes: true, }); - const coursePromises = courseDirectories - .filter((dirent) => dirent.isDirectory()) - .filter(async (dirent) => { - const coursePath = path.join(basePath, dirent.name); - const settingsPath = path.join(coursePath, "settings.yml"); - return await directoryOrFileExists(settingsPath); - }); - const courseNamesFromDirectories = (await Promise.all(coursePromises)).map( - (c) => c.name + const coursePromises = await Promise.all( + courseDirectories + .filter((dirent) => dirent.isDirectory()) + .map(async (dirent) => { + const coursePath = path.join(basePath, dirent.name); + const settingsPath = path.join(coursePath, "settings.yml"); + const hasSettings = await directoryOrFileExists(settingsPath); + return { + dirent, + hasSettings, + }; + }) ); + const courseNamesFromDirectories = coursePromises + .filter(({ hasSettings }) => hasSettings) + .map(({ dirent }) => dirent.name); + return courseNamesFromDirectories; }, @@ -256,10 +263,22 @@ export const fileStorageService = { } const directories = await fs.readdir(basePath, { withFileTypes: true }); - const emptyDirectories = directories - .filter((dirent) => dirent.isDirectory()) - .map((dirent) => path.join(basePath, dirent.name)) - .filter(async (dir) => !(await hasFileSystemEntries(dir))); + console.log(directories); + const emptyDirectories = ( + await Promise.all( + directories + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => path.join(dirent.name)) + .map(async (directory) => { + return { + directory, + files: await fs.readdir(path.join(basePath, directory)), + }; + }) + ) + ) + .filter(({ files }) => files.length === 0) + .map(({ directory }) => directory); return emptyDirectories; },