Skip to content

Commit

Permalink
Revise swipe record model
Browse files Browse the repository at this point in the history
  • Loading branch information
yasuflatland-lf committed Aug 17, 2024
1 parent 703439a commit c458c5e
Show file tree
Hide file tree
Showing 14 changed files with 325 additions and 174 deletions.
3 changes: 2 additions & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ PG_USER=your_db_user
PG_PASSWORD=your_db_password
PG_DBNAME=your_db_name
PG_PORT=5432
PG_SSLMODE=disable
PG_SSLMODE=disable
FL_BATCH_AMOUNT=10
51 changes: 43 additions & 8 deletions backend/graph/services/card.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"github.com/m-mizutani/goerr"
"math/rand"
"strings"
"time"

Expand All @@ -30,13 +31,14 @@ type CardService interface {
FetchAllCardsByCardGroup(ctx context.Context, cardGroupID int64, first *int) ([]*model.Card, error)
AddNewCards(ctx context.Context, targetCards []model.Card, cardGroupID int64) ([]*model.Card, error)
GetCardsByUserAndCardGroup(ctx context.Context, cardGroupID int64, order string, limit int) ([]repository.Card, error)
GetRandomCardsFromRecentUpdates(ctx context.Context, cardGroupID int64, limit int) ([]model.Card, error)
}

func NewCardService(db *gorm.DB, defaultLimit int) CardService {
return &cardService{db: db, defaultLimit: defaultLimit}
}

func convertToGormCard(input model.NewCard) *repository.Card {
func ConvertToGormCard(input model.NewCard) *repository.Card {
return &repository.Card{
Front: input.Front,
Back: input.Back,
Expand All @@ -53,7 +55,7 @@ func convertToGormCard(input model.NewCard) *repository.Card {
}
}

func convertToCard(card repository.Card) *model.Card {
func ConvertToCard(card repository.Card) *model.Card {
return &model.Card{
ID: card.ID,
Front: card.Front,
Expand All @@ -66,11 +68,20 @@ func convertToCard(card repository.Card) *model.Card {
}
}

func ConvertToCards(cards []repository.Card) []model.Card {
var result []model.Card
for _, card := range cards {
convertedCard := ConvertToCard(card)
result = append(result, *convertedCard)
}
return result
}

func convertCardConnection(cards []repository.Card, hasPrevPage, hasNextPage bool) *model.CardConnection {
var result model.CardConnection

for _, dbc := range cards {
card := convertToCard(dbc)
card := ConvertToCard(dbc)

// Use the ID directly as it is already of type int64
result.Edges = append(result.Edges, &model.CardEdge{Cursor: card.ID, Node: card})
Expand Down Expand Up @@ -98,19 +109,19 @@ func (s *cardService) GetCardByID(ctx context.Context, id int64) (*model.Card, e
logger.Logger.ErrorContext(ctx, "Failed to get card by ID", err)
return nil, err
}
return convertToCard(card), nil
return ConvertToCard(card), nil
}

func (s *cardService) CreateCard(ctx context.Context, input model.NewCard) (*model.Card, error) {
gormCard := convertToGormCard(input)
gormCard := ConvertToGormCard(input)
result := s.db.WithContext(ctx).Create(gormCard)
if result.Error != nil {
if strings.Contains(result.Error.Error(), "foreign key constraint") {
return nil, goerr.Wrap(fmt.Errorf("invalid card group ID"))
}
return nil, goerr.Wrap(result.Error, fmt.Errorf("failed to create card"))
}
return convertToCard(*gormCard), nil
return ConvertToCard(*gormCard), nil
}

func (s *cardService) UpdateCard(ctx context.Context, id int64, input model.NewCard) (*model.Card, error) {
Expand All @@ -133,7 +144,7 @@ func (s *cardService) UpdateCard(ctx context.Context, id int64, input model.NewC
if err := s.db.WithContext(ctx).Save(&card).Error; err != nil {
return nil, goerr.Wrap(err, "Failed to save card")
}
return convertToCard(card), nil
return ConvertToCard(card), nil
}

func (s *cardService) DeleteCard(ctx context.Context, id int64) (*bool, error) {
Expand Down Expand Up @@ -208,7 +219,7 @@ func (s *cardService) GetCardsByIDs(ctx context.Context, ids []int64) ([]*model.

var gqlCards []*model.Card
for _, card := range cards {
gqlCards = append(gqlCards, convertToCard(*card))
gqlCards = append(gqlCards, ConvertToCard(*card))
}

return gqlCards, nil
Expand Down Expand Up @@ -321,3 +332,27 @@ func (s *cardService) GetCardsByUserAndCardGroup(

return cards, nil
}

func (s *cardService) GetRandomCardsFromRecentUpdates(ctx context.Context, cardGroupID int64, limit int) ([]model.Card, error) {
var cards []repository.Card

err := s.db.WithContext(ctx).
Where("cardgroup_id = ?", cardGroupID).
Order("updated desc").
Limit(100).
Find(&cards).Error
if err != nil {
return nil, goerr.Wrap(err, "Failed to retrieve recent cards")
}

rng := rand.New(rand.NewSource(time.Now().UnixNano()))
rng.Shuffle(len(cards), func(i, j int) { cards[i], cards[j] = cards[j], cards[i] })

if len(cards) <= limit {
return ConvertToCards(cards), nil
}

randomCards := cards[:limit]

return ConvertToCards(randomCards), nil
}
63 changes: 63 additions & 0 deletions backend/graph/services/card_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,69 @@ func (suite *CardTestSuite) TestCardService() {
assert.Empty(suite.T(), cards)
})

suite.Run("Normal_GetRandomCardsFromRecentUpdates", func() {
// Arrange
createdGroup, _, _ := testutils.CreateUserAndCardGroup(ctx, userService, cardGroupService, roleService)

// Create 50 dummy cards
for i := 0; i < 50; i++ {
input := model.NewCard{
Front: "Front " + strconv.Itoa(i),
Back: "Back " + strconv.Itoa(i),
ReviewDate: time.Now().UTC(),
CardgroupID: createdGroup.ID,
}
_, err := cardService.CreateCard(ctx, input)
assert.NoError(t, err)
}

// Act
limit := 10
randomCards1, err := cardService.GetRandomCardsFromRecentUpdates(ctx, createdGroup.ID, limit)
assert.NoError(t, err)
assert.Len(t, randomCards1, limit) // Ensure that 10 cards are returned

randomCards2, err := cardService.GetRandomCardsFromRecentUpdates(ctx, createdGroup.ID, limit)
assert.NoError(t, err)
assert.Len(t, randomCards2, limit) // Ensure that 10 cards are returned

// Assert
sameOrder := true
for i := range randomCards1 {
if randomCards1[i].ID != randomCards2[i].ID {
sameOrder = false
break
}
}

// Ensure the order is different (i.e., shuffled)
assert.False(t, sameOrder, "The order of cards should be different between the two calls")
})

suite.Run("Normal_GetRandomCardsFromRecentUpdates_LessThanLimit", func() {
// Arrange
createdGroup, _, _ := testutils.CreateUserAndCardGroup(ctx, userService, cardGroupService, roleService)

// Create 5 dummy cards
for i := 0; i < 5; i++ {
input := model.NewCard{
Front: "Front " + strconv.Itoa(i),
Back: "Back " + strconv.Itoa(i),
ReviewDate: time.Now().UTC(),
CardgroupID: createdGroup.ID,
}
_, err := cardService.CreateCard(ctx, input)
assert.NoError(t, err)
}

// Act
limit := 10
randomCards, err := cardService.GetRandomCardsFromRecentUpdates(ctx, createdGroup.ID, limit)

// Assert
assert.NoError(t, err)
assert.Len(t, randomCards, 5) // Ensure that only the available 5 cards are returned
})
}

func TestCardTestSuite(t *testing.T) {
Expand Down
42 changes: 23 additions & 19 deletions backend/graph/services/swiperecord.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,26 @@ func NewSwipeRecordService(db *gorm.DB, defaultLimit int) SwipeRecordService {
return &swipeRecordService{db: db, defaultLimit: defaultLimit}
}

func convertToGormSwipeRecord(input model.NewSwipeRecord) *repository.SwipeRecord {
func ConvertToGormSwipeRecord(input model.NewSwipeRecord) *repository.SwipeRecord {
return &repository.SwipeRecord{
UserID: input.UserID,
Direction: input.Direction,
Created: input.Created,
Updated: input.Updated,
UserID: input.UserID,
CardID: input.CardID,
CardGroupID: input.CardGroupID,
Direction: input.Direction,
Created: input.Created,
Updated: input.Updated,
}
}

func convertToSwipeRecord(swipeRecord repository.SwipeRecord) *model.SwipeRecord {
func ConvertToSwipeRecord(swipeRecord repository.SwipeRecord) *model.SwipeRecord {
return &model.SwipeRecord{
ID: swipeRecord.ID,
UserID: swipeRecord.UserID,
Direction: swipeRecord.Direction,
Created: swipeRecord.Created,
Updated: swipeRecord.Updated,
ID: swipeRecord.ID,
UserID: swipeRecord.UserID,
CardID: swipeRecord.CardID,
CardGroupID: swipeRecord.CardGroupID,
Direction: swipeRecord.Direction,
Created: swipeRecord.Created,
Updated: swipeRecord.Updated,
}
}

Expand All @@ -67,19 +71,19 @@ func (s *swipeRecordService) GetSwipeRecordByID(ctx context.Context, id int64) (
}
return nil, goerr.Wrap(err, "failed to get swipe record by ID")
}
return convertToSwipeRecord(swipeRecord), nil
return ConvertToSwipeRecord(swipeRecord), nil
}

func (s *swipeRecordService) CreateSwipeRecord(ctx context.Context, input model.NewSwipeRecord) (*model.SwipeRecord, error) {
gormSwipeRecord := convertToGormSwipeRecord(input)
gormSwipeRecord := ConvertToGormSwipeRecord(input)
result := s.db.WithContext(ctx).Create(gormSwipeRecord)
if result.Error != nil {
if strings.Contains(result.Error.Error(), "foreign key constraint") {
return nil, goerr.Wrap(fmt.Errorf("invalid swipe ID or card ID"), result.Error)
}
return nil, goerr.Wrap(result.Error, "failed to create swipe record")
}
return convertToSwipeRecord(*gormSwipeRecord), nil
return ConvertToSwipeRecord(*gormSwipeRecord), nil
}

func (s *swipeRecordService) UpdateSwipeRecord(ctx context.Context, id int64, input model.NewSwipeRecord) (*model.SwipeRecord, error) {
Expand All @@ -93,7 +97,7 @@ func (s *swipeRecordService) UpdateSwipeRecord(ctx context.Context, id int64, in
if err := s.db.WithContext(ctx).Save(&swipeRecord).Error; err != nil {
return nil, goerr.Wrap(err, "failed to update swipe record")
}
return convertToSwipeRecord(swipeRecord), nil
return ConvertToSwipeRecord(swipeRecord), nil
}

func (s *swipeRecordService) DeleteSwipeRecord(ctx context.Context, id int64) (*bool, error) {
Expand All @@ -117,7 +121,7 @@ func (s *swipeRecordService) SwipeRecords(ctx context.Context) ([]*model.SwipeRe
}
var gqlSwipeRecords []*model.SwipeRecord
for _, swipeRecord := range swipeRecords {
gqlSwipeRecords = append(gqlSwipeRecords, convertToSwipeRecord(swipeRecord))
gqlSwipeRecords = append(gqlSwipeRecords, ConvertToSwipeRecord(swipeRecord))
}
return gqlSwipeRecords, nil
}
Expand All @@ -133,7 +137,7 @@ func (s *swipeRecordService) SwipeRecordsByUser(ctx context.Context, userID int6
}
var gqlSwipeRecords []*model.SwipeRecord
for _, swipeRecord := range swipeRecords {
gqlSwipeRecords = append(gqlSwipeRecords, convertToSwipeRecord(swipeRecord))
gqlSwipeRecords = append(gqlSwipeRecords, ConvertToSwipeRecord(swipeRecord))
}
return gqlSwipeRecords, nil
}
Expand Down Expand Up @@ -163,7 +167,7 @@ func (s *swipeRecordService) PaginatedSwipeRecordsByUser(ctx context.Context, us
var edges []*model.SwipeRecordEdge
var nodes []*model.SwipeRecord
for _, swipeRecord := range swipeRecords {
node := convertToSwipeRecord(swipeRecord)
node := ConvertToSwipeRecord(swipeRecord)
edges = append(edges, &model.SwipeRecordEdge{
Cursor: swipeRecord.ID,
Node: node,
Expand Down Expand Up @@ -197,7 +201,7 @@ func (s *swipeRecordService) GetSwipeRecordsByIDs(ctx context.Context, ids []int

var gqlSwipeRecords []*model.SwipeRecord
for _, swipeRecord := range swipeRecords {
gqlSwipeRecords = append(gqlSwipeRecords, convertToSwipeRecord(*swipeRecord))
gqlSwipeRecords = append(gqlSwipeRecords, ConvertToSwipeRecord(*swipeRecord))
}

return gqlSwipeRecords, nil
Expand Down
Loading

0 comments on commit c458c5e

Please sign in to comment.