feat: list and show quizzes over MCP for their owner
- quizzes_list_quizzes and quizzes_show_quiz bound to GET /api/v1/quizzes
and /api/v1/quizzes/{slug} behind user.api_token, the per-user
golem15.quizzes.manage throttle, a scope and the access_quizzes permission
- the permission is grantable through pact.HasFrontendPermissions
- owner JSON shapes with solutions in manage_json.go, never used by a
student route
This commit is contained in:
116
controllers/api/manage_json.go
Normal file
116
controllers/api/manage_json.go
Normal file
@@ -0,0 +1,116 @@
|
||||
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/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"`
|
||||
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"`
|
||||
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,
|
||||
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,
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user