package api // The owner shapes of the manage routes. They carry the solutions // (is_correct, explanations, answer_text) and are never used by a student // route; the student shapes live in json.go. Built field by field, so a new // column never leaks. import ( "git.golem15.com/golem15/sm-quizzes-plugin/classes" "git.golem15.com/golem15/sm-quizzes-plugin/graders" "git.golem15.com/golem15/sm-quizzes-plugin/models" "git.golem15.com/golem15/summercms/modules/wire" ) type managedQuizJSON struct { ID uint `json:"id"` Slug string `json:"slug"` Title string `json:"title"` Description *string `json:"description"` Context *string `json:"context"` Mode string `json:"mode"` Navigation string `json:"navigation"` MaxAttempts *int `json:"max_attempts"` TimeLimitSeconds *int `json:"time_limit_seconds"` PassingScore int `json:"passing_score"` ShuffleQuestions bool `json:"shuffle_questions"` RoundSize *int `json:"round_size"` Grader string `json:"grader"` IsPublished bool `json:"is_published"` QuestionCount int `json:"question_count"` AttemptsCount int `json:"attempts_count"` CreatedAt wire.Time `json:"created_at"` UpdatedAt wire.Time `json:"updated_at"` } type managedQuizDetailJSON struct { managedQuizJSON Questions []managedQuestionJSON `json:"questions"` } type managedQuestionJSON struct { ID uint `json:"id"` Text string `json:"text"` Type string `json:"type"` Category *string `json:"category"` AnswerText *string `json:"answer_text"` Explanation *string `json:"explanation"` Points int `json:"points"` SortOrder int `json:"sort_order"` Rubric *string `json:"rubric"` MaxLength *int `json:"max_length"` Answers []managedAnswerJSON `json:"answers"` } type managedAnswerJSON struct { ID uint `json:"id"` Text string `json:"text"` IsCorrect bool `json:"is_correct"` Explanation *string `json:"explanation"` SortOrder int `json:"sort_order"` } func managedQuiz(q models.Quiz, questions, attempts int) managedQuizJSON { return managedQuizJSON{ ID: q.ID, Slug: q.Slug, Title: q.Title, Description: q.Description, Context: q.Context, Mode: quizMode(&q), Navigation: quizNavigation(&q), MaxAttempts: q.MaxAttempts, TimeLimitSeconds: q.TimeLimitSeconds, PassingScore: q.PassingScore, ShuffleQuestions: q.ShuffleQuestions, RoundSize: q.RoundSize, Grader: quizGrader(&q), IsPublished: q.IsPublished, QuestionCount: questions, AttemptsCount: attempts, CreatedAt: wireTime(q.CreatedAt), UpdatedAt: wireTime(q.UpdatedAt), } } func managedQuizRow(row classes.ManagedQuizRow) managedQuizJSON { return managedQuiz(row.Quiz, row.QuestionCount, row.AttemptsCount) } // managedQuizDetail is a quiz loaded by classes.ManagedQuiz with its // questions; attempts is its completed-attempt count. func managedQuizDetail(q models.Quiz, attempts int) managedQuizDetailJSON { out := managedQuizDetailJSON{ managedQuizJSON: managedQuiz(q, len(q.Questions), attempts), Questions: make([]managedQuestionJSON, 0, len(q.Questions)), } for _, question := range q.Questions { out.Questions = append(out.Questions, managedQuestion(question)) } return out } func managedQuestion(q models.Question) managedQuestionJSON { out := managedQuestionJSON{ ID: q.ID, Text: q.Text, Type: q.Type, Category: q.Category, AnswerText: q.AnswerText, Explanation: q.Explanation, Points: q.Points, SortOrder: q.SortOrder, Rubric: q.Rubric, MaxLength: q.MaxLength, Answers: make([]managedAnswerJSON, 0, len(q.Answers)), } for _, a := range q.Answers { out.Answers = append(out.Answers, managedAnswerJSON{ ID: a.ID, Text: a.Text, IsCorrect: a.IsCorrect, Explanation: a.Explanation, SortOrder: a.SortOrder, }) } return out } type statsJSON struct { Quiz statsQuizJSON `json:"quiz"` Unreviewed int `json:"unreviewed"` Students []studentStatJSON `json:"students"` Questions []questionStatJSON `json:"questions"` } type statsQuizJSON struct { ID uint `json:"id"` Slug string `json:"slug"` Title string `json:"title"` Mode string `json:"mode"` PassingScore int `json:"passing_score"` QuestionCount int `json:"question_count"` } type studentStatJSON struct { UserID uint `json:"user_id"` Email string `json:"email"` Name *string `json:"name"` Surname *string `json:"surname"` Attempts int `json:"attempts"` BestScorePercent *float64 `json:"best_score_percent"` LastScorePercent *float64 `json:"last_score_percent"` LastCompletedAt *wire.Time `json:"last_completed_at"` Passed bool `json:"passed"` } type questionStatJSON struct { ID uint `json:"id"` Text string `json:"text"` Type string `json:"type"` Answered int `json:"answered"` Correct int `json:"correct"` CorrectPercent *float64 `json:"correct_percent"` MostChosenWrong *wrongAnswerJSON `json:"most_chosen_wrong"` Unreviewed int `json:"unreviewed"` AveragePoints *float64 `json:"average_points"` } type wrongAnswerJSON struct { AnswerID uint `json:"answer_id"` Text string `json:"text"` Count int `json:"count"` } func quizStats(s *classes.Stats) statsJSON { out := statsJSON{ Quiz: statsQuizJSON{ ID: s.Quiz.ID, Slug: s.Quiz.Slug, Title: s.Quiz.Title, Mode: quizMode(&s.Quiz), PassingScore: s.Quiz.PassingScore, QuestionCount: s.QuestionCount, }, Unreviewed: s.Unreviewed, Students: make([]studentStatJSON, 0, len(s.Students)), Questions: make([]questionStatJSON, 0, len(s.Questions)), } for _, st := range s.Students { row := studentStatJSON{ UserID: st.UserID, Email: st.Email, Name: st.Name, Surname: st.Surname, Attempts: st.Attempts, BestScorePercent: st.BestScorePercent, LastScorePercent: st.LastScorePercent, Passed: st.Passed, } if st.LastCompletedAt != nil { t := wireTime(*st.LastCompletedAt) row.LastCompletedAt = &t } out.Students = append(out.Students, row) } for _, q := range s.Questions { row := questionStatJSON{ ID: q.ID, Text: q.Text, Type: q.Type, Answered: q.Answered, Correct: q.Correct, CorrectPercent: q.CorrectPercent, Unreviewed: q.Unreviewed, AveragePoints: q.AveragePoints, } if q.MostChosenWrong != nil { row.MostChosenWrong = &wrongAnswerJSON{AnswerID: q.MostChosenWrong.AnswerID, Text: q.MostChosenWrong.Text, Count: q.MostChosenWrong.Count} } out.Questions = append(out.Questions, row) } return out } // quizGrader is the quiz's grader code, manual for a quiz read before the // column had a value. func quizGrader(q *models.Quiz) string { if q.Grader == "" { return graders.Manual } return q.Grader }