Files
kouiz/internal/store/quiz.go
2025-06-15 14:46:32 +02:00

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
}