feat: review open answers in the admin and pick a grader per quiz

- Do oceny admin screen (golem15.quizzes.reviews) over the open answers,
  with a read-only context partial, score and comment virtual fields saved
  through SetScore, confirm and regrade record actions, bulk confirm
- open question type, rubric and max_length in the question form; grader
  dropdown on the quiz form from the grader registry
- Review model with quiz, status and student filters
This commit is contained in:
Jakub Zych
2026-11-09 18:31:19 +01:00
parent 1ce1ebee16
commit 2ad4e8b1ec
20 changed files with 1244 additions and 9 deletions

View File

@@ -3,6 +3,7 @@
package controllers
import (
"git.golem15.com/golem15/sm-quizzes-plugin/graders"
"git.golem15.com/golem15/sm-quizzes-plugin/models"
"git.golem15.com/golem15/summercms/modules/pact"
)
@@ -11,8 +12,17 @@ var (
_ pact.AdminController = quizzesAdmin{}
_ pact.AdminRecordSource = quizzesAdmin{}
_ pact.AdminPermissioned = quizzesAdmin{}
_ pact.DropdownOptionsProvider = quizzesAdmin{}
)
// GraderOptions lists the grader dropdown choices: the registry golem15.quizzes
// built at boot. The plugin sets it at Boot; nil offers manual only.
//
// TODO: requires SummerCMS change. DropdownOptions receives neither a
// context nor the app, so the registry comes through a package-level hook.
var GraderOptions func() []graders.Option
// quizzesAdmin lists and edits quizzes. Questions have their own controller
// filtered by quiz.
//
@@ -33,5 +43,25 @@ func (quizzesAdmin) RequiredPermissions() []string {
return []string{"golem15.quizzes.access_quizzes"}
}
// DropdownOptions serves the grader dropdown (options: getGraderOptions):
// manual, then every grader an activated plugin registered. Other fields
// have no method-backed options.
func (quizzesAdmin) DropdownOptions(field string) []pact.Option {
if field != "grader" {
return nil
}
opts := []graders.Option{{Code: graders.Manual, Label: graders.ManualLabel}}
if GraderOptions != nil {
if got := GraderOptions(); len(got) > 0 {
opts = got
}
}
out := make([]pact.Option, 0, len(opts))
for _, o := range opts {
out = append(out, pact.Option{Value: o.Code, Label: o.Label})
}
return out
}
// QuizzesController returns the golem15.quizzes.quizzes admin controller.
func QuizzesController() pact.AdminController { return quizzesAdmin{} }