Passed
Pull Request — main (#42)
by Yume
01:10
created

controllers.GetNextCardByDeck   A

Complexity

Conditions 3

Size

Total Lines 22
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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