Add lesson content editor, quiz test endpoint, and lesson update API
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 36s
CI / test-python-backend-compliance (push) Successful in 31s
CI / test-python-document-crawler (push) Successful in 23s
CI / test-python-dsms-gateway (push) Successful in 21s

- Backend: UpdateLesson handler (PUT /lessons/:id) for editing title, content, quiz questions
- Backend: TestQuiz handler (POST /lessons/:id/quiz-test) for quiz evaluation without enrollment
- Frontend: Content editor with markdown textarea, save, and approve-for-video workflow
- Frontend: Fix quiz endpoint to /lessons/:id/quiz-test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-02-26 17:57:15 +01:00
parent 1698912a27
commit 8acf1d2e12
5 changed files with 311 additions and 12 deletions

View File

@@ -572,6 +572,131 @@ func (h *AcademyHandlers) SubmitQuiz(c *gin.Context) {
c.JSON(http.StatusOK, response)
}
// ============================================================================
// Lesson Update
// ============================================================================
// UpdateLesson updates a lesson's content, title, or quiz questions
// PUT /sdk/v1/academy/lessons/:id
func (h *AcademyHandlers) UpdateLesson(c *gin.Context) {
lessonID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid lesson ID"})
return
}
lesson, err := h.store.GetLesson(c.Request.Context(), lessonID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if lesson == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "lesson not found"})
return
}
var req struct {
Title *string `json:"title"`
Description *string `json:"description"`
ContentURL *string `json:"content_url"`
DurationMinutes *int `json:"duration_minutes"`
QuizQuestions *[]academy.QuizQuestion `json:"quiz_questions"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if req.Title != nil {
lesson.Title = *req.Title
}
if req.Description != nil {
lesson.Description = *req.Description
}
if req.ContentURL != nil {
lesson.ContentURL = *req.ContentURL
}
if req.DurationMinutes != nil {
lesson.DurationMinutes = *req.DurationMinutes
}
if req.QuizQuestions != nil {
lesson.QuizQuestions = *req.QuizQuestions
}
if err := h.store.UpdateLesson(c.Request.Context(), lesson); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"lesson": lesson})
}
// TestQuiz evaluates quiz answers without requiring an enrollment
// POST /sdk/v1/academy/lessons/:id/quiz-test
func (h *AcademyHandlers) TestQuiz(c *gin.Context) {
lessonID, err := uuid.Parse(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid lesson ID"})
return
}
lesson, err := h.store.GetLesson(c.Request.Context(), lessonID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if lesson == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "lesson not found"})
return
}
if len(lesson.QuizQuestions) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "lesson has no quiz questions"})
return
}
var req struct {
Answers []int `json:"answers"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if len(req.Answers) != len(lesson.QuizQuestions) {
c.JSON(http.StatusBadRequest, gin.H{"error": "number of answers must match number of questions"})
return
}
correctCount := 0
var results []academy.QuizResult
for i, question := range lesson.QuizQuestions {
correct := req.Answers[i] == question.CorrectIndex
if correct {
correctCount++
}
results = append(results, academy.QuizResult{
Question: question.Question,
Correct: correct,
Explanation: question.Explanation,
})
}
totalQuestions := len(lesson.QuizQuestions)
score := 0
if totalQuestions > 0 {
score = (correctCount * 100) / totalQuestions
}
c.JSON(http.StatusOK, academy.SubmitQuizResponse{
Score: score,
Passed: score >= 70,
CorrectAnswers: correctCount,
TotalQuestions: totalQuestions,
Results: results,
})
}
// ============================================================================
// Statistics
// ============================================================================