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

01 — Product Overview (Results Module)

StudyLyon — multi-tenant ERP / School Management API. This package designs the Results module client (Flutter, forward-looking spec) against the implemented NestJS backend. Every endpoint, DTO field, schema, index, event, and permission is derived directly from src/modules/results/**, src/modules/exams/**, src/infrastructure/bullmq/event-queue-map.ts, src/modules/rbac/permissions.constants.ts, and docs/IMPLEMENTATION_PLAN.md. No feature is invented; gaps are flagged in Open Questions and marked (planned) / (forward-looking) / (proposed) inline. The PRD keeps the mobile client out of Phase 1 — this entire package is the (forward-looking) client design against the v1 API.


1. Purpose

Results is the grading and reporting layer of the academic cycle. Teachers enter marks per (exam-subject, student), the backend computes totals, percentage, and an overall grade per report card, and publication releases results to stakeholders. The module owns the examination_results collection (examination-result.schema.ts:7-8) but marks are written by the Exams module service (examination.service.ts:176-182); the Results controller proxies marks entry into that service (result.controller.ts:26-31).

ResponsibilitySource
List results for one studentresult.controller.ts:16-20result.service.ts:36-38
List results for one exam-subjectresult.controller.ts:21-25result.service.ts:40-44
Enter/update marks (idempotent upsert)result.controller.ts:26-31examination.service.ts:139-195
Report card per (student, exam)result.controller.ts:32-37result.service.ts:46-98
Publish all results of an examexamination.controller.ts:54-56examination.service.ts:203-220

2. Scope

  • Marks entry per exam-subject, one student at a time, with idempotent re-entry (upsert, examination.service.ts:147-175).
  • Result lookup by student and by exam-subject.
  • Report card generation: per-subject marks, totals, percentage (2 decimals, result.service.ts:83-86), overall grade via deterministic bands (result.service.ts:130-138).
  • Publication of an exam's results, which stamps publishedAt on every result row and flips the exam to status: 'published' (examination.service.ts:203-220).
  • Notification of publication via ExamResultsPublishedin-app queue / results-published job (event-queue-map.ts:27).

3. Out of scope (Phase 1 backend)

  • Student/parent portal visibility — no per-role visibility filter exists in code; every endpoint is behind JwtAuthGuard only (result.controller.ts:10). Blueprint Results.md "before publish, results hidden" is not implemented (see 4.2).
  • Rank computation — blueprint Results.md:54 says "Rank computed per class per exam", but no rank logic exists in result.service.ts. (planned)
  • Analytics — blueprint lists GET /api/v1/results/analytics (Results.md:28); no controller route exists. (proposed)
  • PDF report card export — blueprint assigns it to the Reports module (Results.md:57); no implementation. (planned)
  • Mobile/Flutter client — the PRD keeps mobile out of Phase 1; this spec is the forward-looking client design against the v1 API. (forward-looking)
  • RBAC permissionspermissions.constants.ts:1-97 contains no result.* or exam.* permissions; the blueprint's result.read/compute/publish/report (Results.md:62-69) are unimplemented. (planned)

4. Key behavioural facts from source

  1. Marks upsert is idempotent: examination.service.ts:147-150 looks up by (studentId, examinationSubjectId); existing row is updated in place (:152-161), otherwise created (:176-182). A client can safely re-submit.
  2. Unique index: {tenantId, studentId, examinationSubjectId} unique (examination-result.schema.ts:31-33) — the DB-level backstop for the upsert.
  3. Missing marks count as 0 in the report card (result.service.ts:70 result?.marksObtained ?? 0), but totalMaximumMarks still sums every subject (:72). A student with no entries is graded F, not excluded.
  4. Over-maximum marks throw 404 (examination.service.ts:145-146, NotFoundException) — a quirk: business-rule violations should be 422 BUSINESS_RULE_VIOLATION, but this path returns 404 RESOURCE_NOT_FOUND. The client must treat both as "marks rejected" (see 14_QA_Checklist.md).
  5. Publication is not an immutability lock: publishResults (examination.service.ts:203-220) only stamps publishedAt and sets exam status; marks can still be upserted afterwards. There is no "frozen" check in enterMarks.
  6. Overall grade bands (result.service.ts:130-138): A+ ≥ 90, A ≥ 80, B+ ≥ 70, B ≥ 60, C ≥ 50, D ≥ 40, F < 40 — only the overall grade is computed server-side. Per-subject grade is client-supplied (optional string in EnterMarksDto, examination-subject.dto.ts:57-60) and stored verbatim (examination.service.ts:157).
  7. Report card subject name is the subject ID: subjectName: sub.subjectId.toString() (result.service.ts:75) — no populate/join; the client must resolve display names locally.

5. Success metrics

  • Marks entry converges in ≤ 2 attempts (idempotent upsert → no duplicate/conflict errors).
  • Report card always renders even with missing marks (0-fill) or zero subjects (404 → guided empty state).
  • Publish action visibly stamps results and fires the in-app results-published notification.

6. Open questions / gaps

#GapStatus
1No role-based visibility: students/parents can't be restricted to their own results(planned) — RBAC guards not yet implemented (AGENTS.md)
2publishedAt is never filtered on read paths (examination-result.repository.ts:20-40)(planned)
3Per-subject grade computed? Currently client-sent; blueprint implies server gradingOpen question
4Rank/percentile per class per exam(planned)
5Bulk marks entry (grid POST) — only per-student enterMarks exists(planned)/:id/marks-import in IMPLEMENTATION_PLAN.md:219
6Analytics endpoint(proposed)