feat: grade open answers through a grader registry in the background

- graders registry collected at boot from every HasGraders plugin,
  manual first; a duplicate or reserved code fails boot
- grading job on queue quizzes_grading with three attempts; a stale
  or reviewed answer is never touched; the last failure marks it failed
- learning answers enqueue at once, tests at close; a grade
  recalculates a finished attempt and fires AnswerGradedEvent after
  commit
This commit is contained in:
Jakub Zych
2026-11-09 17:51:55 +01:00
parent 297780cb8e
commit 80ea79ee3e
12 changed files with 942 additions and 24 deletions

32
classes/app.go Normal file
View File

@@ -0,0 +1,32 @@
package classes
import (
"git.golem15.com/golem15/sm-quizzes-plugin/graders"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/conga"
"gorm.io/gorm"
)
// ForApp builds the service the application runs on db: the scoring and
// test grace from its config, the grader registry golem15.quizzes
// published at boot, its event bus and its job queue. Each part that is
// missing stays nil and the service works without it (no graders, no
// events, no jobs). A nil app gives a service with the default grace only.
func ForApp(app *backpack.App, db *gorm.DB) *Service {
if app == nil {
return &Service{DB: db, Grace: DefaultTestGrace}
}
s := &Service{
DB: db,
Scoring: ScoringConfigFrom(app.Config),
Grace: TestGraceFrom(app.Config),
Events: app.Events,
}
if reg, ok := app.Lookup[*graders.Registry](); ok {
s.Graders = reg
}
if m, err := conga.From(app); err == nil && m != nil {
s.Jobs = m
}
return s
}