fix: end grading when a question stops being open

WR-06: a Question save hook clears grading_status on the answers of a
question that changed type away from open, in the save's transaction,
so they leave the review queue and needs_review. GradeAnswer ignores a
question that is no longer open, and a choice or self-check re-answer
clears the text, grade and review columns of its row.
This commit is contained in:
Jakub Zych
2026-11-09 22:52:15 +01:00
parent 09cf61e9e0
commit 9ac53579e4
3 changed files with 62 additions and 5 deletions

45
models/question_hooks.go Normal file
View File

@@ -0,0 +1,45 @@
package models
import (
"time"
"gorm.io/gorm"
)
// wasOpenKey is the statement setting by which BeforeSave tells AfterSave
// that the saved question was open before.
const wasOpenKey = "golem15.quizzes:question_was_open"
// BeforeSave notes whether a stored question that is saved as another
// type was open, for AfterSave. Every write path (the admin form and
// relation manager, the manage API and MCP, the importer) saves through
// GORM, so the hooks cover them all.
func (q *Question) BeforeSave(tx *gorm.DB) error {
if q.ID == 0 || q.Type == TypeOpen {
return nil
}
var stored string
err := tx.Session(&gorm.Session{NewDB: true}).Model(&Question{}).
Where("id = ?", q.ID).Limit(1).Pluck("type", &stored).Error
if err != nil {
return err
}
if stored == TypeOpen {
tx.InstanceSet(wasOpenKey, true)
}
return nil
}
// AfterSave ends the grading of the answers to a question that stopped
// being open, in the same transaction as the save: their grading_status
// becomes NULL, so they leave the review queue and needs_review, and a
// grading job still queued for them does nothing. Their text, grades and
// points stay for the record (stored scores are never touched).
func (q *Question) AfterSave(tx *gorm.DB) error {
if was, ok := tx.InstanceGet(wasOpenKey); !ok || was != true {
return nil
}
return tx.Session(&gorm.Session{NewDB: true}).Model(&AttemptAnswer{}).
Where("question_id = ? AND grading_status IS NOT NULL", q.ID).
Updates(map[string]any{"grading_status": nil, "updated_at": time.Now().UTC()}).Error
}