Backend: Form – Modul 5 – Usmeni ispit
Last modified with commit 54c7b1e on
by TjasaSimovic.
Backend task for the oral exam grading form (GET/POST /btc-certifications/{certificationId}/candidates/{candidateId}/oral-exam) – check the criterion keys, 0–5 scale, calculations and Zaključeno rule against the API spec and DB structure, komisija access, implement, and verify against the spec.
Docs: Form – Modul 5 – Usmeni ispit
Spec: GET /btc-certifications/{certificationId}/candidates/{candidateId}/oral-exam (getBtcOralExamForm), POST …/oral-exam (saveBtcOralExamForm), BtcOralExamForm, BtcOralExamSaveRequest, BtcOralExamCriterion, BtcOralExamGrade in api-docs/openapi.yaml
DB: BtcCertificationGrades, BtcCertificationBoardMembers, BtcCertificationCandidates in api/database/create_schema.sql
1. Double check API spec and DB structure
Criterion keys: the docs inputs map to the 8
BtcOralExamCriterionvalues and the DBCHECK:- a) Prezentacija poslovnog slučaja – 1 input, Ocjena prezentacije →
presentation(its 5 descriptive criteria have no inputs) - b) Igranje uloga – 7 inputs, one per category (the bullet points are guidelines only):
Komunikacija (5 bullets) →
communication, Empatija / Tolerancija (4) →empathy, Osjećaj odgovornosti (4) →responsibility, Samopouzdanje (4) →self-confidence, Izgled (2) →appearance, Poznavanje proizvoda (4) →product-knowledge, Prodajni procesi (1) →sales-process
Agree the keys with the frontend.
- a) Prezentacija poslovnog slučaja – 1 input, Ocjena prezentacije →
Scale: 0–5 with one decimal: spec (
minimum0,maximum5,multipleOf0.1), DB (DECIMAL(2,1),CHECK BETWEEN 0 AND 5) and docs agree.Calculations (spec, unrounded; the frontend rounds for display):
rolePlayGrade(grade b) = average of the entered category grades; null if nonefinalGrade= (a × 3 + b × 7) / 10, only when grade a and grade b both have a value
The docs comment leaves open whether b and the final grade are calculated from partial input. The spec follows the docs recommendation (from the filled values). Confirm.
Zaključeno:
isCompletedwhenpresentationand all 7 categories are entered.Save semantics: the body is the full form state; criteria left out are cleared. Check this matches “Spremi” / “Spremi i zatvori” (both send the whole form).
Access: only active komisija members of the certificiranje (docs: “Only komisija members … can open it”). A Trainer not in the komisija and a removed member → 403. Allowed in any status, also after Zaključeno.
Response sufficiency: the form header needs the certificiranje name and the docs recommend showing the candidate in the footer. The response has only
candidateId; the frontend gets both from the detail endpoint. Confirm, or add them here.Roles and permissions:
btc-certifications.access+ active membership.DB:
UQ_BtcCertificationGrades (CandidateId, BoardMemberId, CriterionKey)keeps one row per criterion even when the form is saved twice at once.
2. Implementation
- Roles and permissions: require
btc-certifications.access→ 403, then active membership of the caller → 403. - Shared helper: one function that takes a member’s grades for a candidate and returns
rolePlayGrade,finalGradeandisCompleted. The detail and results endpoints use the same helper. - GET: unknown / deleted certificiranje, or a candidate of another certificiranje → 404. Return the caller’s grades and the calculated values.
- POST: validation → 400 (unknown
criterionKey, a key twice, grade outside 0–5 or more than one decimal). OneMERGE, no transaction: target = the caller’s rows for this candidate, source = the body; update matches (UpdatedAt), insert new ones, delete the rest. Unique violation (saved twice at once) → retry once; FK error 547 (candidate removed in the meantime) → 404. Return the saved form.
3. Verify implementation against spec
- Shapes and status codes match
getBtcOralExamFormandsaveBtcOralExamForm. - Hand-calculated example: a = 4, categories 3, 4, 5, 3, 4, 4, 5 → b = 4, final = 4.0, Zaključeno. Remove one category → b = average of 6, Nije zaključeno. Remove a → final null.
- Grade 5.5, 3.25 or “x” → 400.
- Two members grading the same candidate don’t see each other’s grades.
- Trainer not in komisija → 403. Removed member → 403. Past certificiranje → save still works.
Done when
Each komisija member can fill in and change their own Usmeni ispit grades per candidate, with grade b, the final grade and Zaključeno calculated as in the docs.