Phase 7: results & review

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jānis Kacēns
2026-05-11 16:47:01 +03:00
parent 968479ff51
commit 715c1e4fe5
2 changed files with 148 additions and 14 deletions
+79
View File
@@ -214,6 +214,20 @@ func (h *TestHandler) QuestionPost(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, fmt.Sprintf("/test/%d/results", test.ID), http.StatusSeeOther)
}
// ResultItem holds per-question data for the results page.
type ResultItem struct {
Question *models.Question
Answers []*ResultAnswer
UserRight bool // user selected the correct answer
Unanswered bool // user skipped without selecting
}
// ResultAnswer annotates each answer with display markers.
type ResultAnswer struct {
*models.Answer
UserPicked bool // user selected this answer
}
func (h *TestHandler) ResultsGet(w http.ResponseWriter, r *http.Request) {
user := auth.UserFromCtx(r.Context())
testID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
@@ -226,8 +240,73 @@ func (h *TestHandler) ResultsGet(w http.ResponseWriter, r *http.Request) {
HTTPError(w, http.StatusNotFound)
return
}
testAnswers, err := h.repo.GetTestAnswers(testID)
if err != nil {
slog.Error("get test answers", "err", err)
HTTPError(w, http.StatusInternalServerError)
return
}
// Index test answers by question ID for quick lookup.
taByQ := make(map[string]*models.TestAnswer, len(testAnswers))
for _, ta := range testAnswers {
taByQ[ta.QuestionID] = ta
}
var items []ResultItem
nCorrect := 0
for _, qid := range test.QuestionIDs {
q, answers, err := h.repo.GetQuestion(qid)
if err != nil {
slog.Error("get question for results", "qid", qid, "err", err)
HTTPError(w, http.StatusInternalServerError)
return
}
ta := taByQ[qid]
var selectedID int64
unanswered := ta == nil || !ta.SelectedAnswerID.Valid
if !unanswered {
selectedID = ta.SelectedAnswerID.Int64
}
userRight := ta != nil && ta.IsCorrect.Valid && ta.IsCorrect.Bool
if userRight {
nCorrect++
}
ra := make([]*ResultAnswer, len(answers))
for i, a := range answers {
ra[i] = &ResultAnswer{
Answer: a,
UserPicked: !unanswered && a.ID == selectedID,
}
}
items = append(items, ResultItem{
Question: q,
Answers: ra,
UserRight: userRight,
Unanswered: unanswered,
})
}
var timeTaken string
if test.CompletedAt.Valid {
d := test.CompletedAt.Time.Sub(test.CreatedAt).Round(time.Second)
h := int(d.Hours())
m := int(d.Minutes()) % 60
s := int(d.Seconds()) % 60
if h > 0 {
timeTaken = fmt.Sprintf("%dh %dm %ds", h, m, s)
} else if m > 0 {
timeTaken = fmt.Sprintf("%dm %ds", m, s)
} else {
timeTaken = fmt.Sprintf("%ds", s)
}
}
data := BaseData(h.auth, r)
data["Test"] = test
data["Items"] = items
data["NCorrect"] = nCorrect
data["TimeTaken"] = timeTaken
h.render.Render(w, http.StatusOK, "test_results", data)
}