Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

03 — User Journey (Results Module)

Journeys map to real endpoints; async hops go through BullMQ (event-queue-map.ts:27ExamResultsPublishedin-app / results-published). States marked (planned) are not implemented server-side.


Journey A — Teacher: enter marks for one exam-subject (happy path)

  1. Open exam → subject list: GET /api/v1/examinations/:id/subjects (examination.controller.ts:51-53).
  2. Open subject detail: shows date, startTime/endTime, maximumMarks, passingMarks (examination-subject.schema.ts:18-31).
  3. Load existing entries: GET /api/v1/results/exam-subject/:examSubjectId (result.controller.ts:21-25).
  4. Enter marks per student: POST /api/v1/results/exam-subject/:examSubjectId/marks with EnterMarksDto (examination-subject.dto.ts:47-65).
  5. Re-enter to correct → same endpoint, server updates in place (idempotent upsert, examination.service.ts:147-175); no 409 duplicates.
  6. Client keeps local draft; syncs in background (see 15_Flutter_Implementation_Guide.md §Offline).

Journey A′ — rejected marks (error path)

  • Marks > maximumMarks → server throws 404 RESOURCE_NOT_FOUND (examination.service.ts:145-146). Client normalises to "marks cannot exceed maximum" and keeps the field in error state. See 14_QA_Checklist.md §5 for the 404-vs-422 quirk.
  • Missing/invalid studentId or examSubjectIdVALIDATION_ERROR (400) per 07_API_Conventions.md.

Journey B — Coordinator: verify & publish exam results

  1. Open exam detail: GET /api/v1/examinations/:id (examination.controller.ts:33-35).
  2. Check status (draft | active | completed | published, examination.schema.ts:28-33).
  3. Inspect subject-wise completion: GET /api/v1/examinations/:id/subjects + per-subject results (Journey A step 3).
  4. Publish: POST /api/v1/examinations/:id/publish (examination.controller.ts:54-56).
    • Server stamps publishedAt on every result row of every subject (examination.service.ts:207), sets exam status: 'published' (:209-211), emits ExamResultsPublished (:212-219) → in-app queue → results-published job (event-queue-map.ts:27).
  5. Student/parent in-app notification delivered (async — job on in-app queue).
  6. ⚠️ No un-publish endpoint exists. No immutability lock after publish (enterMarks still works) — by design for now, flagged in 14_QA_Checklist.md §2.

Journey C — Student: view report card

  1. Open results list: GET /api/v1/results/student/:studentId (result.controller.ts:16-20) → list of examination_results rows with marksObtained, grade, remarks, publishedAt (examination-result.schema.ts:15-25).
  2. Open report card for an exam: GET /api/v1/results/report-card/:studentId/:examId (result.controller.ts:32-37).
  3. Read: per-subject marksObtained/maximumMarks/grade/remarks; totals; percentage (2 decimals); overallGrade (A+…F bands, result.service.ts:130-138); generatedAt (result.service.ts:96).
  4. Missing entries render as 0 with grade F (result.service.ts:70) — client should visually distinguish "0 entered" from "not entered" (data limitation: cannot distinguish; see 14_QA_Checklist.md §3).
  5. ⚠️ subjectName is the raw subject ID (result.service.ts:75) — client maps to display names.
  6. Empty exam (no subjects) → 404 No subjects found for this examination. (result.service.ts:51-52) → client shows empty state.

Journey D — Admin: create exam + subjects (precondition to A)

  1. POST /api/v1/examinations (examination.controller.ts:27-29) — always starts status: 'draft' (examination.service.ts:48); body per CreateExaminationDto (examination.dto.ts:4-26): academicYearId, name, type (midterm|final|unit_test|quarterly|other), startDate, endDate.
  2. POST /api/v1/examinations/:id/subjects (examination.controller.ts:45-50) with CreateExaminationSubjectDto (examination-subject.dto.ts:11-45): subjectId, classId, date, startTime, endTime, maximumMarks (≥1), passingMarks (≥1).
  3. Marks entry enabled (Journey A).

Journey E — (planned) bulk marks import

  • POST /api/v1/examinations/:id/marks-import — spreadsheet import with validation + rollback (IMPLEMENTATION_PLAN.md:219). (planned)
  • GET /api/v1/examinations/:id/schedule (hall tickets/seating IMPLEMENTATION_PLAN.md:216-218) (planned).

Flow summary

flowchart LR
  A[Exam created draft] --> B[Subjects added]
  B --> C[Marks entered per student]
  C -->|POST marks| D{valid <= maximumMarks?}
  D -->|no| E[404 → client shows 'exceeds max']
  D -->|yes| F[Upsert examination_results row]
  F --> G[Coordinator: POST publish]
  G --> H[stamp publishedAt + exam status=published]
  H --> I[ExamResultsPublished event]
  I --> J[in-app queue → results-published job]
  C --> K[Report card GET]
  K --> L[Totals + % + overallGrade computed]