Files
spsays-bff/internal/model/quiz.go
T
2026-10-18 13:02:13 +05:30

138 lines
4.4 KiB
Go

package model
import "time"
// Quiz target kinds.
const (
QuizChapter = "chapter"
QuizLecture = "lecture"
QuizConversation = "conversation"
QuizStandalone = "standalone"
)
// Attempt states, as seen by the person taking the quiz.
const (
QuizNotStarted = "not_started"
QuizInProgress = "in_progress"
QuizCompleted = "completed"
)
// Quiz is a set of multiple-choice questions, optionally tied to a page.
type Quiz struct {
ID string `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
TargetKind string `json:"target_kind"`
TargetPath *string `json:"target_path,omitempty"`
TargetTitle string `json:"target_title"`
// TargetYear is the lecture or conversation's year, for grouping.
TargetYear *int `json:"target_year,omitempty"`
PublishedAt *time.Time `json:"published_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
QuestionCount int `json:"question_count"`
// Admin-only counters.
AttemptCount int `json:"attempt_count,omitempty"`
CompletedCount int `json:"completed_count,omitempty"`
Questions []QuizQuestion `json:"questions,omitempty"`
}
// QuizQuestion is one question. Prompt and option bodies carry the same
// formatting markers as the rest of the admin text.
type QuizQuestion struct {
ID string `json:"id"`
Ord int `json:"ord"`
Prompt string `json:"prompt"`
Explanation string `json:"explanation,omitempty"`
// Multi questions ask for every correct option ("select all that apply").
Multi bool `json:"multi"`
Options []QuizOption `json:"options"`
}
// QuizOption is one choice. IsCorrect is nil whenever the reader must not
// know it yet — every response to someone mid-attempt.
type QuizOption struct {
ID string `json:"id"`
Ord int `json:"ord"`
Body string `json:"body"`
IsCorrect *bool `json:"is_correct,omitempty"`
}
// QuizStatus is the caller's progress on a quiz.
type QuizStatus struct {
State string `json:"state"`
Answered int `json:"answered"`
Total int `json:"total"`
Score *int `json:"score,omitempty"`
}
// QuizCard is a quiz plus the caller's progress, for buttons and lists.
type QuizCard struct {
Quiz
My QuizStatus `json:"my"`
}
// QuizPlay is the next step of an attempt: the current question, or Done.
type QuizPlay struct {
QuizID string `json:"quiz_id"`
Title string `json:"title"`
Answered int `json:"answered"`
Total int `json:"total"`
Done bool `json:"done"`
Question *QuizQuestion `json:"question,omitempty"`
}
// QuizResultItem is one question on a results screen.
type QuizResultItem struct {
QuizQuestion
ChosenOptionIDs []string `json:"chosen_option_ids"`
IsCorrect bool `json:"is_correct"`
ResponseMs *int `json:"response_ms,omitempty"`
}
// QuizResult is a finished attempt.
type QuizResult struct {
Quiz Quiz `json:"quiz"`
Score int `json:"score"`
Total int `json:"total"`
StartedAt time.Time `json:"started_at"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
TotalMs int `json:"total_ms"`
Items []QuizResultItem `json:"items"`
}
// QuizParticipant is one person's attempt, for the admin participants list.
type QuizParticipant struct {
UserID string `json:"user_id"`
Handle string `json:"handle"`
Name string `json:"name"`
AvatarURL *string `json:"avatar_url,omitempty"`
StartedAt time.Time `json:"started_at"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
Answered int `json:"answered"`
Score int `json:"score"`
Total int `json:"total"`
TotalMs int `json:"total_ms"`
}
// QuizOptionStat is how often one option was chosen.
type QuizOptionStat struct {
QuizOption
Picks int `json:"picks"`
}
// QuizQuestionStat aggregates every response to one question.
type QuizQuestionStat struct {
ID string `json:"id"`
Ord int `json:"ord"`
Prompt string `json:"prompt"`
Multi bool `json:"multi"`
Answered int `json:"answered"`
Correct int `json:"correct"`
AvgMs int `json:"avg_ms"`
MedianMs int `json:"median_ms"`
Options []QuizOptionStat `json:"options"`
}