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

app/queries/queries.go   F

Size/Duplication

Total Lines 402
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
cc 61
eloc 239
dl 0
loc 402
rs 3.52
c 0
b 0
f 0

17 Methods

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