mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
can create quiz in canvas
This commit is contained in:
@@ -28,7 +28,7 @@ export default function CourseCalendar() {
|
|||||||
h-full
|
h-full
|
||||||
overflow-y-scroll
|
overflow-y-scroll
|
||||||
border-4
|
border-4
|
||||||
border-slate-600
|
border-gray-900
|
||||||
rounded-xl
|
rounded-xl
|
||||||
bg-slate-950
|
bg-slate-950
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -7,6 +7,14 @@ import {
|
|||||||
import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils";
|
import { quizMarkdownUtils } from "@/models/local/quiz/utils/quizMarkdownUtils";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import QuizPreview from "./QuizPreview";
|
import QuizPreview from "./QuizPreview";
|
||||||
|
import {
|
||||||
|
useAddQuizToCanvasMutation,
|
||||||
|
useCanvasQuizzesQuery,
|
||||||
|
useDeleteQuizFromCanvasMutation,
|
||||||
|
} from "@/hooks/canvas/canvasQuizHooks";
|
||||||
|
import { Spinner } from "@/components/Spinner";
|
||||||
|
import { baseCanvasUrl, canvasApi } from "@/services/canvas/canvasServiceUtils";
|
||||||
|
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
|
||||||
|
|
||||||
export default function EditQuiz({
|
export default function EditQuiz({
|
||||||
moduleName,
|
moduleName,
|
||||||
@@ -59,11 +67,54 @@ export default function EditQuiz({
|
|||||||
<QuizPreview quiz={quiz} />
|
<QuizPreview quiz={quiz} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-5">
|
<QuizButtons moduleName={moduleName} quizName={quizName} />
|
||||||
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
</div>
|
||||||
Add to canvas....
|
);
|
||||||
</button>
|
}
|
||||||
</div>
|
|
||||||
|
function QuizButtons({
|
||||||
|
moduleName,
|
||||||
|
quizName,
|
||||||
|
}: {
|
||||||
|
quizName: string;
|
||||||
|
moduleName: string;
|
||||||
|
}) {
|
||||||
|
const { data: canvasQuizzes } = useCanvasQuizzesQuery();
|
||||||
|
const { data: quiz } = useQuizQuery(moduleName, quizName);
|
||||||
|
const { data: settings } = useLocalCourseSettingsQuery();
|
||||||
|
const addToCanvas = useAddQuizToCanvasMutation();
|
||||||
|
const deleteFromCanvas = useDeleteQuizFromCanvasMutation();
|
||||||
|
|
||||||
|
const quizInCanvas = canvasQuizzes.find((c) => c.title === quizName);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-5">
|
||||||
|
{!quizInCanvas && (
|
||||||
|
<button
|
||||||
|
disabled={addToCanvas.isPending}
|
||||||
|
onClick={() => addToCanvas.mutate(quiz)}
|
||||||
|
>
|
||||||
|
Add to canvas....
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{quizInCanvas && (
|
||||||
|
<a
|
||||||
|
className="btn"
|
||||||
|
target="_blank"
|
||||||
|
href={`${baseCanvasUrl}/courses/${settings.canvasId}/quizzes/${quizInCanvas.id}`}
|
||||||
|
>
|
||||||
|
View in Canvas
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
{quizInCanvas && (
|
||||||
|
<button
|
||||||
|
disabled={deleteFromCanvas.isPending}
|
||||||
|
onClick={() => deleteFromCanvas.mutate(quizInCanvas.id)}
|
||||||
|
>
|
||||||
|
Delete from Canvas
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{(addToCanvas.isPending || deleteFromCanvas.isPending) && <Spinner />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,62 @@
|
|||||||
|
import {
|
||||||
|
useMutation,
|
||||||
|
useQueryClient,
|
||||||
|
useSuspenseQuery,
|
||||||
|
} from "@tanstack/react-query";
|
||||||
|
import { useLocalCourseSettingsQuery } from "../localCourse/localCoursesHooks";
|
||||||
|
import { canvasQuizService } from "@/services/canvas/canvasQuizService";
|
||||||
|
import { LocalQuiz } from "@/models/local/quiz/localQuiz";
|
||||||
|
|
||||||
export const canvasQuizKeys = {
|
export const canvasQuizKeys = {
|
||||||
quizzes: (canvasCourseId: number )=> ["canvas", canvasCourseId, "quizzes"],
|
quizzes: (canvasCourseId: number) => ["canvas", canvasCourseId, "quizzes"],
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export const useCanvasQuizzesQuery = () => {
|
||||||
|
const { data: settings } = useLocalCourseSettingsQuery();
|
||||||
|
|
||||||
export const useCanvasQuizQuery
|
return useSuspenseQuery({
|
||||||
|
queryKey: canvasQuizKeys.quizzes(settings.canvasId),
|
||||||
|
queryFn: async () => canvasQuizService.getAll(settings.canvasId),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAddQuizToCanvasMutation = () => {
|
||||||
|
const { data: settings } = useLocalCourseSettingsQuery();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async (quiz: LocalQuiz) => {
|
||||||
|
const assignmentGroup = settings.assignmentGroups.find(
|
||||||
|
(g) => g.name === quiz.localAssignmentGroupName
|
||||||
|
);
|
||||||
|
console.log("starting");
|
||||||
|
await canvasQuizService.create(
|
||||||
|
settings.canvasId,
|
||||||
|
quiz,
|
||||||
|
assignmentGroup?.canvasId
|
||||||
|
);
|
||||||
|
console.log("ending");
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
console.log("invalidating");
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: canvasQuizKeys.quizzes(settings.canvasId),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteQuizFromCanvasMutation = () => {
|
||||||
|
const { data: settings } = useLocalCourseSettingsQuery();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: async (canvasQuizId: number) => {
|
||||||
|
await canvasQuizService.delete(settings.canvasId, canvasQuizId);
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: canvasQuizKeys.quizzes(settings.canvasId),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
19
nextjs/src/models/canvas/quizzes/canvasQuizAnswerModel.ts
Normal file
19
nextjs/src/models/canvas/quizzes/canvasQuizAnswerModel.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
export interface CanvasQuizAnswer {
|
||||||
|
id: number;
|
||||||
|
text: string;
|
||||||
|
html?: string;
|
||||||
|
weight: number;
|
||||||
|
// comments?: string;
|
||||||
|
// text_after_answers?: string;
|
||||||
|
// answer_match_left?: string;
|
||||||
|
// answer_match_right?: string;
|
||||||
|
// matching_answer_incorrect_matches?: string;
|
||||||
|
// numerical_answer_type?: string;
|
||||||
|
// exact?: number;
|
||||||
|
// margin?: number;
|
||||||
|
// approximate?: number;
|
||||||
|
// precision?: number;
|
||||||
|
// start?: number;
|
||||||
|
// end?: number;
|
||||||
|
// blank_id?: number;
|
||||||
|
}
|
||||||
14
nextjs/src/models/canvas/quizzes/canvasQuizQuestionModel.ts
Normal file
14
nextjs/src/models/canvas/quizzes/canvasQuizQuestionModel.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { CanvasQuizAnswer } from "./canvasQuizAnswerModel";
|
||||||
|
|
||||||
|
export interface CanvasQuizQuestion {
|
||||||
|
id: number;
|
||||||
|
quiz_id: number;
|
||||||
|
position?: number;
|
||||||
|
question_name: string;
|
||||||
|
question_type: string;
|
||||||
|
question_text: string;
|
||||||
|
correct_comments: string;
|
||||||
|
incorrect_comments: string;
|
||||||
|
neutral_comments: string;
|
||||||
|
answers?: CanvasQuizAnswer[];
|
||||||
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
import { baseCanvasUrl, canvasServiceUtils } from "./canvasServiceUtils";
|
import { canvasApi, canvasServiceUtils } from "./canvasServiceUtils";
|
||||||
import { axiosClient } from "../axiosUtils";
|
import { axiosClient } from "../axiosUtils";
|
||||||
import { CanvasAssignmentGroup } from "@/models/canvas/assignments/canvasAssignmentGroup";
|
import { CanvasAssignmentGroup } from "@/models/canvas/assignments/canvasAssignmentGroup";
|
||||||
import { LocalAssignmentGroup } from "@/models/local/assignment/localAssignmentGroup";
|
import { LocalAssignmentGroup } from "@/models/local/assignment/localAssignmentGroup";
|
||||||
import { rateLimitAwareDelete } from "./canvasWebRequestor";
|
import { rateLimitAwareDelete } from "./canvasWebRequestor";
|
||||||
|
|
||||||
|
|
||||||
export const canvasAssignmentGroupService = {
|
export const canvasAssignmentGroupService = {
|
||||||
async getAll(courseId: number): Promise<CanvasAssignmentGroup[]> {
|
async getAll(courseId: number): Promise<CanvasAssignmentGroup[]> {
|
||||||
console.log("Requesting assignment groups");
|
console.log("Requesting assignment groups");
|
||||||
const url = `${baseCanvasUrl}/courses/${courseId}/assignment_groups`;
|
const url = `${canvasApi}/courses/${courseId}/assignment_groups`;
|
||||||
const assignmentGroups = await canvasServiceUtils.paginatedRequest<
|
const assignmentGroups = await canvasServiceUtils.paginatedRequest<
|
||||||
CanvasAssignmentGroup[]
|
CanvasAssignmentGroup[]
|
||||||
>({
|
>({
|
||||||
@@ -22,7 +21,7 @@ export const canvasAssignmentGroupService = {
|
|||||||
localAssignmentGroup: LocalAssignmentGroup
|
localAssignmentGroup: LocalAssignmentGroup
|
||||||
): Promise<LocalAssignmentGroup> {
|
): Promise<LocalAssignmentGroup> {
|
||||||
console.log(`Creating assignment group: ${localAssignmentGroup.name}`);
|
console.log(`Creating assignment group: ${localAssignmentGroup.name}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/assignment_groups`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/assignment_groups`;
|
||||||
const body = {
|
const body = {
|
||||||
name: localAssignmentGroup.name,
|
name: localAssignmentGroup.name,
|
||||||
group_weight: localAssignmentGroup.weight,
|
group_weight: localAssignmentGroup.weight,
|
||||||
@@ -45,7 +44,7 @@ export const canvasAssignmentGroupService = {
|
|||||||
if (!localAssignmentGroup.canvasId) {
|
if (!localAssignmentGroup.canvasId) {
|
||||||
throw new Error("Cannot update assignment group if canvas ID is null");
|
throw new Error("Cannot update assignment group if canvas ID is null");
|
||||||
}
|
}
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/assignment_groups/${localAssignmentGroup.canvasId}`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/assignment_groups/${localAssignmentGroup.canvasId}`;
|
||||||
const body = {
|
const body = {
|
||||||
name: localAssignmentGroup.name,
|
name: localAssignmentGroup.name,
|
||||||
group_weight: localAssignmentGroup.weight,
|
group_weight: localAssignmentGroup.weight,
|
||||||
@@ -56,11 +55,11 @@ export const canvasAssignmentGroupService = {
|
|||||||
|
|
||||||
async delete(
|
async delete(
|
||||||
canvasCourseId: number,
|
canvasCourseId: number,
|
||||||
canvasAssignmentGroupId: number
|
canvasAssignmentGroupId: number,
|
||||||
, assignmentGroupName: string
|
assignmentGroupName: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log(`Deleting assignment group: ${assignmentGroupName}`);
|
console.log(`Deleting assignment group: ${assignmentGroupName}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/assignment_groups/${canvasAssignmentGroupId}`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/assignment_groups/${canvasAssignmentGroupId}`;
|
||||||
await rateLimitAwareDelete(url);
|
await rateLimitAwareDelete(url);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CanvasAssignment } from "@/models/canvas/assignments/canvasAssignment";
|
import { CanvasAssignment } from "@/models/canvas/assignments/canvasAssignment";
|
||||||
import { baseCanvasUrl, canvasServiceUtils } from "./canvasServiceUtils";
|
import { canvasApi, canvasServiceUtils } from "./canvasServiceUtils";
|
||||||
import { LocalAssignment } from "@/models/local/assignment/localAssignment";
|
import { LocalAssignment } from "@/models/local/assignment/localAssignment";
|
||||||
import { axiosClient } from "../axiosUtils";
|
import { axiosClient } from "../axiosUtils";
|
||||||
import { markdownToHTMLSafe } from "../htmlMarkdownUtils";
|
import { markdownToHTMLSafe } from "../htmlMarkdownUtils";
|
||||||
@@ -8,17 +8,15 @@ import { assignmentPoints } from "@/models/local/assignment/utils/assignmentPoin
|
|||||||
|
|
||||||
export const canvasAssignmentService = {
|
export const canvasAssignmentService = {
|
||||||
async getAll(courseId: number): Promise<CanvasAssignment[]> {
|
async getAll(courseId: number): Promise<CanvasAssignment[]> {
|
||||||
const url = `courses/${courseId}/assignments`;
|
const url = `${canvasApi}/courses/${courseId}/assignments`;
|
||||||
const assignments = await canvasServiceUtils.paginatedRequest<
|
const { data: assignments } = await axiosClient.get<CanvasAssignment[]>(
|
||||||
CanvasAssignment[]
|
url
|
||||||
>({ url });
|
|
||||||
return assignments.flatMap((assignments) =>
|
|
||||||
assignments.map((a) => ({
|
|
||||||
...a,
|
|
||||||
due_at: a.due_at ? new Date(a.due_at).toLocaleString() : undefined, // timezones?
|
|
||||||
lock_at: a.lock_at ? new Date(a.lock_at).toLocaleString() : undefined, // timezones?
|
|
||||||
}))
|
|
||||||
);
|
);
|
||||||
|
return assignments.map((a) => ({
|
||||||
|
...a,
|
||||||
|
due_at: a.due_at ? new Date(a.due_at).toLocaleString() : undefined, // timezones?
|
||||||
|
lock_at: a.lock_at ? new Date(a.lock_at).toLocaleString() : undefined, // timezones?
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
async create(
|
async create(
|
||||||
@@ -27,7 +25,7 @@ export const canvasAssignmentService = {
|
|||||||
canvasAssignmentGroupId?: number
|
canvasAssignmentGroupId?: number
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
console.log(`Creating assignment: ${localAssignment.name}`);
|
console.log(`Creating assignment: ${localAssignment.name}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/assignments`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/assignments`;
|
||||||
const body = {
|
const body = {
|
||||||
assignment: {
|
assignment: {
|
||||||
name: localAssignment.name,
|
name: localAssignment.name,
|
||||||
@@ -66,7 +64,7 @@ export const canvasAssignmentService = {
|
|||||||
canvasAssignmentGroupId?: number
|
canvasAssignmentGroupId?: number
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log(`Updating assignment: ${localAssignment.name}`);
|
console.log(`Updating assignment: ${localAssignment.name}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${courseId}/assignments/${canvasAssignmentId}`;
|
const url = `${canvasApi}/courses/${courseId}/assignments/${canvasAssignmentId}`;
|
||||||
const body = {
|
const body = {
|
||||||
assignment: {
|
assignment: {
|
||||||
name: localAssignment.name,
|
name: localAssignment.name,
|
||||||
@@ -94,7 +92,7 @@ export const canvasAssignmentService = {
|
|||||||
assignmentName: string
|
assignmentName: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log(`Deleting assignment from Canvas: ${assignmentName}`);
|
console.log(`Deleting assignment from Canvas: ${assignmentName}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${courseId}/assignments/${assignmentCanvasId}`;
|
const url = `${canvasApi}/courses/${courseId}/assignments/${assignmentCanvasId}`;
|
||||||
const response = await axiosClient.delete(url);
|
const response = await axiosClient.delete(url);
|
||||||
|
|
||||||
if (!response.status.toString().startsWith("2")) {
|
if (!response.status.toString().startsWith("2")) {
|
||||||
@@ -134,7 +132,7 @@ export const canvasAssignmentService = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const rubricUrl = `${baseCanvasUrl}/courses/${courseId}/rubrics`;
|
const rubricUrl = `${canvasApi}/courses/${courseId}/rubrics`;
|
||||||
const rubricResponse = await axiosClient.post<CanvasRubricCreationResponse>(
|
const rubricResponse = await axiosClient.post<CanvasRubricCreationResponse>(
|
||||||
rubricUrl,
|
rubricUrl,
|
||||||
rubricBody
|
rubricBody
|
||||||
@@ -142,7 +140,7 @@ export const canvasAssignmentService = {
|
|||||||
|
|
||||||
if (!rubricResponse.data) throw new Error("Failed to create rubric");
|
if (!rubricResponse.data) throw new Error("Failed to create rubric");
|
||||||
|
|
||||||
const assignmentPointAdjustmentUrl = `${baseCanvasUrl}/courses/${courseId}/assignments/${assignmentCanvasId}`;
|
const assignmentPointAdjustmentUrl = `${canvasApi}/courses/${courseId}/assignments/${assignmentCanvasId}`;
|
||||||
const assignmentPointAdjustmentBody = {
|
const assignmentPointAdjustmentBody = {
|
||||||
assignment: { points_possible: assignmentPoints(localAssignment) },
|
assignment: { points_possible: assignmentPoints(localAssignment) },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CanvasModuleItem } from "@/models/canvas/modules/canvasModuleItems";
|
import { CanvasModuleItem } from "@/models/canvas/modules/canvasModuleItems";
|
||||||
import { CanvasPage } from "@/models/canvas/pages/canvasPageModel";
|
import { CanvasPage } from "@/models/canvas/pages/canvasPageModel";
|
||||||
import { axiosClient } from "../axiosUtils";
|
import { axiosClient } from "../axiosUtils";
|
||||||
import { baseCanvasUrl } from "./canvasServiceUtils";
|
import { canvasApi } from "./canvasServiceUtils";
|
||||||
import { CanvasModule } from "@/models/canvas/modules/canvasModule";
|
import { CanvasModule } from "@/models/canvas/modules/canvasModule";
|
||||||
|
|
||||||
export const canvasModuleService = {
|
export const canvasModuleService = {
|
||||||
@@ -11,7 +11,7 @@ export const canvasModuleService = {
|
|||||||
item: CanvasModuleItem
|
item: CanvasModuleItem
|
||||||
) {
|
) {
|
||||||
console.log(`Updating module item ${item.title}`);
|
console.log(`Updating module item ${item.title}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/modules/${canvasModuleId}/items/${item.id}`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/modules/${canvasModuleId}/items/${item.id}`;
|
||||||
const body = {
|
const body = {
|
||||||
module_item: { title: item.title, position: item.position },
|
module_item: { title: item.title, position: item.position },
|
||||||
};
|
};
|
||||||
@@ -28,7 +28,7 @@ export const canvasModuleService = {
|
|||||||
contentId: number | string
|
contentId: number | string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log(`Creating new module item ${title}`);
|
console.log(`Creating new module item ${title}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/modules/${canvasModuleId}/items`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/modules/${canvasModuleId}/items`;
|
||||||
const body = { module_item: { title, type, content_id: contentId } };
|
const body = { module_item: { title, type, content_id: contentId } };
|
||||||
await axiosClient.post(url, body);
|
await axiosClient.post(url, body);
|
||||||
},
|
},
|
||||||
@@ -40,7 +40,7 @@ export const canvasModuleService = {
|
|||||||
canvasPage: CanvasPage
|
canvasPage: CanvasPage
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log(`Creating new module item ${title}`);
|
console.log(`Creating new module item ${title}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/modules/${canvasModuleId}/items`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/modules/${canvasModuleId}/items`;
|
||||||
const body = {
|
const body = {
|
||||||
module_item: { title, type: "Page", page_url: canvasPage.url },
|
module_item: { title, type: "Page", page_url: canvasPage.url },
|
||||||
};
|
};
|
||||||
@@ -48,13 +48,13 @@ export const canvasModuleService = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async getCourseModules(canvasCourseId: number) {
|
async getCourseModules(canvasCourseId: number) {
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/modules`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/modules`;
|
||||||
const response = await axiosClient.get<CanvasModule[]>(url);
|
const response = await axiosClient.get<CanvasModule[]>(url);
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async createModule(canvasCourseId: number, moduleName: string) {
|
async createModule(canvasCourseId: number, moduleName: string) {
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/modules`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/modules`;
|
||||||
const body = {
|
const body = {
|
||||||
module: {
|
module: {
|
||||||
name: moduleName,
|
name: moduleName,
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import { CanvasPage } from "@/models/canvas/pages/canvasPageModel";
|
import { CanvasPage } from "@/models/canvas/pages/canvasPageModel";
|
||||||
import { LocalCoursePage } from "@/models/local/page/localCoursePage";
|
import { LocalCoursePage } from "@/models/local/page/localCoursePage";
|
||||||
import { baseCanvasUrl, canvasServiceUtils } from "./canvasServiceUtils";
|
import { canvasApi, canvasServiceUtils } from "./canvasServiceUtils";
|
||||||
import { markdownToHTMLSafe } from "../htmlMarkdownUtils";
|
import { markdownToHTMLSafe } from "../htmlMarkdownUtils";
|
||||||
import { axiosClient } from "../axiosUtils";
|
import { axiosClient } from "../axiosUtils";
|
||||||
import { rateLimitAwareDelete } from "./canvasWebRequestor";
|
import { rateLimitAwareDelete } from "./canvasWebRequestor";
|
||||||
|
|
||||||
|
|
||||||
export const canvasPageService = {
|
export const canvasPageService = {
|
||||||
async getAll(courseId: number): Promise<CanvasPage[]> {
|
async getAll(courseId: number): Promise<CanvasPage[]> {
|
||||||
console.log("requesting pages");
|
console.log("requesting pages");
|
||||||
const url = `${baseCanvasUrl}/courses/${courseId}/pages`;
|
const url = `${canvasApi}/courses/${courseId}/pages`;
|
||||||
const pages = await canvasServiceUtils.paginatedRequest<CanvasPage[]>({
|
const pages = await canvasServiceUtils.paginatedRequest<CanvasPage[]>({
|
||||||
url,
|
url,
|
||||||
});
|
});
|
||||||
@@ -21,7 +20,7 @@ export const canvasPageService = {
|
|||||||
page: LocalCoursePage
|
page: LocalCoursePage
|
||||||
): Promise<CanvasPage> {
|
): Promise<CanvasPage> {
|
||||||
console.log(`Creating course page: ${page.name}`);
|
console.log(`Creating course page: ${page.name}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/pages`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/pages`;
|
||||||
const body = {
|
const body = {
|
||||||
wiki_page: {
|
wiki_page: {
|
||||||
title: page.name,
|
title: page.name,
|
||||||
@@ -42,7 +41,7 @@ export const canvasPageService = {
|
|||||||
page: LocalCoursePage
|
page: LocalCoursePage
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
console.log(`Updating course page: ${page.name}`);
|
console.log(`Updating course page: ${page.name}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${courseId}/pages/${canvasPageId}`;
|
const url = `${canvasApi}/courses/${courseId}/pages/${canvasPageId}`;
|
||||||
const body = {
|
const body = {
|
||||||
wiki_page: {
|
wiki_page: {
|
||||||
title: page.name,
|
title: page.name,
|
||||||
@@ -54,7 +53,7 @@ export const canvasPageService = {
|
|||||||
|
|
||||||
async delete(courseId: number, canvasPageId: number): Promise<void> {
|
async delete(courseId: number, canvasPageId: number): Promise<void> {
|
||||||
console.log(`Deleting page from canvas ${canvasPageId}`);
|
console.log(`Deleting page from canvas ${canvasPageId}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${courseId}/pages/${canvasPageId}`;
|
const url = `${canvasApi}/courses/${courseId}/pages/${canvasPageId}`;
|
||||||
await rateLimitAwareDelete(url);
|
await rateLimitAwareDelete(url);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,27 +1,38 @@
|
|||||||
import { CanvasQuiz } from "@/models/canvas/quizzes/canvasQuizModel";
|
import { CanvasQuiz } from "@/models/canvas/quizzes/canvasQuizModel";
|
||||||
import { axiosClient } from "../axiosUtils";
|
import { axiosClient } from "../axiosUtils";
|
||||||
import { baseCanvasUrl } from "./canvasServiceUtils";
|
import { canvasApi } from "./canvasServiceUtils";
|
||||||
import { LocalQuiz } from "@/models/local/quiz/localQuiz";
|
import { LocalQuiz } from "@/models/local/quiz/localQuiz";
|
||||||
import { markdownToHTMLSafe } from "../htmlMarkdownUtils";
|
import { markdownToHTMLSafe } from "../htmlMarkdownUtils";
|
||||||
import { getDateFromStringOrThrow } from "@/models/local/timeUtils";
|
import { getDateFromStringOrThrow } from "@/models/local/timeUtils";
|
||||||
import { canvasAssignmentService } from "./canvasAssignmentService";
|
import { canvasAssignmentService } from "./canvasAssignmentService";
|
||||||
import { LocalQuizQuestion } from "@/models/local/quiz/localQuizQuestion";
|
import {
|
||||||
|
LocalQuizQuestion,
|
||||||
|
QuestionType,
|
||||||
|
} from "@/models/local/quiz/localQuizQuestion";
|
||||||
|
import { CanvasQuizQuestion } from "@/models/canvas/quizzes/canvasQuizQuestionModel";
|
||||||
|
|
||||||
const getAnswers = (question: LocalQuizQuestion) => {
|
const getAnswers = (question: LocalQuizQuestion) => {
|
||||||
return question.answers.map((answer: any) => ({
|
if (question.questionType === QuestionType.MATCHING)
|
||||||
answer_html: answer.htmlText,
|
return question.answers.map((a) => ({
|
||||||
|
answer_match_left: a.text,
|
||||||
|
answer_match_right: a.matchedText,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return question.answers.map((answer) => ({
|
||||||
|
answer_html: markdownToHTMLSafe(answer.text),
|
||||||
answer_weight: answer.correct ? 100 : 0,
|
answer_weight: answer.correct ? 100 : 0,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const createQuestionOnly = async (
|
const createQuestionOnly = async (
|
||||||
canvasCourseId: number,
|
canvasCourseId: number,
|
||||||
canvasQuizId: number,
|
canvasQuizId: number,
|
||||||
question: LocalQuizQuestion,
|
question: LocalQuizQuestion,
|
||||||
position: number
|
position: number
|
||||||
): Promise<{ question: any; position: number }> => {
|
) => {
|
||||||
console.log("Creating individual question", question);
|
console.log("Creating individual question"); //, question);
|
||||||
|
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/quizzes/${canvasQuizId}/questions`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/quizzes/${canvasQuizId}/questions`;
|
||||||
const body = {
|
const body = {
|
||||||
question: {
|
question: {
|
||||||
question_text: markdownToHTMLSafe(question.text),
|
question_text: markdownToHTMLSafe(question.text),
|
||||||
@@ -32,7 +43,7 @@ const createQuestionOnly = async (
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await axiosClient.post(url, body);
|
const response = await axiosClient.post<CanvasQuizQuestion>(url, body);
|
||||||
const newQuestion = response.data;
|
const newQuestion = response.data;
|
||||||
|
|
||||||
if (!newQuestion) throw new Error("Created question is null");
|
if (!newQuestion) throw new Error("Created question is null");
|
||||||
@@ -52,7 +63,7 @@ const hackFixQuestionOrdering = async (
|
|||||||
id: qp.question.id.toString(),
|
id: qp.question.id.toString(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/quizzes/${canvasQuizId}/reorder`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/quizzes/${canvasQuizId}/reorder`;
|
||||||
await axiosClient.post(url, { order });
|
await axiosClient.post(url, { order });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -65,21 +76,43 @@ const hackFixRedundantAssignments = async (canvasCourseId: number) => {
|
|||||||
assignment.submission_types.includes("online_quiz")
|
assignment.submission_types.includes("online_quiz")
|
||||||
);
|
);
|
||||||
|
|
||||||
const deletionTasks = assignmentsToDelete.map((assignment) =>
|
await Promise.all(
|
||||||
canvasAssignmentService.delete(
|
assignmentsToDelete.map(
|
||||||
canvasCourseId,
|
async (assignment) =>
|
||||||
assignment.id,
|
await canvasAssignmentService.delete(
|
||||||
assignment.name
|
canvasCourseId,
|
||||||
|
assignment.id,
|
||||||
|
assignment.name
|
||||||
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
await Promise.all(deletionTasks);
|
|
||||||
console.log(`Deleted ${assignmentsToDelete.length} redundant assignments`);
|
console.log(`Deleted ${assignmentsToDelete.length} redundant assignments`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createQuizQuestions = async (
|
||||||
|
canvasCourseId: number,
|
||||||
|
canvasQuizId: number,
|
||||||
|
localQuiz: LocalQuiz
|
||||||
|
) => {
|
||||||
|
console.log("Creating quiz questions"); //, localQuiz);
|
||||||
|
|
||||||
|
const tasks = localQuiz.questions.map(
|
||||||
|
async (question, index) =>
|
||||||
|
await createQuestionOnly(canvasCourseId, canvasQuizId, question, index)
|
||||||
|
);
|
||||||
|
const questionAndPositions = await Promise.all(tasks);
|
||||||
|
await hackFixQuestionOrdering(
|
||||||
|
canvasCourseId,
|
||||||
|
canvasQuizId,
|
||||||
|
questionAndPositions
|
||||||
|
);
|
||||||
|
await hackFixRedundantAssignments(canvasCourseId);
|
||||||
|
};
|
||||||
|
|
||||||
export const canvasQuizService = {
|
export const canvasQuizService = {
|
||||||
async getAll(canvasCourseId: number): Promise<CanvasQuiz[]> {
|
async getAll(canvasCourseId: number): Promise<CanvasQuiz[]> {
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/quizzes`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/quizzes`;
|
||||||
const response = await axiosClient.get<CanvasQuiz[]>(url);
|
const response = await axiosClient.get<CanvasQuiz[]>(url);
|
||||||
return response.data.map((quiz) => ({
|
return response.data.map((quiz) => ({
|
||||||
...quiz,
|
...quiz,
|
||||||
@@ -97,7 +130,7 @@ export const canvasQuizService = {
|
|||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
console.log("Creating quiz", localQuiz);
|
console.log("Creating quiz", localQuiz);
|
||||||
|
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/quizzes`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/quizzes`;
|
||||||
const body = {
|
const body = {
|
||||||
quiz: {
|
quiz: {
|
||||||
title: localQuiz.name,
|
title: localQuiz.name,
|
||||||
@@ -124,31 +157,14 @@ export const canvasQuizService = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await axiosClient.post(url, body);
|
const { data: canvasQuiz } = await axiosClient.post<CanvasQuiz>(url, body);
|
||||||
const canvasQuiz: CanvasQuiz = response.data;
|
|
||||||
|
|
||||||
if (!canvasQuiz) throw new Error("Created quiz is null");
|
if (!canvasQuiz) throw new Error("Created quiz is null");
|
||||||
|
|
||||||
await this.createQuizQuestions(canvasCourseId, canvasQuiz.id, localQuiz);
|
await createQuizQuestions(canvasCourseId, canvasQuiz.id, localQuiz);
|
||||||
return canvasQuiz.id;
|
return canvasQuiz.id;
|
||||||
},
|
},
|
||||||
|
async delete(canvasCourseId: number, canvasQuizId: number) {
|
||||||
async createQuizQuestions(
|
const url = `${canvasApi}/courses/${canvasCourseId}/quizzes/${canvasQuizId}`;
|
||||||
canvasCourseId: number,
|
await axiosClient.delete(url);
|
||||||
canvasQuizId: number,
|
|
||||||
localQuiz: LocalQuiz
|
|
||||||
) {
|
|
||||||
console.log("Creating quiz questions", localQuiz);
|
|
||||||
|
|
||||||
const tasks = localQuiz.questions.map((question, index) =>
|
|
||||||
createQuestionOnly(canvasCourseId, canvasQuizId, question, index)
|
|
||||||
);
|
|
||||||
const questionAndPositions = await Promise.all(tasks);
|
|
||||||
await hackFixQuestionOrdering(
|
|
||||||
canvasCourseId,
|
|
||||||
canvasQuizId,
|
|
||||||
questionAndPositions
|
|
||||||
);
|
|
||||||
await hackFixRedundantAssignments(canvasCourseId);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
import { CanvasEnrollmentTermModel } from "@/models/canvas/enrollmentTerms/canvasEnrollmentTermModel";
|
import { CanvasEnrollmentTermModel } from "@/models/canvas/enrollmentTerms/canvasEnrollmentTermModel";
|
||||||
import { baseCanvasUrl, canvasServiceUtils } from "./canvasServiceUtils";
|
import { canvasApi, canvasServiceUtils } from "./canvasServiceUtils";
|
||||||
import { CanvasCourseModel } from "@/models/canvas/courses/canvasCourseModel";
|
import { CanvasCourseModel } from "@/models/canvas/courses/canvasCourseModel";
|
||||||
import { CanvasModuleItem } from "@/models/canvas/modules/canvasModuleItems";
|
import { CanvasModuleItem } from "@/models/canvas/modules/canvasModuleItems";
|
||||||
import { CanvasPage } from "@/models/canvas/pages/canvasPageModel";
|
import { CanvasPage } from "@/models/canvas/pages/canvasPageModel";
|
||||||
import { CanvasEnrollmentModel } from "@/models/canvas/enrollments/canvasEnrollmentModel";
|
import { CanvasEnrollmentModel } from "@/models/canvas/enrollments/canvasEnrollmentModel";
|
||||||
import { axiosClient } from "../axiosUtils";
|
import { axiosClient } from "../axiosUtils";
|
||||||
|
|
||||||
|
|
||||||
const getAllTerms = async () => {
|
const getAllTerms = async () => {
|
||||||
const url = `${baseCanvasUrl}/accounts/10/terms`;
|
const url = `${canvasApi}/accounts/10/terms`;
|
||||||
const { data } = await axiosClient.get<
|
const { data } = await axiosClient.get<{
|
||||||
{
|
enrollment_terms: CanvasEnrollmentTermModel[];
|
||||||
enrollment_terms: CanvasEnrollmentTermModel[];
|
}>(url);
|
||||||
}
|
|
||||||
>(url);
|
|
||||||
const terms = data.enrollment_terms;
|
const terms = data.enrollment_terms;
|
||||||
return terms;
|
return terms;
|
||||||
};
|
};
|
||||||
@@ -21,7 +18,7 @@ const getAllTerms = async () => {
|
|||||||
export const canvasService = {
|
export const canvasService = {
|
||||||
getAllTerms,
|
getAllTerms,
|
||||||
async getCourses(termId: number) {
|
async getCourses(termId: number) {
|
||||||
const url = `${baseCanvasUrl}/courses`;
|
const url = `${canvasApi}/courses`;
|
||||||
const response = await axiosClient.get<CanvasCourseModel[]>(url);
|
const response = await axiosClient.get<CanvasCourseModel[]>(url);
|
||||||
const allCourses = response.data;
|
const allCourses = response.data;
|
||||||
const coursesInTerm = allCourses
|
const coursesInTerm = allCourses
|
||||||
@@ -31,7 +28,7 @@ export const canvasService = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async getCourse(courseId: number): Promise<CanvasCourseModel> {
|
async getCourse(courseId: number): Promise<CanvasCourseModel> {
|
||||||
const url = `${baseCanvasUrl}/courses/${courseId}`;
|
const url = `${canvasApi}/courses/${courseId}`;
|
||||||
const { data } = await axiosClient.get<CanvasCourseModel>(url);
|
const { data } = await axiosClient.get<CanvasCourseModel>(url);
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
@@ -56,10 +53,9 @@ export const canvasService = {
|
|||||||
return currentTerms;
|
return currentTerms;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
async getEnrolledStudents(canvasCourseId: number) {
|
async getEnrolledStudents(canvasCourseId: number) {
|
||||||
console.log(`Getting students for course ${canvasCourseId}`);
|
console.log(`Getting students for course ${canvasCourseId}`);
|
||||||
const url = `${baseCanvasUrl}/courses/${canvasCourseId}/enrollments?enrollment_type=student`;
|
const url = `${canvasApi}/courses/${canvasCourseId}/enrollments?enrollment_type=student`;
|
||||||
const { data } = await axiosClient.get<CanvasEnrollmentModel[]>(url);
|
const { data } = await axiosClient.get<CanvasEnrollmentModel[]>(url);
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
import { AxiosResponseHeaders, RawAxiosResponseHeaders } from "axios";
|
import { AxiosResponseHeaders, RawAxiosResponseHeaders } from "axios";
|
||||||
import { axiosClient } from "../axiosUtils";
|
import { axiosClient } from "../axiosUtils";
|
||||||
|
|
||||||
|
export const baseCanvasUrl = "https://snow.instructure.com";
|
||||||
export const baseCanvasUrl = "https://snow.instructure.com/api/v1";
|
export const canvasApi = baseCanvasUrl + "/api/v1";
|
||||||
|
|
||||||
const getNextUrl = (
|
const getNextUrl = (
|
||||||
headers: AxiosResponseHeaders | RawAxiosResponseHeaders
|
headers: AxiosResponseHeaders | RawAxiosResponseHeaders
|
||||||
|
|||||||
Reference in New Issue
Block a user