Passed
Pull Request — main (#71)
by Yume
01:16
created

controllers.PostSelfEvaluateResponse   B

Complexity

Conditions 7

Size

Total Lines 44
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 30
nop 1
dl 0
loc 44
rs 7.76
c 0
b 0
f 0
1
package controllers
2
3
import (
4
	"fmt"
5
	"github.com/gofiber/fiber/v2"
6
	"github.com/memnix/memnixrest/pkg/core"
7
	"github.com/memnix/memnixrest/pkg/database"
8
	"github.com/memnix/memnixrest/pkg/logger"
9
	"github.com/memnix/memnixrest/pkg/models"
10
	"github.com/memnix/memnixrest/pkg/queries"
11
	"github.com/memnix/memnixrest/pkg/utils"
12
	"net/http"
13
	"strconv"
14
)
15
16
// GetAllTodayCard function to get all today card for a user
17
// @Description Get all today card
18
// @Summary gets a list of card
19
// @Tags Card
20
// @Produce json
21
// @Success 200  {array} models.TodayResponse
22
// @Security Beaver
23
// @Router /v1/cards/today [get]
24
func GetAllTodayCard(c *fiber.Ctx) error {
25
	var res *models.ResponseHTTP
26
27
	user, ok := c.Locals("user").(models.User)
28
	if !ok {
29
		return queries.RequestError(c, http.StatusUnauthorized, utils.ErrorForbidden)
30
	}
31
32
	if res = queries.FetchTodayCard(user.ID); !res.Success {
33
		log := logger.CreateLog(fmt.Sprintf("Error on GetAllTodayCard: %s", res.Message), logger.LogQueryGetError).SetType(logger.LogTypeError).AttachIDs(user.ID, 0, 0)
34
		_ = log.SendLog()
35
		return queries.RequestError(c, http.StatusInternalServerError, res.Message)
36
	}
37
38
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
39
		Success: true,
40
		Message: "Get today's cards",
41
		Data:    res.Data,
42
		Count:   res.Count,
43
	})
44
}
45
46
// GetTrainingCardsByDeck function to get training cards by deck
47
// @Description Get training cards from a deck
48
// @Summary gets a list of cards
49
// @Tags Card
50
// @Produce json
51
// @Success 200 {array} models.Card
52
// @Param deckId path int true "Deck ID"
53
// @Security Beaver
54
// @Router /v1/cards/{deckID}/training [get]
55
func GetTrainingCardsByDeck(c *fiber.Ctx) error {
56
	res := new(models.ResponseHTTP)
57
58
	user, ok := c.Locals("user").(models.User)
59
	if !ok {
60
		return queries.RequestError(c, http.StatusUnauthorized, utils.ErrorForbidden)
61
	}
62
63
	deckID := c.Params("deckID")
64
	deckIDInt, _ := strconv.ParseInt(deckID, 10, 32)
65
66
	access := queries.CheckAccess(user.ID, uint(deckIDInt), models.AccessStudent)
67
	if !access.Success {
68
		log := logger.CreateLog(fmt.Sprintf("Forbidden from %s on deck %d - GetTodayCard: %s", user.Email, deckIDInt, res.Message), logger.LogPermissionForbidden).SetType(logger.LogTypeWarning).AttachIDs(user.ID, uint(deckIDInt), 0)
69
		_ = log.SendLog()
70
		return queries.RequestError(c, http.StatusForbidden, utils.ErrorForbidden)
71
	}
72
73
	if res = queries.FetchTrainingCards(user.ID, uint(deckIDInt)); !res.Success {
74
		log := logger.CreateLog(fmt.Sprintf("Error on GetTrainingCardsByDeck: %s from %s", res.Message, user.Email), logger.LogQueryGetError).SetType(logger.LogTypeError).AttachIDs(user.ID, uint(deckIDInt), 0)
75
		_ = log.SendLog()
76
		return queries.RequestError(c, http.StatusInternalServerError, res.Message)
77
	}
78
79
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
80
		Success: true,
81
		Message: "Get today's card",
82
		Data:    res.Data,
83
		Count:   res.Count,
84
	})
85
}
86
87
// GetAllCards function to get all cards (deprecated)
88
// @Description Get every card. Shouldn't really be used
89
// @Summary gets all cards
90
// @Tags Card
91
// @Produce json
92
// @Security Admin
93
// @Success 200 {array} models.Card
94
// @Router /v1/cards/ [get]
95
// @Deprecated
96
func GetAllCards(c *fiber.Ctx) error {
97
	db := database.DBConn // DB Conn
98
99
	var cards []models.Card
100
101
	if res := db.Joins("Deck").Find(&cards); res.Error != nil {
102
		return queries.RequestError(c, http.StatusInternalServerError, utils.ErrorRequestFailed)
103
	}
104
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
105
		Success: true,
106
		Message: "Get All cards",
107
		Data:    cards,
108
		Count:   len(cards),
109
	})
110
}
111
112
// GetCardByID function to get a card by id
113
// @Description Get a card by id
114
// @Summary gets a card
115
// @Tags Card
116
// @Produce json
117
// @Param id path int true "Card ID"
118
// @Security Admin
119
// @Success 200 {object} models.Card
120
// @Router /v1/cards/id/{id} [get]
121
func GetCardByID(c *fiber.Ctx) error {
122
	db := database.DBConn // DB Conn
123
124
	// Params
125
	id := c.Params("id")
126
127
	card := new(models.Card)
128
129
	if err := db.Joins("Deck").Joins("Mcq").First(&card, id).Error; err != nil {
130
		return queries.RequestError(c, http.StatusInternalServerError, err.Error())
131
	}
132
133
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
134
		Success: true,
135
		Message: "Success get card by ID.",
136
		Data:    *card,
137
		Count:   1,
138
	})
139
}
140
141
// GetCardsFromDeck method to get cards from deck
142
// @Description Get every card from a deck
143
// @Summary gets a list of card
144
// @Tags Card
145
// @Produce json
146
// @Param deckID path int true "Deck ID"
147
// @Security Beaver
148
// @Success 200 {array} models.Card
149
// @Router /v1/cards/deck/{deckID} [get]
150
func GetCardsFromDeck(c *fiber.Ctx) error {
151
	db := database.DBConn // DB Conn
152
153
	// Params
154
	id := c.Params("deckID")
155
	deckID, _ := strconv.ParseUint(id, 10, 32)
156
157
	user, ok := c.Locals("user").(models.User)
158
	if !ok {
159
		return queries.RequestError(c, http.StatusUnauthorized, utils.ErrorForbidden)
160
	}
161
162
	if res := queries.CheckAccess(user.ID, uint(deckID), models.AccessStudent); !res.Success {
163
		log := logger.CreateLog(fmt.Sprintf("Forbidden from %s on deck %d - GetCardsFromDeck: %s", user.Email, deckID, res.Message), logger.LogPermissionForbidden).SetType(logger.LogTypeWarning).AttachIDs(user.ID, uint(deckID), 0)
164
		_ = log.SendLog()
165
		return queries.RequestError(c, http.StatusForbidden, utils.ErrorForbidden)
166
	}
167
168
	var cards []models.Card
169
170
	if err := db.Joins("Deck").Joins("Mcq").Where("cards.deck_id = ?", id).Find(&cards).Error; err != nil {
171
		log := logger.CreateLog(fmt.Sprintf("Error on GetCardsFromDeck: %s from %s on %d", err.Error(), user.Email, deckID), logger.LogQueryGetError).SetType(logger.LogTypeError).AttachIDs(user.ID, uint(deckID), 0)
172
		_ = log.SendLog()
173
		return queries.RequestError(c, http.StatusInternalServerError, err.Error())
174
	}
175
176
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
177
		Success: true,
178
		Message: "Success get cards from deck.",
179
		Data:    cards,
180
		Count:   len(cards),
181
	})
182
}
183
184
// POST
185
186
// CreateNewCard method
187
// @Description Create a new card (must be a deck editor)
188
// @Summary creates a card
189
// @Tags Card
190
// @Produce json
191
// @Accept json
192
// @Security Beaver
193
// @Param card body models.Card true "Card to create"
194
// @Success 200
195
// @Router /v1/cards/new [post]
196
func CreateNewCard(c *fiber.Ctx) error {
197
	db := database.DBConn // DB Conn
198
	card := new(models.Card)
199
200
	user, ok := c.Locals("user").(models.User)
201
	if !ok {
202
		return queries.RequestError(c, http.StatusUnauthorized, utils.ErrorForbidden)
203
	}
204
205
	if err := c.BodyParser(&card); err != nil {
206
		log := logger.CreateLog(fmt.Sprintf("Error on CreateNewCard: %s from %s", err.Error(), user.Email), logger.LogBodyParserError).SetType(logger.LogTypeError).AttachIDs(user.ID, 0, 0)
207
		_ = log.SendLog()
208
		return queries.RequestError(c, http.StatusBadRequest, err.Error())
209
	}
210
211
	if res := queries.CheckAccess(user.ID, card.DeckID, models.AccessEditor); !res.Success {
212
		log := logger.CreateLog(fmt.Sprintf("Forbidden from %s on deck %d - CreateNewCard: %s", user.Email, card.DeckID, res.Message), logger.LogPermissionForbidden).SetType(logger.LogTypeWarning).AttachIDs(user.ID, card.DeckID, 0)
213
		_ = log.SendLog()
214
		return queries.RequestError(c, http.StatusForbidden, utils.ErrorForbidden)
215
	}
216
217
	if res := queries.CheckCardLimit(user.Permissions, card.DeckID); !res {
218
		log := logger.CreateLog(fmt.Sprintf("Forbidden from %s on deck %d - CreateNewCard: This deck has reached his limit", user.Email, card.DeckID), logger.LogDeckCardLimit).SetType(logger.LogTypeWarning).AttachIDs(user.ID, card.DeckID, 0)
219
		_ = log.SendLog()
220
		return queries.RequestError(c, http.StatusForbidden, "This deck has reached his limit ! You can't add more card to it.")
221
	}
222
223
	if card.NotValidate() {
224
		log := logger.CreateLog(fmt.Sprintf("BadRequest from %s on deck %d - CreateNewCard: BadRequest", user.Email, card.DeckID), logger.LogBadRequest).SetType(logger.LogTypeWarning).AttachIDs(user.ID, card.DeckID, 0)
225
		_ = log.SendLog()
226
		return queries.RequestError(c, http.StatusBadRequest, utils.ErrorQALen)
227
	}
228
229
	_, ok = card.ValidateMCQ(&user)
230
	if !ok {
231
		return queries.RequestError(c, http.StatusInternalServerError, utils.ErrorRequestFailed)
232
	}
233
234
	db.Create(card)
235
236
	log := logger.CreateLog(fmt.Sprintf("Created: %d - %s", card.ID, card.Question), logger.LogCardCreated).SetType(logger.LogTypeInfo).AttachIDs(user.ID, card.DeckID, card.ID)
237
	_ = log.SendLog()
238
239
	if res := queries.UpdateSubUsers(card, &user); res != nil {
240
		return queries.RequestError(c, http.StatusInternalServerError, utils.ErrorRequestFailed)
241
	}
242
243
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
244
		Success: true,
245
		Message: "Success register a card",
246
		Data:    *card,
247
		Count:   1,
248
	})
249
}
250
251
// PostSelfEvaluateResponse method
252
// @Description Post a self evaluated response
253
// @Summary posts a response
254
// @Tags Card
255
// @Produce json
256
// @Security Beaver
257
// @Accept json
258
// @Param card body models.CardSelfResponse true "Self response"
259
// @Success 200
260
// @Router /v1/cards/selfresponse [post]
261
func PostSelfEvaluateResponse(c *fiber.Ctx) error {
262
	db := database.DBConn // DB Conn
263
264
	user, ok := c.Locals("user").(models.User)
265
	if !ok {
266
		return queries.RequestError(c, http.StatusUnauthorized, utils.ErrorForbidden)
267
	}
268
269
	response := new(models.CardSelfResponse)
270
	card := new(models.Card)
271
272
	if err := c.BodyParser(&response); err != nil {
273
		log := logger.CreateLog(fmt.Sprintf("Error on PostSelfEvaluateResponse: %s from %s", err.Error(), user.Email), logger.LogBodyParserError).SetType(logger.LogTypeError).AttachIDs(user.ID, 0, 0)
274
		_ = log.SendLog()
275
		return queries.RequestError(c, http.StatusBadRequest, err.Error())
276
	}
277
278
	if response.Quality > 4 || response.Quality < 1 {
279
		log := logger.CreateLog(fmt.Sprintf("Error on PostSelfEvaluateResponse: Quality > 4 from %s", user.Email), logger.LogBodyParserError).SetType(logger.LogTypeError).AttachIDs(user.ID, 0, 0)
280
		_ = log.SendLog()
281
		return queries.RequestError(c, http.StatusBadRequest, utils.ErrorBreak)
282
	}
283
284
	if err := db.Joins("Deck").First(&card, response.CardID).Error; err != nil {
285
		log := logger.CreateLog(fmt.Sprintf("Error on PostSelfEvaluateResponse: %s from %s", err.Error(), user.Email), logger.LogQueryGetError).SetType(logger.LogTypeError).AttachIDs(user.ID, 0, 0)
286
		_ = log.SendLog()
287
		return queries.RequestError(c, http.StatusServiceUnavailable, err.Error())
288
	}
289
290
	res := queries.CheckAccess(user.ID, card.Deck.ID, models.AccessStudent)
291
	if !res.Success {
292
		log := logger.CreateLog(fmt.Sprintf("Forbidden from %s on deck %d - PostSelfEvaluateResponse: %s", user.Email, card.DeckID, res.Message), logger.LogPermissionForbidden).SetType(logger.LogTypeWarning).AttachIDs(user.ID, card.DeckID, 0)
293
		_ = log.SendLog()
294
		return queries.RequestError(c, http.StatusForbidden, utils.ErrorForbidden)
295
	}
296
297
	//TODO: Add error handling
298
	_ = queries.PostSelfEvaluatedMem(&user, card, response.Quality, response.Training)
299
300
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
301
		Success: true,
302
		Message: "Success post response",
303
		Data:    nil,
304
		Count:   1,
305
	})
306
}
307
308
// PostResponse method
309
// @Description Post a response and check it
310
// @Summary posts a response
311
// @Tags Card
312
// @Produce json
313
// @Security Beaver
314
// @Accept json
315
// @Param card body models.CardResponse true "Response"
316
// @Success 200 {object} models.CardResponseValidation
317
// @Router /v1/cards/response [post]
318
func PostResponse(c *fiber.Ctx) error {
319
	db := database.DBConn // DB Conn
320
321
	user, ok := c.Locals("user").(models.User)
322
	if !ok {
323
		return queries.RequestError(c, http.StatusUnauthorized, utils.ErrorForbidden)
324
	}
325
326
	response := new(models.CardResponse)
327
	card := new(models.Card)
328
329
	if err := c.BodyParser(&response); err != nil {
330
		log := logger.CreateLog(fmt.Sprintf("Error on PostResponse: %s from %s", err.Error(), user.Email), logger.LogBodyParserError).SetType(logger.LogTypeError).AttachIDs(user.ID, 0, 0)
331
		_ = log.SendLog()
332
		return queries.RequestError(c, http.StatusBadRequest, err.Error())
333
	}
334
335
	if err := db.Joins("Deck").First(&card, response.CardID).Error; err != nil {
336
		log := logger.CreateLog(fmt.Sprintf("Error on PostResponse: %s from %s", err.Error(), user.Email), logger.LogQueryGetError).SetType(logger.LogTypeError).AttachIDs(user.ID, 0, 0)
337
		_ = log.SendLog()
338
		return queries.RequestError(c, http.StatusServiceUnavailable, err.Error())
339
	}
340
341
	res := queries.CheckAccess(user.ID, card.Deck.ID, models.AccessStudent)
342
	if !res.Success {
343
		log := logger.CreateLog(fmt.Sprintf("Forbidden from %s on deck %d - PostResponse: %s", user.Email, card.DeckID, res.Message), logger.LogPermissionForbidden).SetType(logger.LogTypeWarning).AttachIDs(user.ID, card.DeckID, 0)
344
		_ = log.SendLog()
345
		return queries.RequestError(c, http.StatusForbidden, utils.ErrorForbidden)
346
	}
347
348
	validation := new(models.CardResponseValidation)
349
350
	if core.ValidateAnswer(response.Response, card) {
351
		validation.SetCorrect()
352
	} else {
353
		validation.SetIncorrect()
354
	}
355
356
	//TODO: Add error handling
357
	_ = queries.PostMem(&user, card, validation, response.Training)
358
359
	validation.Answer = card.Answer
360
361
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
362
		Success: true,
363
		Message: "Success post response",
364
		Data:    *validation,
365
		Count:   1,
366
	})
367
}
368
369
// PUT
370
371
// UpdateCardByID method
372
// @Description Edit a card
373
// @Summary edits a card
374
// @Tags Card
375
// @Produce json
376
// @Success 200 {object} models.Card
377
// @Security Beaver
378
// @Accept json
379
// @Param card body models.Card true "card to edit"
380
// @Param id path int true "card id"
381
// @Router /v1/cards/{cardID}/edit [put]
382
func UpdateCardByID(c *fiber.Ctx) error {
383
	db := database.DBConn // DB Conn
384
385
	// Params
386
	id := c.Params("id")
387
	cardID, _ := strconv.ParseUint(id, 10, 32)
388
389
	user, ok := c.Locals("user").(models.User)
390
	if !ok {
391
		return queries.RequestError(c, http.StatusUnauthorized, utils.ErrorForbidden)
392
	}
393
	card := new(models.Card)
394
395
	if err := db.First(&card, id).Error; err != nil {
396
		log := logger.CreateLog(fmt.Sprintf("Error on UpdateCardByID: %s from %s", err.Error(), user.Email), logger.LogQueryGetError).SetType(logger.LogTypeError).AttachIDs(user.ID, 0, uint(cardID))
397
		_ = log.SendLog()
398
		return queries.RequestError(c, http.StatusInternalServerError, err.Error())
399
	}
400
401
	if res := queries.CheckAccess(user.ID, card.DeckID, models.AccessEditor); !res.Success {
402
		log := logger.CreateLog(fmt.Sprintf("Forbidden from %s on deck %d - UpdateCardByID: %s", user.Email, card.DeckID, res.Message), logger.LogPermissionForbidden).SetType(logger.LogTypeWarning).AttachIDs(user.ID, card.DeckID, uint(cardID))
403
		_ = log.SendLog()
404
		return queries.RequestError(c, http.StatusForbidden, utils.ErrorForbidden)
405
	}
406
407
	if err := UpdateCard(c, card, &user); !err.Success {
408
		log := logger.CreateLog(fmt.Sprintf("Error on UpdateCardByID: %s from %s", err.Message, user.Email), logger.LogBadRequest).SetType(logger.LogTypeError).AttachIDs(user.ID, 0, uint(cardID))
409
		_ = log.SendLog()
410
		return queries.RequestError(c, http.StatusBadRequest, err.Message)
411
	}
412
413
	log := logger.CreateLog(fmt.Sprintf("Edited: %d - %s", card.ID, card.Question), logger.LogCardEdited).SetType(logger.LogTypeInfo).AttachIDs(user.ID, card.DeckID, card.ID)
414
	_ = log.SendLog()
415
416
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
417
		Success: true,
418
		Message: "Success update card by ID",
419
		Data:    *card,
420
		Count:   1,
421
	})
422
}
423
424
// UpdateCard function
425
func UpdateCard(c *fiber.Ctx, card *models.Card, user *models.User) *models.ResponseHTTP {
426
	db := database.DBConn
427
428
	deckID := card.DeckID
429
430
	res := new(models.ResponseHTTP)
431
432
	if err := c.BodyParser(&card); err != nil {
433
		res.GenerateError(err.Error())
434
		return res
435
	}
436
437
	if deckID != card.DeckID {
438
		res.GenerateError(utils.ErrorBreak)
439
		return res
440
	}
441
442
	if card.NotValidate() {
443
		res.GenerateError(utils.ErrorQALen)
444
		return res
445
	}
446
447
	shouldUpdateMcq := false
448
449
	mcq, ok := card.ValidateMCQ(user)
450
	if !ok {
451
		res.GenerateError(utils.ErrorRequestFailed)
452
		return res
453
	}
454
455
	shouldUpdateMcq = mcq != nil
456
457
	db.Save(card)
458
459
	if shouldUpdateMcq {
460
		mcq.UpdateLinkedAnswers()
461
	}
462
463
	res.GenerateSuccess("Success update card", nil, 0)
464
	return res
465
}
466
467
// DeleteCardByID method
468
// @Description Delete a card (must be a deck owner)
469
// @Summary deletes a card
470
// @Tags Card
471
// @Produce json
472
// @Security Beaver
473
// @Param id path int true "card id"
474
// @Success 200
475
// @Router /v1/cards/{cardID} [delete]
476
func DeleteCardByID(c *fiber.Ctx) error {
477
	db := database.DBConn // DB Conn
478
	id := c.Params("id")
479
	cardID, _ := strconv.ParseUint(id, 10, 32)
480
481
	user, ok := c.Locals("user").(models.User)
482
	if !ok {
483
		return queries.RequestError(c, http.StatusUnauthorized, utils.ErrorForbidden)
484
	}
485
486
	card := new(models.Card)
487
488
	if err := db.First(&card, id).Error; err != nil {
489
		return queries.RequestError(c, http.StatusServiceUnavailable, err.Error())
490
	}
491
492
	if res := queries.CheckAccess(user.ID, card.DeckID, models.AccessOwner); !res.Success {
493
		log := logger.CreateLog(fmt.Sprintf("Forbidden from %s on deck %d - DeleteCardById: %s", user.Email, card.DeckID, res.Message), logger.LogPermissionForbidden).SetType(logger.LogTypeWarning).AttachIDs(user.ID, card.DeckID, uint(cardID))
494
		_ = log.SendLog()
495
		return queries.RequestError(c, http.StatusForbidden, utils.ErrorForbidden)
496
	}
497
498
	var memDates []models.MemDate
499
500
	if err := db.Joins("Card").Where("mem_dates.card_id = ?", card.ID).Find(&memDates).Error; err != nil {
501
		log := logger.CreateLog(fmt.Sprintf("Error on DeleteCardById: %s from %s", err.Error(), user.Email), logger.LogQueryGetError).SetType(logger.LogTypeError).AttachIDs(user.ID, 0, uint(cardID))
502
		_ = log.SendLog()
503
		return queries.RequestError(c, http.StatusInternalServerError, utils.ErrorRequestFailed)
504
		// TODO: Error
505
	}
506
507
	db.Unscoped().Delete(memDates)
508
509
	db.Delete(card)
510
511
	log := logger.CreateLog(fmt.Sprintf("Deleted: %d - %s", card.ID, card.Question), logger.LogCardDeleted).SetType(logger.LogTypeInfo).AttachIDs(user.ID, card.DeckID, card.ID)
512
	_ = log.SendLog()
513
514
	return c.Status(http.StatusOK).JSON(models.ResponseHTTP{
515
		Success: true,
516
		Message: "Success delete card by ID",
517
		Data:    *card,
518
		Count:   1,
519
	})
520
}
521