lms-backend/routers/router/api/v1/lessons/questions.go

453 lines
12 KiB
Go

package lessons
import (
"errors"
"fmt"
"time"
"git.ex.umbach.dev/LMS/libcore/models"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"gorm.io/gorm"
"lms.de/backend/modules/database"
"lms.de/backend/modules/structs"
"lms.de/backend/modules/utils"
"lms.de/backend/socketclients"
)
func GetQuestions(c *fiber.Ctx) error {
// swagger:operation GET /v1/lessons/{lessonId}/questions questions getQuestions
// ---
// summary: Get questions
// produces:
// - application/json
// responses:
// '200':
// description: Questions
// schema:
// "$ref": "#/definitions/QuestionsResponse"
// '500':
// description: Failed to get questions
var params structs.LessonIdParam
if err := c.ParamsParser(&params); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.JSON([]models.Question{})
}
// get questions
var questions []models.Question
if err := database.DB.Model(&models.Question{}).
Where("lesson_id = ?", params.LessonId).
Find(&questions).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return c.SendStatus(fiber.StatusNotFound)
}
}
// get all questions liked by user
var likedQuestions []string
if err := database.DB.Model(&models.QuestionLike{}).
Where("creator_user_id = ?", c.Locals("userId").(string)).
Pluck("question_id", &likedQuestions).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return c.SendStatus(fiber.StatusInternalServerError)
}
}
/*
var questionLikes []models.QuestionLike
if err := database.DB.Model(&models.QuestionLike{}).
Where("creator_user_id = ?", c.Locals("userId").(string)).
Find(&questionLikes).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return c.SendStatus(fiber.StatusInternalServerError)
}
}
var likedQuestions []string
for _, questionLike := range questionLikes {
likedQuestions = append(likedQuestions, questionLike.QuestionId)
} */
return c.JSON(structs.QuestionsResponse{
Questions: questions,
LikedQuestions: likedQuestions,
})
}
func CreateQuestion(c *fiber.Ctx) error {
// swagger:operation POST /v1/lessons/{lessonId}/questions question createQuestion
// ---
// summary: Create question
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateQuestionRequest"
// responses:
// '200':
// description: Question created successfully
// schema:
// type: string
// '400':
// description: Invalid request body
// '500':
// description: Failed to create question
var params structs.LessonIdParam
if err := c.ParamsParser(&params); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
var body structs.CreateQuestionRequest
if err := c.BodyParser(&body); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// create question
question := models.Question{
Id: uuid.New().String(),
LessonId: params.LessonId,
Message: body.Message,
Likes: 0,
CreatorUserId: c.Locals("userId").(string),
CreatedAt: time.Now(),
}
if err := database.DB.Create(&question).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
socketclients.BroadcastMessageToTopic(
c.Locals("organizationId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionCreated,
Body: question,
},
)
return c.JSON(fiber.Map{
"status": "success",
})
}
func CreateQuestionReply(c *fiber.Ctx) error {
// swagger:operation POST /v1/lessons/{lessonId}/questions/{questionId}/reply question createQuestionReply
// ---
// summary: Create question reply
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateQuestionRequest"
// responses:
// '200':
// description: Question reply created successfully
// schema:
// type: string
// '400':
// description: Invalid request body
// '500':
// description: Failed to create question reply
var params structs.LessonIdQuestionIdParam
if err := c.ParamsParser(&params); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
var body structs.CreateQuestionRequest
if err := c.BodyParser(&body); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// check if question belongs to lesson
var question models.Question
if err := database.DB.Model(&models.Question{}).
Where("id = ? AND lesson_id = ?", params.QuestionId, params.LessonId).
First(&question).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// create question reply
question = models.Question{
Id: uuid.New().String(),
LessonId: params.LessonId,
QuestionId: params.QuestionId,
ReplyId: params.QuestionId,
Message: body.Message,
Likes: 0,
CreatorUserId: c.Locals("userId").(string),
}
if err := database.DB.Create(&question).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
socketclients.BroadcastMessageToTopic(
c.Locals("organizationId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionCreated,
Body: question,
},
)
return c.JSON(fiber.Map{
"status": "success",
})
}
func LikeQuestion(c *fiber.Ctx) error {
// swagger:operation POST /v1/lessons/{lessonId}/questions/{questionId}/likes question likeQuestion
// ---
// summary: Like question
// produces:
// - application/json
// responses:
// '200':
// description: Question liked successfully
// schema:
// type: string
// '500':
// description: Failed to like question
var params structs.LessonIdQuestionIdParam
if err := c.ParamsParser(&params); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
fmt.Println("lesson not found")
return c.SendStatus(fiber.StatusNotFound)
}
// check if question belongs to lesson
var question models.Question
if err := database.DB.Model(&models.Question{}).
Where("id = ? AND lesson_id = ?", params.QuestionId, params.LessonId).
First(&question).Error; err != nil {
fmt.Println("question not found")
return c.SendStatus(fiber.StatusNotFound)
}
// check if user already liked question
var questionLike models.QuestionLike
if err := database.DB.Model(&models.QuestionLike{}).
Where("question_id = ? AND creator_user_id = ?", params.QuestionId, c.Locals("userId").(string)).
First(&questionLike).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return c.SendStatus(fiber.StatusInternalServerError)
}
}
if questionLike.Id != "" {
fmt.Println("question already liked")
return c.SendStatus(fiber.StatusConflict)
}
// like question
questionLike = models.QuestionLike{
Id: uuid.New().String(),
QuestionId: params.QuestionId,
CreatorUserId: c.Locals("userId").(string),
CreatedAt: time.Now(),
}
if err := database.DB.Create(&questionLike).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
// update question likes
if err := database.DB.Model(&models.Question{}).
Where("id = ?", params.QuestionId).
Update("likes", gorm.Expr("likes + 1")).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
// send message to user clients who liked the question
socketclients.SendMessageToUserWithTopic(
c.Locals("userId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionLiked,
Body: question.Id,
},
)
// inform all users about the like
socketclients.BroadcastMessageToTopic(
c.Locals("userId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionCountUpLikes,
Body: question.Id,
},
)
return c.JSON(fiber.Map{
"status": "success",
})
}
func DislikeQuestion(c *fiber.Ctx) error {
// swagger:operation DELETE /v1/lessons/{lessonId}/questions/{questionId}/likes question dislikeQuestion
// ---
// summary: Dislike question
// produces:
// - application/json
// responses:
// '200':
// description: Question disliked successfully
// schema:
// type: string
// '500':
// description: Failed to dislike question
var params structs.LessonIdQuestionIdParam
if err := c.ParamsParser(&params); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
// check if lesson belongs to organization
var lesson models.Lesson
if err := database.DB.Model(&models.Lesson{}).
Where("id = ? AND organization_id = ?", params.LessonId, c.Locals("organizationId").(string)).
First(&lesson).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// check if question belongs to lesson
var question models.Question
if err := database.DB.Model(&models.Question{}).
Where("id = ? AND lesson_id = ?", params.QuestionId, params.LessonId).
First(&question).Error; err != nil {
return c.SendStatus(fiber.StatusNotFound)
}
// check if user already liked question
var questionLike models.QuestionLike
if err := database.DB.Model(&models.QuestionLike{}).
Where("question_id = ? AND creator_user_id = ?", params.QuestionId, c.Locals("userId").(string)).
First(&questionLike).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return c.SendStatus(fiber.StatusInternalServerError)
}
}
if questionLike.Id == "" {
return c.SendStatus(fiber.StatusConflict)
}
// dislike question
if err := database.DB.Delete(&questionLike).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
// update question likes
if err := database.DB.Model(&models.Question{}).
Where("id = ?", params.QuestionId).
Update("likes", gorm.Expr("likes - 1")).Error; err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
// send message to user clients who disliked the question
socketclients.SendMessageToUserWithTopic(
c.Locals("userId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionDisliked,
Body: question.Id,
},
)
// inform all users about the dislike
socketclients.BroadcastMessageToTopic(
c.Locals("userId").(string),
utils.SubscribedTopicLessonsId(params.LessonId),
structs.SendSocketMessage{
Cmd: utils.SendCmdLessonQuestionCountDownLikes,
Body: question.Id,
},
)
return c.JSON(fiber.Map{
"status": "success",
})
}