quzuu-api-test / controllers /academy_exam_controller.go
lifedebugger's picture
Deploy files from GitHub repository
51ec421
package controllers
import (
"strconv"
"abdanhafidz.com/go-boilerplate/models/dto"
entity "abdanhafidz.com/go-boilerplate/models/entity"
http_error "abdanhafidz.com/go-boilerplate/models/error"
"abdanhafidz.com/go-boilerplate/services"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type AcademyExamController interface {
Attempt(ctx *gin.Context)
Answer(ctx *gin.Context)
Submit(ctx *gin.Context)
List(ctx *gin.Context)
}
type academyExamController struct{ academyExamService services.AcademyExamService }
func NewAcademyExamController(academyExamService services.AcademyExamService) AcademyExamController {
return &academyExamController{academyExamService: academyExamService}
}
// Attempt godoc
// @Summary Attempt Academy Exam
// @Description Start an attempt for a specific exam in an academy
// @Tags Academy Exam
// @Accept json
// @Produce json
// @Param academy_slug path string true "Academy Slug"
// @Param exam_slug path string true "Exam Slug"
// @Success 200 {object} dto.SuccessResponse[models.AcademyExamAttempt]
// @Failure 400 {object} dto.ErrorResponse
// @Router /api/v1/academy/{academy_slug}/exam/{exam_slug}/attempt [get]
func (c *academyExamController) Attempt(ctx *gin.Context) {
academySlug := ctx.Param("academy_slug")
examSlug := ctx.Param("exam_slug")
accountId := ParseAccountId(ctx)
res, err := c.academyExamService.AttemptAcademyExam(ctx.Request.Context(), academySlug, examSlug, accountId)
ResponseJSON(ctx, gin.H{"academy_slug": academySlug, "exam_slug": examSlug}, res, err)
}
// Answer godoc
// @Summary Answer Academy Exam Question
// @Description Submit an answer for a specific question in an exam attempt
// @Tags Academy Exam
// @Accept json
// @Produce json
// @Param academy_slug path string true "Academy Slug"
// @Param attempt_id path string true "Exam Attempt ID"
// @Param request body dto.AnswerEventExamRequest true "Answer Exam Event Request"
// @Success 200 {object} dto.SuccessResponse[any]
// @Failure 400 {object} dto.ErrorResponse
// @Router /api/v1/academy/{academy_slug}/exam/{attempt_id}/answer_question [post]
func (c *academyExamController) Answer(ctx *gin.Context) {
academySlug := ctx.Param("academy_slug")
attemptId, err := uuid.Parse(ctx.Param("attempt_id"))
if err != nil {
ResponseJSON[any](ctx, gin.H{"attempt_id": ctx.Param("attempt_id")}, nil, http_error.INVALID_TOKEN)
return
}
req := RequestJSON[dto.AnswerEventExamRequest](ctx)
res, err := c.academyExamService.AnswerAcademyExam(ctx.Request.Context(), academySlug, attemptId, req.QuestionId, req.Answer)
ResponseJSON(ctx, gin.H{"cp_grader_result": res}, req, err)
}
// Submit godoc
// @Summary Submit Academy Exam
// @Description Submit the exam attempt for evaluation
// @Tags Academy Exam
// @Accept json
// @Produce json
// @Param academy_slug path string true "Academy Slug"
// @Param attempt_id path string true "Exam Attempt ID"
// @Success 200 {object} dto.SuccessResponse[entity.AcademyExamResult]
// @Failure 400 {object} dto.ErrorResponse
// @Router /api/v1/academy/{academy_slug}/exam/{attempt_id}/submit [post]
func (c *academyExamController) Submit(ctx *gin.Context) {
attemptId, err := uuid.Parse(ctx.Param("attempt_id"))
if err != nil {
ResponseJSON[any](ctx, gin.H{"attempt_id": ctx.Param("attempt_id")}, nil, http_error.INVALID_TOKEN)
return
}
res, err := c.academyExamService.SubmitAcademyExam(ctx.Request.Context(), attemptId)
ResponseJSON(ctx, gin.H{}, res, err)
}
// List godoc
// @Summary List Academy Exams
// @Description Retrieve a list of exams available in a specific academy
// @Tags Academy Exam
// @Accept json
// @Produce json
// @Param academy_slug path string true "Academy Slug"
// @Param limit query int false "Items per page" default(10)
// @Param page query int false "Page number" default(1)
// @Param search query string false "Search by title, slug, or description"
// @Param sortBy query string false "Sort field (title, slug, duration, created_at)"
// @Param orderBy query string false "Sort direction (asc / desc)"
// @Success 200 {object} dto.SuccessResponse[[]entity.Exam]
// @Failure 400 {object} dto.ErrorResponse
// @Router /api/v1/academy/{academy_slug}/exam [get]
func (c *academyExamController) List(ctx *gin.Context) {
academySlug := ctx.Param("academy_slug")
accountId := ParseAccountId(ctx)
limit, _ := strconv.Atoi(ctx.DefaultQuery("limit", "10"))
page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
search := ctx.DefaultQuery("search", "")
sortBy := ctx.DefaultQuery("sortBy", "created_at")
order := ctx.DefaultQuery("orderBy", "")
if order == "" {
order = ctx.DefaultQuery("order", "desc")
}
if limit < 1 {
limit = 10
} else if limit > 100 {
limit = 100
}
if page < 1 {
page = 1
}
offset := (page - 1) * limit
p := entity.Pagination{Limit: limit, Offset: offset, Search: search, SortBy: sortBy, Order: order}
list, total, err := c.academyExamService.ListExamByAcademy(ctx.Request.Context(), academySlug, accountId, p)
if err != nil {
ResponseJSON[any, any](ctx, nil, nil, err)
return
}
totalPages := int((total + int64(limit) - 1) / int64(limit))
if total == 0 {
totalPages = 1
}
if page > totalPages {
page = totalPages
offset = (page - 1) * limit
p.Offset = offset
list, total, err = c.academyExamService.ListExamByAcademy(ctx.Request.Context(), academySlug, accountId, p)
if err != nil {
ResponseJSON[any, any](ctx, nil, nil, err)
return
}
}
meta := gin.H{
"academy_slug": academySlug,
"totalItems": total,
"totalPages": totalPages,
"currentPage": page,
"limit": limit,
}
ResponseJSON(ctx, meta, list, nil)
}