Backend: Assessment results
GitHub Issue: #228
Last modified with commit 7a580a0 on
by Aljaž.
Backend task for the results page (GET /assessments/{assessmentId}/results, POST /written-test-grades) – check the calculation rules against the API spec and DB structure, permissions, implement, and verify with a hand-calculated example.
Docs: Assessment results
Spec: GET /assessments/{assessmentId}/results (getAssessmentResults), POST /assessments/{assessmentId}/written-test-grades (setAssessmentWrittenTestGrades), AssessmentResults, AssessmentWrittenTestGrade in api-docs/openapi.yaml
DB: AssessmentGrades, AssessmentCandidates, AssessmentWrittenTestGrades, AssessmentBoardMembers in api/database/create_schema.sql
1. Double check API spec and DB structure
- Each results table row in the docs maps to a field: Grupna diskusija →
groupDiscussion, prvi kontakt →firstContact, argumentacija →argumentation, Pismena provjera znanja →writtenTest, Zbroj →sum, Intervju →interview, UKUPNO →total. - Calculation rules in the spec vs the docs:
- activity value = the average POGODNOST across board members who entered grades (including
removed members);
nullif there are none - argumentation disabled → copy firstContact
- Zbroj = the average of the non-null group discussion, first contact, argumentation and written test values (calculated even when some are missing; the docs follow the spec, not Figma)
- UKUPNO = the average of the non-null Zbroj and Intervju
- unrounded values (docs recommendation). Write this into the spec.
- activity value = the average POGODNOST across board members who entered grades (including
removed members);
- Pregled unosa članova komisije: the spec lists only graded activities (argumentation only if enabled; no presentation / interview-notes). The docs say this is an open question. Confirm. An individual activity counts as completed only when it’s completed for every candidate.
- Header data (brand · date · name): from the detail endpoint, or add it here?
- CSV export is done in the frontend. Confirm the response has everything the CSV needs.
- Roles and permissions: results are visible to Trainers (
assessments.access-all) and to evaluators in the komisija. Editing the written test grade needsassessments.edit. Check against the docs. - DB:
AssessmentWrittenTestGrades.Grade DECIMAL(2,1)0–3, one row per candidate that has a grade (no row = no grade). The docs only guess this range from Figma. Confirm. It’s a separate table so its FK blocks removing a candidate who has a grade. Grades of removed board members stay in AssessmentGrades and are counted.
2. Implementation
- Roles and permissions: GET →
assessments.access-allor an active board member; POST →assessments.edit. - DB: one query for all grades of the assessment, then calculate in code with the shared helpers (category → POGODNOST per member and candidate → average across members).
- Board member overview: active members only, using each form’s status helper (Grupna diskusija, prvi kontakt, argumentacija, Strukturirani intervju – ocijene).
-
POST /written-test-grades: update only the listed candidates (nullclears the grade); eachcandidateIdmust belong to the assessment (404); grade validated 0–3 with one decimal; return the recalculated results. OneMERGEon AssessmentWrittenTestGrades, no transaction: a value inserts or updates the row,nulldeletes it. FK error 547 (candidate removed in the meantime) → 404; unique violation (saved twice at once) → retry once.
3. Verify implementation against spec
- Response shapes and status codes match both operations.
- Results checked against an example calculated by hand: 2 board members (one removed after grading), 3 candidates, some grades missing, argumentation on and off.
- A candidate with no grades at all → every activity field and
sum/totalarenull. - Evaluator → POST 403. Writing a grade of 3.25 or 4 → 400.
Done when
The results page and CSV can be built from the API, and the numbers match the hand-calculated example.