Yimaru-BackEnd/internal/web_server/handlers/initial_assessment.go

216 lines
6.4 KiB
Go

package handlers
import (
"Yimaru-Backend/internal/domain"
"strconv"
"github.com/gofiber/fiber/v2"
)
// CreateAssessmentQuestion godoc
// @Summary Create assessment question
// @Description Creates a new assessment question using the unified questions system
// @Tags assessment-question
// @Accept json
// @Produce json
// @Param body body createQuestionReq true "Create question payload"
// @Success 201 {object} domain.Response
// @Failure 400 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/assessment/questions [post]
func (h *Handler) CreateAssessmentQuestion(c *fiber.Ctx) error {
var req createQuestionReq
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid request body",
Error: err.Error(),
})
}
if req.QuestionText == "" {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Validation error",
Error: "question_text is required",
})
}
// Build options input
var options []domain.CreateQuestionOptionInput
for _, opt := range req.Options {
options = append(options, domain.CreateQuestionOptionInput{
OptionText: opt.OptionText,
OptionOrder: opt.OptionOrder,
IsCorrect: opt.IsCorrect,
})
}
// Build short answers input
var shortAnswers []domain.CreateShortAnswerInput
for _, sa := range req.ShortAnswers {
shortAnswers = append(shortAnswers, domain.CreateShortAnswerInput{
AcceptableAnswer: sa.AcceptableAnswer,
MatchType: sa.MatchType,
})
}
input := domain.CreateQuestionInput{
QuestionText: req.QuestionText,
QuestionType: req.QuestionType,
DifficultyLevel: req.DifficultyLevel,
Points: req.Points,
Explanation: req.Explanation,
Tips: req.Tips,
VoicePrompt: req.VoicePrompt,
SampleAnswerVoicePrompt: req.SampleAnswerVoicePrompt,
Status: req.Status,
Options: options,
ShortAnswers: shortAnswers,
}
question, err := h.assessmentSvc.CreateQuestion(c.Context(), input)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Message: "Failed to create assessment question",
Error: err.Error(),
})
}
return c.Status(fiber.StatusCreated).JSON(domain.Response{
Message: "Assessment question created successfully",
StatusCode: fiber.StatusCreated,
Success: true,
Data: questionRes{
ID: question.ID,
QuestionText: question.QuestionText,
QuestionType: question.QuestionType,
Status: question.Status,
CreatedAt: question.CreatedAt.String(),
},
})
}
// ListAssessmentQuestions godoc
// @Summary List assessment questions
// @Description Returns all active assessment questions from the initial assessment set
// @Tags assessment-question
// @Produce json
// @Success 200 {array} domain.QuestionWithDetails
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/assessment/questions [get]
func (h *Handler) ListAssessmentQuestions(c *fiber.Ctx) error {
questions, err := h.assessmentSvc.ListQuestions(c.Context())
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Message: "Failed to fetch assessment questions",
Error: err.Error(),
})
}
var questionResponses []questionRes
for _, q := range questions {
var options []optionRes
for _, opt := range q.Options {
options = append(options, optionRes{
ID: opt.ID,
OptionText: opt.OptionText,
OptionOrder: opt.OptionOrder,
IsCorrect: opt.IsCorrect,
})
}
var shortAnswers []shortAnswerRes
for _, sa := range q.ShortAnswers {
shortAnswers = append(shortAnswers, shortAnswerRes{
ID: sa.ID,
AcceptableAnswer: sa.AcceptableAnswer,
MatchType: sa.MatchType,
})
}
questionResponses = append(questionResponses, questionRes{
ID: q.ID,
QuestionText: q.QuestionText,
QuestionType: q.QuestionType,
DifficultyLevel: q.DifficultyLevel,
Points: q.Points,
Status: q.Status,
CreatedAt: q.CreatedAt.String(),
Options: options,
ShortAnswers: shortAnswers,
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Questions fetched successfully",
Data: questionResponses,
Success: true,
StatusCode: 200,
})
}
// GetAssessmentQuestionByID godoc
// @Summary Get assessment question by ID
// @Description Returns a single assessment question with its options or answer
// @Tags assessment-question
// @Produce json
// @Param id path int true "Question ID"
// @Success 200 {object} domain.QuestionWithDetails
// @Failure 400 {object} domain.ErrorResponse
// @Failure 404 {object} domain.ErrorResponse
// @Failure 500 {object} domain.ErrorResponse
// @Router /api/v1/assessment/questions/{id} [get]
func (h *Handler) GetAssessmentQuestionByID(c *fiber.Ctx) error {
idStr := c.Params("id")
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil || id <= 0 {
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
Message: "Invalid question ID",
Error: "question ID must be a positive integer",
})
}
question, err := h.assessmentSvc.GetQuestionByID(c.Context(), id)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(domain.ErrorResponse{
Message: "Failed to fetch assessment question",
Error: err.Error(),
})
}
var options []optionRes
for _, opt := range question.Options {
options = append(options, optionRes{
ID: opt.ID,
OptionText: opt.OptionText,
OptionOrder: opt.OptionOrder,
IsCorrect: opt.IsCorrect,
})
}
var shortAnswers []shortAnswerRes
for _, sa := range question.ShortAnswers {
shortAnswers = append(shortAnswers, shortAnswerRes{
ID: sa.ID,
AcceptableAnswer: sa.AcceptableAnswer,
MatchType: sa.MatchType,
})
}
return c.Status(fiber.StatusOK).JSON(domain.Response{
Message: "Question fetched successfully",
Data: questionRes{
ID: question.ID,
QuestionText: question.QuestionText,
QuestionType: question.QuestionType,
DifficultyLevel: question.DifficultyLevel,
Points: question.Points,
Status: question.Status,
CreatedAt: question.CreatedAt.String(),
Options: options,
ShortAnswers: shortAnswers,
},
Success: true,
StatusCode: 200,
})
}