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

queries.FetchNextTodayCard   A

Complexity

Conditions 2

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 12
nop 1
dl 0
loc 16
rs 9.8
c 0
b 0
f 0
1
package queries
2
3
import (
4
	"errors"
5
	"fmt"
6
	"math/rand"
7
	"time"
8
9
	"github.com/memnix/memnixrest/app/models"
10
	"github.com/memnix/memnixrest/pkg/core"
11
	"github.com/memnix/memnixrest/pkg/database"
12
	"github.com/memnix/memnixrest/pkg/utils"
13
	"gorm.io/gorm"
14
)
15
16
// UpdateSubUsers generates MemDate for sub users
17
func UpdateSubUsers(card *models.Card, user *models.User) error {
18
	var users []models.User
19
	result := new(models.ResponseHTTP)
20
21
	if result = GetSubUsers(card.DeckID); !result.Success {
22
		log := models.CreateLog(fmt.Sprintf("Error from %s on deck %d - CreateNewCard: %s", user.Email, card.DeckID, result.Message),
23
			models.LogQueryGetError).SetType(models.LogTypeError).AttachIDs(user.ID, card.DeckID, card.ID)
24
		_ = log.SendLog()
25
		return errors.New("couldn't get sub users")
26
	}
27
28
	switch result.Data.(type) {
29
	default:
30
		return errors.New("couldn't get sub users")
31
	case []models.User:
32
		users = result.Data.([]models.User)
33
	}
34
35
	for i := range users {
36
		_ = GenerateMemDate(users[i].ID, card.ID, card.DeckID)
37
	}
38
39
	return nil
40
}
41
42
// FillResponseDeck returns a filled models.ResponseDeck
43
// This function might become a method of models.ResponseDeck
44
func FillResponseDeck(deck *models.Deck, permission models.AccessPermission, toggleToday bool) models.ResponseDeck {
45
	db := database.DBConn
46
47
	deckResponse := new(models.ResponseDeck)
48
49
	deckResponse.Deck = *deck
50
	deckResponse.DeckID = deck.ID
51
	deckResponse.Permission = permission
52
	deckResponse.ToggleToday = toggleToday
53
54
	if owner := deck.GetOwner(); owner.ID != 0 {
55
		publicUser := new(models.PublicUser)
56
57
		publicUser.Set(&owner)
58
59
		deckResponse.Owner = *publicUser
60
		deckResponse.OwnerId = owner.ID
61
	}
62
63
	var count int64
64
	if err := db.Table("cards").Where("cards.deck_id = ?", deck.ID).Count(&count).Error; err != nil {
65
		deckResponse.CardCount = 0
66
	} else {
67
		deckResponse.CardCount = count
68
	}
69
	return *deckResponse
70
}
71
72
// GenerateCreatorAccess sets an user as a deck creator
73
func GenerateCreatorAccess(user *models.User, deck *models.Deck) *models.ResponseHTTP {
74
	db := database.DBConn
75
	// TODO: Change models.User & models.Deck to uint
76
	access := new(models.Access)
77
	res := new(models.ResponseHTTP)
78
79
	access.Set(user.ID, deck.ID, models.AccessOwner)
80
	db.Create(access)
81
82
	res.GenerateSuccess("Success register a creator access !", *access, 1)
83
	return res
84
}
85
86
// GenerateAccess sets a default student access to a deck for a given user
87
func GenerateAccess(user *models.User, deck *models.Deck) *models.ResponseHTTP {
88
	db := database.DBConn
89
	res := new(models.ResponseHTTP)
90
91
	if deck.Status != models.DeckPublic && user.Permissions != models.PermAdmin {
92
		res.GenerateError(utils.ErrorForbidden)
93
		return res
94
	}
95
96
	access := new(models.Access)
97
98
	if err := db.Joins("User").Joins("Deck").Where("accesses.user_id = ? AND accesses.deck_id =?", user.ID, deck.ID).Find(&access).Error; err != nil {
99
		if errors.Is(err, gorm.ErrRecordNotFound) {
100
			access.Set(user.ID, deck.ID, models.AccessStudent)
101
			db.Preload("User").Preload("Deck").Create(access)
102
		}
103
104
	} else {
105
		if access.Permission >= models.AccessStudent {
106
			res.GenerateError(utils.ErrorAlreadySub)
107
			return res
108
109
		} else {
110
			access.Set(user.ID, deck.ID, models.AccessStudent)
111
			db.Preload("User").Preload("Deck").Save(access)
112
		}
113
	}
114
115
	res.GenerateSuccess("Success register an access", *access, 1)
116
	return res
117
}
118
119
// CheckAccess verifies if a given user as the right models.Permission to perform an action on a deck
120
func CheckAccess(userID, deckID uint, perm models.AccessPermission) *models.ResponseHTTP {
121
	db := database.DBConn // DB Conn
122
123
	access := new(models.Access)
124
	res := new(models.ResponseHTTP)
125
126
	if err := db.Joins("User").Joins("Deck").Where("accesses.user_id = ? AND accesses.deck_id = ?", userID, deckID).First(&access).Error; err != nil {
127
		access.Permission = models.AccessNone
128
	}
129
130
	if access.Permission < perm {
131
		res.GenerateError(utils.ErrorForbidden)
132
		return res
133
	}
134
135
	res.GenerateSuccess("Success checking access permissions", *access, 1)
136
	return res
137
}
138
139
// CheckCardLimit verifies that a deck can handle more cards
140
func CheckCardLimit(permission models.Permission, deckID uint) bool {
141
	db := database.DBConn // DB Conn
142
	var count int64
143
144
	if err := db.Table("cards").Where("cards.deck_id = ? AND cards.deleted_at IS NULL", deckID).Count(&count).Error; err != nil {
145
		//TODO: Handle error
146
		return true
147
	}
148
149
	if permission < models.PermMod && count >= utils.MaxCardDeck {
150
		return false
151
	}
152
153
	return true
154
}
155
156
// CheckCode prevents deck code from being duplicated
157
func CheckCode(key, code string) bool {
158
	db := database.DBConn // DB Conn
159
	var count int64
160
161
	if err := db.Table("decks").Where("decks.key = ? AND decks.code = ? AND decks.deleted_at IS NULL", key, code).Count(&count).Error; err != nil {
162
		// TODO: Handle error
163
		return true
164
	}
165
166
	if count != 0 {
167
		return false
168
	}
169
170
	return true
171
}
172
173
// CheckDeckLimit verifies that the user hasn't reached the limit
174
func CheckDeckLimit(user *models.User) bool {
175
	db := database.DBConn // DB Conn
176
	var count int64
177
178
	if err := db.Table("accesses").Where("accesses.user_id = ? AND accesses.permission = ? AND accesses.deleted_at IS NULL", user.ID, models.AccessOwner).Count(&count).Error; err != nil {
179
		//TODO: Handle error
180
		return true
181
	}
182
183
	if user.Permissions < models.PermMod && count >= utils.MaxDeckNormalUser {
184
		return false
185
	}
186
187
	return true
188
}
189
190
// PostSelfEvaluatedMem updates Mem & MemDate
191
func PostSelfEvaluatedMem(user *models.User, card *models.Card, quality uint, training bool) *models.ResponseHTTP {
192
	db := database.DBConn // DB Conn
193
	res := new(models.ResponseHTTP)
194
195
	memDate := new(models.MemDate)
196
197
	if err := db.Joins("Card").Joins("User").Joins("Deck").Where("mem_dates.user_id = ? AND mem_dates.card_id = ?",
198
		user.ID, card.ID).First(&memDate).Error; err != nil {
199
		res.GenerateError(utils.ErrorRequestFailed) // MemDate not found
200
		// TODO: Create a default MemDate
201
		return res
202
	}
203
204
	exMem := FetchMem(memDate.CardID, user.ID)
205
	if exMem.Efactor == 0 {
206
		exMem.FillDefaultValues(user.ID, card.ID)
207
	}
208
209
	core.UpdateMemSelfEvaluated(&exMem, training, quality)
210
211
	res.GenerateSuccess("Success Post Mem", nil, 0)
212
	return res
213
}
214
215
// PostMem updates MemDate & Mem
216
func PostMem(user *models.User, card *models.Card, validation *models.CardResponseValidation, training bool) *models.ResponseHTTP {
217
	db := database.DBConn // DB Conn
218
	res := new(models.ResponseHTTP)
219
220
	memDate := new(models.MemDate)
221
222
	if err := db.Joins("Card").Joins("User").Joins("Deck").Where("mem_dates.user_id = ? AND mem_dates.card_id = ?",
223
		user.ID, card.ID).First(&memDate).Error; err != nil {
224
		res.GenerateError(utils.ErrorRequestFailed) // MemDate not found
225
		// TODO: Create a default MemDate
226
		return res
227
	}
228
229
	exMem := FetchMem(memDate.CardID, user.ID)
230
	if exMem.Efactor == 0 {
231
		exMem.FillDefaultValues(user.ID, card.ID)
232
	}
233
234
	if training {
235
		core.UpdateMemTraining(&exMem, validation.Validate)
236
	} else {
237
		core.UpdateMem(&exMem, validation.Validate)
238
	}
239
	res.GenerateSuccess("Success Post Mem", nil, 0)
240
	return res
241
}
242
243
// PopulateMemDate with default value for a given user & deck
244
// This is used on deck sub
245
func PopulateMemDate(user *models.User, deck *models.Deck) *models.ResponseHTTP {
246
	db := database.DBConn // DB Conn
247
	var cards []models.Card
248
	res := new(models.ResponseHTTP)
249
250
	if err := db.Joins("Deck").Where("cards.deck_id = ?", deck.ID).Find(&cards).Error; err != nil {
251
		res.GenerateError(err.Error()) // MemDate not found
252
		return res
253
	}
254
255
	for i := range cards {
256
		_ = GenerateMemDate(user.ID, cards[i].ID, cards[i].DeckID)
257
	}
258
	res.GenerateSuccess("Success generated mem_date", nil, 0)
259
	return res
260
}
261
262
// GetSubUsers returns a list of users sub to a deck
263
func GetSubUsers(deckID uint) *models.ResponseHTTP {
264
	res := new(models.ResponseHTTP)
265
266
	db := database.DBConn // DB Conn
267
	var users []models.User
268
269
	if err := db.Joins("left join accesses ON users.id = accesses.user_id AND accesses.deck_id = ?", deckID).Where("accesses.permission > ?", models.AccessNone).Find(&users).Error; err != nil {
270
		res.GenerateError(err.Error())
271
		return res
272
	}
273
	res.GenerateSuccess("Success getting sub users", users, len(users))
274
	return res
275
}
276
277
// GenerateMemDate with default nextDate
278
func GenerateMemDate(userID, cardID, deckID uint) *models.ResponseHTTP {
279
	db := database.DBConn // DB Conn
280
	res := new(models.ResponseHTTP)
281
282
	memDate := new(models.MemDate)
283
284
	if err := db.Joins("User").Joins("Card").Where("mem_dates.user_id = ? AND mem_dates.card_id = ?", userID, cardID).First(&memDate).Error; err != nil {
285
		if errors.Is(err, gorm.ErrRecordNotFound) {
286
			memDate.SetDefaultNextDate(userID, cardID, deckID)
287
			db.Create(memDate)
288
		} else {
289
			res.GenerateError(err.Error())
290
			return res
291
		}
292
	}
293
	res.GenerateSuccess("Success generate MemDate", memDate, 1)
294
	return res
295
}
296
297
// FetchMem returns last mem of an user on a given card
298
func FetchMem(cardID, userID uint) models.Mem {
299
	db := database.DBConn // DB Conn
300
301
	mem := new(models.Mem)
302
	if err := db.Joins("Card").Where("mems.card_id = ? AND mems.user_id = ?", cardID, userID).Order("id desc").First(&mem).Error; err != nil {
303
		if errors.Is(err, gorm.ErrRecordNotFound) {
304
			mem.Efactor = 0
305
		}
306
	}
307
	return *mem
308
}
309
310
// GenerateMCQ returns a list of answer
311
func GenerateMCQ(memDate *models.MemDate, userID uint) []string {
312
313
	mem := FetchMem(memDate.CardID, userID)
314
315
	var answersList []string
316
	if mem.IsMCQ() || memDate.Card.Type == models.CardMCQ {
317
318
		answersList = memDate.Card.GetMCQAnswers()
319
		if len(answersList) == 4 {
320
			memDate.Card.Type = models.CardMCQ // MCQ
321
		}
322
		return answersList
323
	}
324
325
	return answersList
326
}
327
328
// FetchTrainingCards returns training cards
329
func FetchTrainingCards(userID, deckID uint) *models.ResponseHTTP {
330
	res := new(models.ResponseHTTP)
331
	db := database.DBConn // DB Conn
332
	var result []models.ResponseCard
333
334
	var memDates []models.MemDate
335
336
	if err := db.Joins("Deck").Joins("Card").Where("mem_dates.deck_id = ? AND mem_dates.user_id = ?", deckID, userID).Find(&memDates).Error; err != nil {
337
		res.GenerateError(err.Error())
338
		return res
339
	}
340
	responseCard := new(models.ResponseCard)
341
	var answersList []string
342
343
	for i := range memDates {
344
345
		answersList = GenerateMCQ(&memDates[i], userID)
346
		responseCard.Set(&memDates[i], answersList)
347
348
		result = append(result, *responseCard)
349
	}
350
351
	rand.Seed(time.Now().UnixNano())
352
	rand.Shuffle(len(result), func(i, j int) { result[i], result[j] = result[j], result[i] })
353
354
	res.GenerateSuccess("Success getting next card", result, len(result))
355
	return res
356
357
}
358
359
// FetchTodayCard return today cards
360
func FetchTodayCard(userID uint) *models.ResponseHTTP {
361
	db := database.DBConn // DB Conn
362
	t := time.Now()
363
364
	res := new(models.ResponseHTTP)
365
	var memDates []models.MemDate
366
367
	m := make(map[uint][]models.ResponseCard)
368
369
	if err := db.Joins(
370
		"left join accesses ON mem_dates.deck_id = accesses.deck_id AND accesses.user_id = ?",
371
		userID).Joins("Card").Joins("Deck").Where("mem_dates.user_id = ? AND mem_dates.next_date < ? AND accesses.permission >= ? AND accesses.toggle_today IS true",
372
		userID, t.AddDate(0, 0, 1).Add(
373
			time.Duration(-t.Hour())*time.Hour), models.AccessStudent).Order("next_date asc").Find(&memDates).Error; err != nil {
374
		res.GenerateError("Today's memDate not found")
375
		return res
376
	}
377
378
	rand.Seed(time.Now().UnixNano())
379
	rand.Shuffle(len(memDates), func(i, j int) { memDates[i], memDates[j] = memDates[j], memDates[i] })
380
381
	var answersList []string
382
	responseCard := new(models.ResponseCard)
383
384
	for index := range memDates {
385
		answersList = GenerateMCQ(&memDates[index], userID)
386
		responseCard.Set(&memDates[index], answersList)
387
		m[responseCard.Card.DeckID] = append(m[responseCard.Card.DeckID], *responseCard)
388
	}
389
390
	todayResponse := new(models.TodayResponse)
391
392
	for key := range m {
393
		deckResponse := new(models.DeckResponse)
394
		deckResponse.DeckID = key
395
		deckResponse.Cards = m[key]
396
		deckResponse.Count = len(deckResponse.Cards)
397
		todayResponse.DecksReponses = append(todayResponse.DecksReponses, *deckResponse)
398
	}
399
400
	todayResponse.Count = len(todayResponse.DecksReponses)
401
402
	res.GenerateSuccess("Success getting next today's cards", todayResponse, len(memDates))
403
	return res
404
}
405