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 }