Skip to main content
Glama
gradeWithRubric.ts4.28 kB
import { canvasPutForm } from "../../client.js"; export interface GradeWithRubricInput { courseIdentifier: string | number; assignmentId: string | number; userId: string | number; rubricAssessment?: Record<string, { points: number; ratingId?: string; comments?: string; }>; grade?: string | number; comment?: string; } export interface GradeResponse { id: number; user_id: number; assignment_id: number; score: number; grade: string; } /** * Validate rubric assessment input */ function validateRubricAssessment( rubricAssessment: Record<string, any> ): void { for (const [criterionId, assessment] of Object.entries(rubricAssessment)) { if (!assessment.points && assessment.points !== 0) { throw new Error( `Criterion "${criterionId}" is missing required "points" field` ); } if (typeof assessment.points !== 'number') { throw new Error( `Criterion "${criterionId}" points must be a number, got ${typeof assessment.points}` ); } if (assessment.points < 0) { throw new Error( `Criterion "${criterionId}" points cannot be negative: ${assessment.points}` ); } } } /** * Convert rubric assessment to Canvas form-encoded format */ function buildRubricAssessmentFormData( rubricAssessment: Record<string, { points: number; ratingId?: string; comments?: string; }>, comment?: string ): Record<string, string> { const formData: Record<string, string> = {}; // Transform rubric_assessment object into Canvas's form-encoded format for (const [criterionId, assessment] of Object.entries(rubricAssessment)) { // Points are required formData[`rubric_assessment[${criterionId}][points]`] = String(assessment.points); // Rating ID is optional but recommended if (assessment.ratingId) { formData[`rubric_assessment[${criterionId}][rating_id]`] = assessment.ratingId; } // Comments are optional if (assessment.comments) { formData[`rubric_assessment[${criterionId}][comments]`] = assessment.comments; } } // Add optional overall comment if (comment) { formData["comment[text_comment]"] = comment; } return formData; } /** * Grade a single submission using a rubric. * * Makes direct Canvas API calls with form-encoded data. * The rubric must already be associated with the assignment. * Criterion IDs in Canvas often start with underscore (e.g., "_8027"). * * @param input - Grading parameters * @returns Canvas API response with graded submission data * * @throws Error if rubric assessment is invalid * * @example * ```typescript * await gradeWithRubric({ * courseIdentifier: "60366", * assignmentId: "123", * userId: "456", * rubricAssessment: { * "_8027": { points: 100, comments: "Excellent work!" } * }, * comment: "Great submission overall" * }); * ``` */ export async function gradeWithRubric( input: GradeWithRubricInput ): Promise<GradeResponse> { const { courseIdentifier, assignmentId, userId, rubricAssessment, grade, comment } = input; // Validate: Must have either rubricAssessment OR grade if (!rubricAssessment && !grade && grade !== 0) { throw new Error('Must provide either rubricAssessment or grade'); } let formData: Record<string, string> = {}; // Handle rubric-based grading if (rubricAssessment && Object.keys(rubricAssessment).length > 0) { validateRubricAssessment(rubricAssessment); formData = buildRubricAssessmentFormData(rubricAssessment, comment); } // Handle simple grading else if (grade !== undefined) { formData['submission[posted_grade]'] = String(grade); if (comment) { formData['comment[text_comment]'] = comment; } } // Canvas API endpoint for updating submission const endpoint = `/courses/${courseIdentifier}/assignments/${assignmentId}/submissions/${userId}`; try { // Submit the grade with rubric assessment using form encoding const response = await canvasPutForm<GradeResponse>(endpoint, formData); return response; } catch (error: any) { throw new Error( `Failed to grade submission: ${error.message}\n` + `Check that rubric is configured for grading and criterion IDs are correct.` ); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/vishalsachdev/canvas-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server