mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
working on settings
This commit is contained in:
@@ -1,17 +1,48 @@
|
|||||||
import { DayOfWeekInput } from "@/components/form/DayOfWeekInput";
|
import { DayOfWeekInput } from "@/components/form/DayOfWeekInput";
|
||||||
import SelectInput from "@/components/form/SelectInput";
|
import SelectInput from "@/components/form/SelectInput";
|
||||||
|
import { useCourseListInTermQuery } from "@/hooks/canvas/canvasCourseHooks";
|
||||||
import { useCanvasTermsQuery } from "@/hooks/canvas/canvasHooks";
|
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 { CanvasEnrollmentTermModel } from "@/models/canvas/enrollmentTerms/canvasEnrollmentTermModel";
|
||||||
import { DayOfWeek } from "@/models/local/localCourse";
|
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() {
|
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<
|
const [selectedTerm, setSelectedTerm] = useState<
|
||||||
CanvasEnrollmentTermModel | undefined
|
CanvasEnrollmentTermModel | undefined
|
||||||
>();
|
>();
|
||||||
|
const { data: canvasCourses } = useCourseListInTermQuery(selectedTerm?.id);
|
||||||
const [selectedDaysOfWeek, setSelectedDaysOfWeek] = useState<DayOfWeek[]>([]);
|
const [selectedDaysOfWeek, setSelectedDaysOfWeek] = useState<DayOfWeek[]>([]);
|
||||||
|
const [selectedCanvasCourse, setSelectedCanvasCourse] = useState<
|
||||||
|
CanvasCourseModel | undefined
|
||||||
|
>();
|
||||||
|
const [selectedDirectory, setSelectedDirectory] = useState<
|
||||||
|
string | undefined
|
||||||
|
>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -23,7 +54,22 @@ export default function NewCourseForm() {
|
|||||||
getOptionName={(t) => t.name}
|
getOptionName={(t) => t.name}
|
||||||
/>
|
/>
|
||||||
{selectedTerm && (
|
{selectedTerm && (
|
||||||
<div>
|
<>
|
||||||
|
<SelectInput
|
||||||
|
value={selectedCanvasCourse}
|
||||||
|
setValue={setSelectedCanvasCourse}
|
||||||
|
label={"Course"}
|
||||||
|
options={canvasCourses}
|
||||||
|
getOptionName={(c) => c.name}
|
||||||
|
/>
|
||||||
|
<SelectInput
|
||||||
|
value={selectedDirectory}
|
||||||
|
setValue={setSelectedDirectory}
|
||||||
|
label={"Storage Folder"}
|
||||||
|
options={emptyDirectories}
|
||||||
|
getOptionName={(d) => d}
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
<DayOfWeekInput
|
<DayOfWeekInput
|
||||||
selectedDays={selectedDaysOfWeek}
|
selectedDays={selectedDaysOfWeek}
|
||||||
updateSettings={(day) => {
|
updateSettings={(day) => {
|
||||||
@@ -36,8 +82,9 @@ export default function NewCourseForm() {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,12 +50,6 @@ export async function GET(
|
|||||||
return withErrorHandling(async () => {
|
return withErrorHandling(async () => {
|
||||||
try {
|
try {
|
||||||
const url = getUrl(params);
|
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;
|
var requestCount = 1;
|
||||||
url.searchParams.set("per_page", "100");
|
url.searchParams.set("per_page", "100");
|
||||||
@@ -100,7 +94,7 @@ export async function POST(
|
|||||||
try {
|
try {
|
||||||
const url = getUrl(params);
|
const url = getUrl(params);
|
||||||
const body = await req.json();
|
const body = await req.json();
|
||||||
const response = await axiosClient.post(url, body);
|
const response = await axiosClient.post(url.toString(), body);
|
||||||
|
|
||||||
const headers = proxyResponseHeaders(response);
|
const headers = proxyResponseHeaders(response);
|
||||||
return new NextResponse(JSON.stringify(response.data), { headers });
|
return new NextResponse(JSON.stringify(response.data), { headers });
|
||||||
@@ -123,7 +117,7 @@ export async function PUT(
|
|||||||
try {
|
try {
|
||||||
const url = getUrl(params);
|
const url = getUrl(params);
|
||||||
const body = await req.json();
|
const body = await req.json();
|
||||||
const response = await axiosClient.put(url, body);
|
const response = await axiosClient.put(url.toString(), body);
|
||||||
|
|
||||||
const headers = proxyResponseHeaders(response);
|
const headers = proxyResponseHeaders(response);
|
||||||
return new NextResponse(JSON.stringify(response.data), { headers });
|
return new NextResponse(JSON.stringify(response.data), { headers });
|
||||||
@@ -143,7 +137,7 @@ export async function DELETE(
|
|||||||
return withErrorHandling(async () => {
|
return withErrorHandling(async () => {
|
||||||
try {
|
try {
|
||||||
const url = getUrl(params);
|
const url = getUrl(params);
|
||||||
const response = await axiosClient.delete(url);
|
const response = await axiosClient.delete(url.toString());
|
||||||
|
|
||||||
const headers = proxyResponseHeaders(response);
|
const headers = proxyResponseHeaders(response);
|
||||||
return new NextResponse(JSON.stringify(response.data), { headers });
|
return new NextResponse(JSON.stringify(response.data), { headers });
|
||||||
|
|||||||
9
nextjs/src/app/api/directories/empty/route.ts
Normal file
9
nextjs/src/app/api/directories/empty/route.ts
Normal file
@@ -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);
|
||||||
|
});
|
||||||
|
|
||||||
@@ -8,8 +8,17 @@ export const canvasCourseKeys = {
|
|||||||
["canvas", canavasId, "course details"] as const,
|
["canvas", canavasId, "course details"] as const,
|
||||||
assignmentGroups: (canavasId: number) =>
|
assignmentGroups: (canavasId: number) =>
|
||||||
["canvas", canavasId, "assignment groups"] as const,
|
["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) =>
|
export const useCanvasCourseQuery = (canvasId: number) =>
|
||||||
useSuspenseQuery({
|
useSuspenseQuery({
|
||||||
queryKey: canvasCourseKeys.courseDetails(canvasId),
|
queryKey: canvasCourseKeys.courseDetails(canvasId),
|
||||||
|
|||||||
16
nextjs/src/hooks/localCourse/storageDirectoryHooks.ts
Normal file
16
nextjs/src/hooks/localCourse/storageDirectoryHooks.ts
Normal file
@@ -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<string[]>(url);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -10,9 +10,11 @@ const baseCanvasUrl = "https://snow.instructure.com/api/v1";
|
|||||||
|
|
||||||
const getAllTerms = async () => {
|
const getAllTerms = async () => {
|
||||||
const url = `${baseCanvasUrl}/accounts/10/terms`;
|
const url = `${baseCanvasUrl}/accounts/10/terms`;
|
||||||
const {data} = await axiosClient.get<{
|
const { data } = await axiosClient.get<
|
||||||
enrollment_terms: CanvasEnrollmentTermModel[];
|
{
|
||||||
}[]>(url);
|
enrollment_terms: CanvasEnrollmentTermModel[];
|
||||||
|
}[]
|
||||||
|
>(url);
|
||||||
const terms = data.flatMap((r) => r.enrollment_terms);
|
const terms = data.flatMap((r) => r.enrollment_terms);
|
||||||
return terms;
|
return terms;
|
||||||
};
|
};
|
||||||
@@ -21,11 +23,12 @@ export const canvasService = {
|
|||||||
getAllTerms,
|
getAllTerms,
|
||||||
async getCourses(termId: number) {
|
async getCourses(termId: number) {
|
||||||
const url = `${baseCanvasUrl}/courses`;
|
const url = `${baseCanvasUrl}/courses`;
|
||||||
const coursesResponse =
|
const response = await axiosClient.get<CanvasCourseModel[][]>(url);
|
||||||
await canvasServiceUtils.paginatedRequest<CanvasCourseModel>({ url });
|
const allCourses = response.data;
|
||||||
return coursesResponse
|
const coursesInTerm = allCourses
|
||||||
.flat()
|
.flatMap((l) => l)
|
||||||
.filter((c) => c.enrollment_term_id === termId);
|
.filter((c) => c.enrollment_term_id === termId);
|
||||||
|
return coursesInTerm;
|
||||||
},
|
},
|
||||||
|
|
||||||
async getCourse(courseId: number): Promise<CanvasCourseModel> {
|
async getCourse(courseId: number): Promise<CanvasCourseModel> {
|
||||||
|
|||||||
@@ -32,17 +32,24 @@ export const fileStorageService = {
|
|||||||
const courseDirectories = await fs.readdir(basePath, {
|
const courseDirectories = await fs.readdir(basePath, {
|
||||||
withFileTypes: true,
|
withFileTypes: true,
|
||||||
});
|
});
|
||||||
const coursePromises = courseDirectories
|
const coursePromises = await Promise.all(
|
||||||
.filter((dirent) => dirent.isDirectory())
|
courseDirectories
|
||||||
.filter(async (dirent) => {
|
.filter((dirent) => dirent.isDirectory())
|
||||||
const coursePath = path.join(basePath, dirent.name);
|
.map(async (dirent) => {
|
||||||
const settingsPath = path.join(coursePath, "settings.yml");
|
const coursePath = path.join(basePath, dirent.name);
|
||||||
return await directoryOrFileExists(settingsPath);
|
const settingsPath = path.join(coursePath, "settings.yml");
|
||||||
});
|
const hasSettings = await directoryOrFileExists(settingsPath);
|
||||||
const courseNamesFromDirectories = (await Promise.all(coursePromises)).map(
|
return {
|
||||||
(c) => c.name
|
dirent,
|
||||||
|
hasSettings,
|
||||||
|
};
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const courseNamesFromDirectories = coursePromises
|
||||||
|
.filter(({ hasSettings }) => hasSettings)
|
||||||
|
.map(({ dirent }) => dirent.name);
|
||||||
|
|
||||||
return courseNamesFromDirectories;
|
return courseNamesFromDirectories;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -256,10 +263,22 @@ export const fileStorageService = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const directories = await fs.readdir(basePath, { withFileTypes: true });
|
const directories = await fs.readdir(basePath, { withFileTypes: true });
|
||||||
const emptyDirectories = directories
|
console.log(directories);
|
||||||
.filter((dirent) => dirent.isDirectory())
|
const emptyDirectories = (
|
||||||
.map((dirent) => path.join(basePath, dirent.name))
|
await Promise.all(
|
||||||
.filter(async (dir) => !(await hasFileSystemEntries(dir)));
|
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;
|
return emptyDirectories;
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user