Skip to main content

Grading Module

Location: backend/src/grading/

The grading module manages two core entities:

  1. Assessments - exam or coursework items within a term and subject (e.g., "Mid-term Exam", "Take Home Project")
  2. Grades - individual student scores for each assessment

This module uses Supabase Row-Level Security (RLS) to enforce that teachers can only create/modify assessments and grades for subjects they're assigned to.

Files

FilePurpose
grading.module.tsModule definition
assessment.controller.tsAssessment API endpoints
assessment.service.tsAssessment business logic
grade.controller.tsGrade API endpoints
grade.service.tsGrade business logic
dto/create-assessment.dto.tsAssessment creation validation
dto/update-assessment.dto.tsAssessment update validation
dto/create-grade.dto.tsSingle grade creation
dto/update-grade.dto.tsGrade update
dto/bulk-grade.dto.tsBulk grade creation/update
dto/exclude.dto.tsGrade/assessment exclusion toggle

Assessment Data Model

FieldTypeDescription
idUUIDPrimary key
term_idUUIDThe term this assessment belongs to
subject_idUUIDThe subject being assessed
titlestringAssessment name (e.g., "Mid-term Exam")
assessment_typeenumexam or coursework
assessment_datedate?Optional date of the assessment
max_scorenumberMaximum possible score (default: 100)
weightnumber?Weight within its type category
is_excludedbooleanIf true, excluded from calculations
exclusion_reasonstring?Why the assessment was excluded
sort_ordernumber?Display ordering

Grade Data Model

FieldTypeDescription
idUUIDPrimary key
assessment_idUUIDThe assessment this grade belongs to
student_idUUIDThe student being graded
scorenumberThe student's score
letter_gradestring?Auto-calculated letter grade
remarksstring?Optional teacher remarks
is_excludedbooleanIf true, excluded from calculations
exclusion_reasonstring?Why the grade was excluded
created_byUUIDTeacher who created the grade
updated_byUUIDTeacher who last updated the grade

Row-Level Security (RLS)

Unlike other modules that use the service role client, the grading module uses createUserClient(token, 'grading') for write operations. This means:

  • Teachers can only create/modify assessments for subjects they're assigned to teach
  • RLS policies on the grading schema enforce this at the database level
  • If a teacher tries to grade a subject they don't teach, a 403 Forbidden error is returned

Read operations that need to join student data use the service role client to bypass RLS.

Exclusion System

Both assessments and individual grades can be excluded from calculations:

  • Excluded Assessment: The entire assessment is skipped when computing term grades. Useful if an assessment is cancelled.
  • Excluded Grade: An individual student's grade is skipped. Useful if a student was absent or excused.

The ExcludeDto is shared between assessment and grade exclusion.

Assessment Endpoints

All endpoints require AuthGuard.

GET /api/assessments?termId=<id>&subjectId=<id>

Returns all assessments for a term and subject, ordered by sort_order.


GET /api/assessments/:id

Returns a single assessment.


POST /api/assessments

Creates a new assessment. RLS enforced - only the assigned teacher can create.

Body:

{
"termId": "uuid",
"subjectId": "uuid",
"title": "Mid-term Exam",
"assessmentType": "exam",
"maxScore": 100,
"weight": 1,
"sortOrder": 1
}

PATCH /api/assessments/:id

Updates an assessment. RLS enforced.


PATCH /api/assessments/:id/exclude

Toggles the exclusion status of an assessment.

Body:

{
"isExcluded": true,
"exclusionReason": "Assessment cancelled due to weather"
}

DELETE /api/assessments/:id

Deletes an assessment and all its grades. RLS enforced.

Grade Endpoints

All endpoints require AuthGuard.

GET /api/grades?assessmentId=<id>

Returns all grades for an assessment, enriched with student names, sorted by last name.


GET /api/grades/by-term?termId=<id>&subjectId=<id>

Returns all assessments for a term + subject, with nested grades and student data. This powers the grading sheet view.

Response structure:

[
{
"id": "assessment-uuid",
"title": "Mid-term Exam",
"max_score": 100,
"assessment_type": "exam",
"grades": [
{
"id": "grade-uuid",
"student_id": "uuid",
"score": 85,
"remarks": "Good work",
"is_excluded": false,
"student": {
"id": "uuid",
"first_name": "Jane",
"last_name": "Doe"
}
}
]
}
]

POST /api/grades

Creates a single grade. RLS enforced.

Body:

{
"assessmentId": "uuid",
"studentId": "uuid",
"score": 85,
"remarks": "Good work"
}

Error: 409 Conflict if a grade already exists for this student + assessment.


POST /api/grades/bulk

Creates or updates multiple grades at once. Uses upsert on (assessment_id, student_id), so existing grades are updated in place.

Body:

{
"assessmentId": "uuid",
"grades": [
{ "studentId": "uuid1", "score": 85, "remarks": "Good" },
{ "studentId": "uuid2", "score": 72 }
]
}

PATCH /api/grades/:id

Updates a grade's score or remarks. RLS enforced.


PATCH /api/grades/:id/exclude

Toggles the exclusion status of a grade.

Body:

{
"isExcluded": true,
"exclusionReason": "Student was absent"
}