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"` }