122 lines
2.9 KiB
Go
122 lines
2.9 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"math/rand/v2"
|
|
"slices"
|
|
"time"
|
|
|
|
"forge.cadoles.com/wpetit/kouiz/internal/timex"
|
|
"github.com/pkg/errors"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func (s *Store) UpsertQuizCategory(ctx context.Context, category *QuizCategory) error {
|
|
return errors.WithStack(s.Do(ctx, func(db *gorm.DB) error {
|
|
var existing *QuizCategory
|
|
err := db.Find(&existing, "name = ? and theme = ?", category.Name, category.Theme).Error
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if existing != nil {
|
|
category.Model = existing.Model
|
|
}
|
|
|
|
if err := db.Save(category).Error; err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
}))
|
|
}
|
|
|
|
func (s *Store) UpsertQuizEntry(ctx context.Context, entry *QuizEntry) error {
|
|
return errors.WithStack(s.Do(ctx, func(db *gorm.DB) error {
|
|
var existing *QuizEntry
|
|
err := db.Find(&existing, "provider = ? and provider_id = ?", entry.Provider, entry.ProviderID).Error
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if existing != nil {
|
|
entry.Model = existing.Model
|
|
}
|
|
|
|
if err := db.Save(entry).Error; err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
}))
|
|
}
|
|
|
|
func (s *Store) GetQuizTurn(ctx context.Context, playInterval time.Duration, period timex.PeriodType) (*QuizTurn, error) {
|
|
var quizTurn *QuizTurn
|
|
err := s.Tx(ctx, func(tx *gorm.DB) error {
|
|
now := time.Now().UTC()
|
|
|
|
err := tx.Model(&quizTurn).Preload("Entries").Preload("Entries.Category").Order("ended_at DESC").First(&quizTurn, "ended_at >= ?", now).Error
|
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if quizTurn != nil && quizTurn.ID != 0 {
|
|
return nil
|
|
}
|
|
|
|
interval := timex.NewInterval(playInterval, period)
|
|
current := interval.GetCurrent()
|
|
start, end := interval.GetIntervalBounds(current, time.Now().UTC())
|
|
|
|
quizTurn = &QuizTurn{
|
|
StartedAt: start.UTC(),
|
|
EndedAt: end.UTC(),
|
|
}
|
|
|
|
alreadyUsed := make([]uint, 0)
|
|
err = tx.Table("quiz_turn_entries").Pluck("quiz_entry_id", &alreadyUsed).Error
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
query := tx.Model(&QuizEntry{})
|
|
|
|
if len(alreadyUsed) > 0 {
|
|
query = query.Where("id NOT IN ?", alreadyUsed)
|
|
}
|
|
|
|
var entryIDs []uint
|
|
err = query.Pluck("id", &entryIDs).Error
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
for range 3 {
|
|
index := rand.IntN(len(entryIDs))
|
|
quizTurn.Entries = append(quizTurn.Entries, &QuizEntry{
|
|
Model: gorm.Model{
|
|
ID: entryIDs[index],
|
|
},
|
|
})
|
|
entryIDs = slices.Delete(entryIDs, index, index+1)
|
|
}
|
|
|
|
if err := tx.Save(quizTurn).Error; err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
err = tx.Model(&QuizTurn{}).Preload("Entries").Preload("Entries.Category").First(&quizTurn).Error
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
return quizTurn, nil
|
|
}
|