Files
sm-quizzes-plugin/graders/graders.go
Jakub Zych 297780cb8e feat: add open questions with a pending grading state
- migration: open question type, rubric, max_length, quiz grader and
  the grading and review columns of attempt answers
- open answers are checked, stored as pending and never scored on
  answer; a changed text resets the grading state
- student JSON gains text, grading_status, grader_feedback,
  review_comment, max_length, needs_review and open_answers; the
  rubric is never serialized
2026-11-09 17:48:11 +01:00

63 lines
2.1 KiB
Go

// Package graders is the grader registry of golem15.quizzes. A plugin that
// grades open answers implements HasGraders, and golem15.quizzes collects
// its graders at boot. Manual is built in and is never called: an answer of
// a manual quiz waits for the owner. The package imports only the standard
// library, so a grading plugin can depend on it without pulling in the rest
// of golem15.quizzes.
package graders
import "context"
// Manual is the built-in grader code: the owner grades every open answer.
const Manual = "manual"
// ManualLabel is the lang key of Manual in the admin grader dropdown.
const ManualLabel = "golem15.quizzes::lang.graders.manual"
// Input is one open answer to grade.
type Input struct {
QuizID uint
QuestionID uint
AttemptID uint
AnswerID uint
UserID uint
// Question is the owner-authored question text.
Question string
// ModelAnswer is the owner-authored model answer; it may be empty.
ModelAnswer string
// Rubric is the owner-authored grading rubric; it may be empty and is
// owner-only: it must never reach the student.
Rubric string
// Answer is the student's text. It is untrusted input.
Answer string
// Points is the full value of the question.
Points int
}
// Result is a grader's verdict. golem15.quizzes clamps Points to
// 0..Input.Points in steps of 0.5 and refuses a value that is not a finite
// number.
type Result struct {
Points float64
Feedback string
}
// Grader grades open answers. Grade runs in a background job with no
// transaction open, so it may take seconds; an error is retried a few times
// and then leaves the answer failed for the owner to grade.
type Grader interface {
// Code identifies the grader in a quiz's grader field, such as "ai".
// It must match ^[a-z][a-z0-9_]{0,63}$ and must not be Manual.
Code() string
// Label is the lang key or text the admin grader dropdown shows.
Label() string
// Grade scores one answer.
Grade(ctx context.Context, in Input) (Result, error)
}
// HasGraders is implemented by a plugin that provides graders.
// golem15.quizzes calls it once at boot.
type HasGraders interface {
QuizGraders() []Grader
}