Passed
Push — main ( 7c64d9...5da60f )
by Yume
01:20
created

app/queries/queries.go   F

Size/Duplication

Total Lines 411
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
cc 62
eloc 244
dl 0
loc 411
rs 3.44
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A queries.CheckCardLimit 0 14 4
A queries.GenerateCreatorAccess 0 11 1
A queries.UpdateSubUsers 0 23 5
B queries.GenerateAccess 0 30 6
A queries.CheckAccess 0 17 3
A queries.FillResponseDeck 0 26 3
A queries.PostMem 0 26 4
A queries.FetchMem 0 10 3
A queries.FetchTrainingCards 0 27 4
A queries.FetchNextCard 0 24 4
A queries.PopulateMemDate 0 15 3
A queries.GetSubUsers 0 12 2
A queries.GenerateMCQ 0 15 4
A queries.CheckMCQLimit 0 13 3
A queries.FetchNextTodayCard 0 16 2
A queries.CheckDeckLimit 0 14 4
A queries.FetchTodayCard 0 31 4
A queries.GenerateMemDate 0 17 3
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 = ?", 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
// CheckMCQLimit verifies that a deck can handle more mcqs
157
func CheckMCQLimit(deckID uint) bool {
158
	db := database.DBConn // DB Conn
159
	var count int64
160
161
	if err := db.Table("mcqs").Where("mcqs.deck_id = ?", deckID).Count(&count).Error; err != nil {
162
		return true
163
	}
164
165
	if count >= utils.MaxMcqDeck {
166
		return false
167
	}
168
169
	return true
170
}
171
172
// CheckDeckLimit verifies that the user hasn't reached the limit
173
func CheckDeckLimit(user *models.User) bool {
174
	db := database.DBConn // DB Conn
175
	var count int64
176
177
	if err := db.Table("accesses").Where("accesses.user_id = ? AND accesses.permission = ?", user.ID, models.AccessOwner).Count(&count).Error; err != nil {
178
		//TODO: Handle error
179
		return true
180
	}
181
182
	if user.Permissions < models.PermMod && count >= utils.MaxDeckNormalUser {
183
		return false
184
	}
185
186
	return true
187
}
188
189
// PostMem updates MemDate & Mem
190
func PostMem(user *models.User, card *models.Card, validation *models.CardResponseValidation, training bool) *models.ResponseHTTP {
191
	db := database.DBConn // DB Conn
192
	//TODO: Replace struct params with ids
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
	if training {
210
		core.UpdateMemTraining(&exMem, validation.Validate)
211
	} else {
212
		core.UpdateMem(&exMem, validation.Validate)
213
	}
214
	res.GenerateSuccess("Success Post Mem", nil, 0)
215
	return res
216
}
217
218
// PopulateMemDate with default value for a given user & deck
219
// This is used on deck sub
220
func PopulateMemDate(user *models.User, deck *models.Deck) *models.ResponseHTTP {
221
	db := database.DBConn // DB Conn
222
	var cards []models.Card
223
	res := new(models.ResponseHTTP)
224
225
	if err := db.Joins("Deck").Where("cards.deck_id = ?", deck.ID).Find(&cards).Error; err != nil {
226
		res.GenerateError(err.Error()) // MemDate not found
227
		return res
228
	}
229
230
	for i := range cards {
231
		_ = GenerateMemDate(user.ID, cards[i].ID, cards[i].DeckID)
232
	}
233
	res.GenerateSuccess("Success generated mem_date", nil, 0)
234
	return res
235
}
236
237
// GetSubUsers returns a list of users sub to a deck
238
func GetSubUsers(deckID uint) *models.ResponseHTTP {
239
	res := new(models.ResponseHTTP)
240
241
	db := database.DBConn // DB Conn
242
	var users []models.User
243
244
	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 {
245
		res.GenerateError(err.Error())
246
		return res
247
	}
248
	res.GenerateSuccess("Success getting sub users", users, len(users))
249
	return res
250
}
251
252
// GenerateMemDate with default nextDate
253
func GenerateMemDate(userID, cardID, deckID uint) *models.ResponseHTTP {
254
	db := database.DBConn // DB Conn
255
	res := new(models.ResponseHTTP)
256
257
	memDate := new(models.MemDate)
258
259
	if err := db.Joins("User").Joins("Card").Where("mem_dates.user_id = ? AND mem_dates.card_id = ?", userID, cardID).First(&memDate).Error; err != nil {
260
		if errors.Is(err, gorm.ErrRecordNotFound) {
261
			memDate.SetDefaultNextDate(userID, cardID, deckID)
262
			db.Create(memDate)
263
		} else {
264
			res.GenerateError(err.Error())
265
			return res
266
		}
267
	}
268
	res.GenerateSuccess("Success generate MemDate", memDate, 1)
269
	return res
270
}
271
272
// FetchMem returns last mem of an user on a given card
273
func FetchMem(cardID, userID uint) models.Mem {
274
	db := database.DBConn // DB Conn
275
276
	mem := new(models.Mem)
277
	if err := db.Joins("Card").Where("mems.card_id = ? AND mems.user_id = ?", cardID, userID).Order("id desc").First(&mem).Error; err != nil {
278
		if errors.Is(err, gorm.ErrRecordNotFound) {
279
			mem.Efactor = 0
280
		}
281
	}
282
	return *mem
283
}
284
285
// GenerateMCQ returns a list of answer
286
func GenerateMCQ(memDate *models.MemDate, userID uint) []string {
287
288
	mem := FetchMem(memDate.CardID, userID)
289
290
	var answersList []string
291
	if mem.IsMCQ() || memDate.Card.Type == models.CardMCQ {
292
293
		answersList = memDate.Card.GetMCQAnswers()
294
		if len(answersList) == 4 {
295
			memDate.Card.Type = models.CardMCQ // MCQ
296
		}
297
		return answersList
298
	}
299
300
	return answersList
301
}
302
303
// FetchTrainingCards returns training cards
304
func FetchTrainingCards(userID, deckID uint) *models.ResponseHTTP {
305
	res := new(models.ResponseHTTP)
306
	db := database.DBConn // DB Conn
307
	var result []models.ResponseCard
308
309
	var memDates []models.MemDate
310
311
	if err := db.Joins("Deck").Joins("Card").Where("mem_dates.deck_id = ? AND mem_dates.user_id = ?", deckID, userID).Find(&memDates).Error; err != nil {
312
		res.GenerateError(err.Error())
313
		return res
314
	}
315
	responseCard := new(models.ResponseCard)
316
	var answersList []string
317
318
	for i := range memDates {
319
320
		answersList = GenerateMCQ(&memDates[i], userID)
321
		responseCard.Set(&memDates[i].Card, answersList)
322
323
		result = append(result, *responseCard)
324
	}
325
326
	rand.Seed(time.Now().UnixNano())
327
	rand.Shuffle(len(result), func(i, j int) { result[i], result[j] = result[j], result[i] })
328
329
	res.GenerateSuccess("Success getting next card", result, len(result))
330
	return res
331
332
}
333
334
// FetchTodayCard return today cards
335
func FetchTodayCard(userID uint) *models.ResponseHTTP {
336
	db := database.DBConn // DB Conn
337
	t := time.Now()
338
339
	res := new(models.ResponseHTTP)
340
	var memDates []models.MemDate
341
342
	if err := db.Joins(
343
		"left join accesses ON mem_dates.deck_id = accesses.deck_id AND accesses.user_id = ?",
344
		userID).Joins("Card").Joins("Deck").Where("mem_dates.user_id = ? AND mem_dates.next_date < ? AND accesses.permission >= ? AND accesses.toggle_today IS true",
345
		userID, t.AddDate(0, 0, 1).Add(
346
			time.Duration(-t.Hour())*time.Hour), models.AccessStudent).Order("next_date asc").Find(&memDates).Error; err != nil {
347
		res.GenerateError("Today's memDate not found")
348
		return res
349
	}
350
351
	var answersList []string
352
	var responseCardList []models.ResponseCard
353
	responseCard := new(models.ResponseCard)
354
355
	for index := range memDates {
356
		answersList = GenerateMCQ(&memDates[index], userID)
357
		responseCard.Set(&memDates[index].Card, answersList)
358
		responseCardList = append(responseCardList, *responseCard)
359
	}
360
361
	rand.Seed(time.Now().UnixNano())
362
	rand.Shuffle(len(responseCardList), func(i, j int) { responseCardList[i], responseCardList[j] = responseCardList[j], responseCardList[i] })
363
364
	res.GenerateSuccess("Success getting next today's cards", responseCardList, len(responseCardList))
365
	return res
366
}
367
368
// FetchNextTodayCard return next today card
369
func FetchNextTodayCard(userID uint) *models.ResponseHTTP {
370
	res := new(models.ResponseHTTP)
371
	responseCard := new(models.ResponseCard)
372
	memDate := new(models.MemDate)
373
	var answersList []string
374
375
	if result := memDate.GetNextToday(userID); !result.Success {
376
		res.GenerateError("Next card not found")
377
		return res
378
	}
379
	answersList = GenerateMCQ(memDate, userID)
380
381
	responseCard.Set(&memDate.Card, answersList)
382
383
	res.GenerateSuccess("Success getting next card", responseCard, 1)
384
	return res
385
}
386
387
// FetchNextCard returns next card
388
func FetchNextCard(userID, deckID uint) *models.ResponseHTTP {
389
	res := new(models.ResponseHTTP)
390
	responseCard := new(models.ResponseCard)
391
	memDate := new(models.MemDate)
392
	var answersList []string
393
394
	if deckID != 0 {
395
		if result := memDate.GetNextByDeck(userID, deckID); !result.Success {
396
			res.GenerateError("Next card not found")
397
			return res
398
		}
399
	} else {
400
		if result := memDate.GetNext(userID); !result.Success {
401
			res.GenerateError("Next card not found")
402
			return res
403
		}
404
405
	}
406
407
	answersList = GenerateMCQ(memDate, userID)
408
	responseCard.Set(&memDate.Card, answersList)
409
410
	res.GenerateSuccess("Success getting next card", responseCard, 1)
411
	return res
412
}
413