Skip to content

Commit

Permalink
Add strategies for swiping
Browse files Browse the repository at this point in the history
  • Loading branch information
yasuflatland-lf committed Aug 16, 2024
1 parent ef60a7c commit 3005ba9
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 102 deletions.
5 changes: 1 addition & 4 deletions backend/graph/services/card_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,8 @@ func (suite *CardTestSuite) TestCardService() {

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

if createdUser != nil {
createdGroup, _, _ := testutils.CreateUserAndCardGroup(ctx, userService, cardGroupService, roleService)

}
// Create 200 dummy cards
for i := 0; i < 200; i++ {
input := model.NewCard{
Expand Down
26 changes: 23 additions & 3 deletions backend/pkg/usecases/swipe_manager/def1_state_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,30 @@ package swipe_manager
import (
repository "backend/graph/db"
"golang.org/x/net/context"
"time"
)

type Def1StateStrategy struct{}
type def1StateStrategy struct {
swipeManagerUsecase SwipeManagerUsecase
}

type Def1StateStrategy interface {
ChangeState(ctx context.Context, swipeRecords []repository.SwipeRecord) error
IsApplicable(swipeRecords []repository.SwipeRecord) bool
}

func NewDef1StateStrategy(swipeManagerUsecase SwipeManagerUsecase) Def1StateStrategy {
return &def1StateStrategy{
swipeManagerUsecase: swipeManagerUsecase,
}
}

func (d *def1StateStrategy) ChangeState(ctx context.Context, swipeRecords []repository.SwipeRecord) error {
return d.swipeManagerUsecase.ChangeState(ctx, userID, DEF1)
}

func (d Def1StateStrategy) ChangeState(ctx context.Context, s *SwipeManagerUsecase, swipeRecords []repository.SwipeRecord) error {
return s.changeState(ctx, userID, DEF1)
func (d *def1StateStrategy) IsApplicable(swipeRecords []repository.SwipeRecord) bool {
// Check if none of the conditions match and time since last swipe is significant
// Implement the specific logic for Def1
return time.Since(swipeRecords[0].Updated) > 24*time.Hour
}
26 changes: 23 additions & 3 deletions backend/pkg/usecases/swipe_manager/def2_state_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,28 @@ import (
"golang.org/x/net/context"
)

type Def2StateStrategy struct{}
type def2StateStrategy struct {
swipeManagerUsecase SwipeManagerUsecase
}

type Def2StateStrategy interface {
ChangeState(ctx context.Context, swipeRecords []repository.SwipeRecord) error
IsApplicable(swipeRecords []repository.SwipeRecord) bool
}

// NewDef2StateStrategy creates a new instance of Def2StateStrategy.
func NewDef2StateStrategy(swipeManagerUsecase SwipeManagerUsecase) Def2StateStrategy {
return &def2StateStrategy{
swipeManagerUsecase: swipeManagerUsecase,
}
}

// ChangeState changes the state of the swipe records to DEF2.
func (d *def2StateStrategy) ChangeState(ctx context.Context, swipeRecords []repository.SwipeRecord) error {
// Assuming userID is somehow available in the context or swipeRecords
return d.swipeManagerUsecase.ChangeState(ctx, userID, DEF2)
}

func (d Def2StateStrategy) ChangeState(ctx context.Context, s *SwipeManagerUsecase, swipeRecords []repository.SwipeRecord) error {
return s.changeState(ctx, userID, DEF2)
func (d *def2StateStrategy) IsApplicable(swipeRecords []repository.SwipeRecord) bool {
return true
}
31 changes: 28 additions & 3 deletions backend/pkg/usecases/swipe_manager/difficult_state_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,36 @@ package swipe_manager

import (
repository "backend/graph/db"
"backend/graph/services"
"golang.org/x/net/context"
)

type DifficultStateStrategy struct{}
type difficultStateStrategy struct {
swipeManagerUsecase SwipeManagerUsecase
}

type DifficultStateStrategy interface {
ChangeState(ctx context.Context, swipeRecords []repository.SwipeRecord) error
IsApplicable(swipeRecords []repository.SwipeRecord) bool
}

func NewDifficultStateStrategy(swipeManagerUsecase SwipeManagerUsecase) DifficultStateStrategy {
return &difficultStateStrategy{
swipeManagerUsecase: swipeManagerUsecase,
}
}

func (d *difficultStateStrategy) ChangeState(ctx context.Context, swipeRecords []repository.SwipeRecord) error {
return d.swipeManagerUsecase.ChangeState(ctx, userID, DIFFICULT)
}

func (d DifficultStateStrategy) ChangeState(ctx context.Context, s *SwipeManagerUsecase, swipeRecords []repository.SwipeRecord) error {
return s.changeState(ctx, userID, DIFFICULT)
func (d *difficultStateStrategy) IsApplicable(swipeRecords []repository.SwipeRecord) bool {
// If the last 5 records indicate other than "known", configure difficult
unknownCount := 0
for i := 0; i < 5 && i < len(swipeRecords); i++ {
if swipeRecords[i].Direction != services.KNOWN {
unknownCount++
}
}
return unknownCount == 5
}
32 changes: 29 additions & 3 deletions backend/pkg/usecases/swipe_manager/easy_state_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,37 @@ package swipe_manager

import (
repository "backend/graph/db"
"backend/graph/services"
"golang.org/x/net/context"
)

type EasyStateStrategy struct{}
type easyStateStrategy struct {
swipeManagerUsecase SwipeManagerUsecase
}

type EasyStateStrategy interface {
ChangeState(ctx context.Context, swipeRecords []repository.SwipeRecord) error
IsApplicable(swipeRecords []repository.SwipeRecord) bool
}

func NewEasyStateStrategy(swipeManagerUsecase SwipeManagerUsecase) EasyStateStrategy {
return &easyStateStrategy{
swipeManagerUsecase: swipeManagerUsecase,
}
}

func (e *easyStateStrategy) ChangeState(ctx context.Context, swipeRecords []repository.SwipeRecord) error {
// Assuming userID and EASY are available in this context
return e.swipeManagerUsecase.ChangeState(ctx, userID, EASY)
}

func (e EasyStateStrategy) ChangeState(ctx context.Context, s *SwipeManagerUsecase, swipeRecords []repository.SwipeRecord) error {
return s.changeState(ctx, userID, EASY)
func (e *easyStateStrategy) IsApplicable(swipeRecords []repository.SwipeRecord) bool {
// Check if the last 5 records indicate "known"
knownCount := 0
for i := 0; i < 5 && i < len(swipeRecords); i++ {
if swipeRecords[i].Direction == services.KNOWN {
knownCount++
}
}
return knownCount == 5
}
34 changes: 31 additions & 3 deletions backend/pkg/usecases/swipe_manager/good_state_strategy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,39 @@ package swipe_manager

import (
repository "backend/graph/db"
"backend/graph/services"
"golang.org/x/net/context"
)

type GoodStateStrategy struct{}
// GoodStateStrategy implements the SwipeManagerUsecase interface
type goodStateStrategy struct {
swipeManagerUsecase SwipeManagerUsecase
}

type GoodStateStrategy interface {
ChangeState(ctx context.Context, swipeRecords []repository.SwipeRecord) error
IsApplicable(swipeRecords []repository.SwipeRecord) bool
}

// NewGoodStateStrategy returns an instance of GoodStateStrategy
func NewGoodStateStrategy(swipeManagerUsecase SwipeManagerUsecase) GoodStateStrategy {
return &goodStateStrategy{
swipeManagerUsecase: swipeManagerUsecase,
}
}

// ChangeState changes the state of the given swipe records to GOOD
func (g *goodStateStrategy) ChangeState(ctx context.Context, swipeRecords []repository.SwipeRecord) error {
return g.swipeManagerUsecase.ChangeState(ctx, userID, GOOD)
}

func (g GoodStateStrategy) ChangeState(ctx context.Context, s *SwipeManagerUsecase, swipeRecords []repository.SwipeRecord) error {
return s.changeState(ctx, userID, GOOD)
func (g *goodStateStrategy) IsApplicable(swipeRecords []repository.SwipeRecord) bool {
// Check if 5 out of the last 10 records are "known"
knownCount := 0
for i := 0; i < 10 && i < len(swipeRecords); i++ {
if swipeRecords[i].Direction == services.KNOWN {
knownCount++
}
}
return knownCount >= 5
}
23 changes: 0 additions & 23 deletions backend/pkg/usecases/swipe_manager/state_context.go

This file was deleted.

83 changes: 23 additions & 60 deletions backend/pkg/usecases/swipe_manager/swipe_manager_usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"context"
"fmt"
"github.com/m-mizutani/goerr"
"time"
)

// Enum definitions for states
Expand All @@ -18,25 +17,30 @@ const (
DEF2 = "Def2"
)

type SwipeManagerUsecase struct {
type swipeManagerUsecase struct {
swipeService services.SwipeRecordService
cardGroupService services.CardGroupService
cardService services.CardService
}

type SwipeManagerUsecase interface {
HandleSwipe(ctx context.Context, userID int64, cardGroupID int64, order string, limit int) ([]repository.Card, error)
ChangeState(ctx context.Context, userID int64, newState string) error
}

func NewSwipeManagerUsecase(
swipeService services.SwipeRecordService,
cardGroupService services.CardGroupService,
cardService services.CardService) *SwipeManagerUsecase {
return &SwipeManagerUsecase{
cardService services.CardService) SwipeManagerUsecase {
return &swipeManagerUsecase{
swipeService: swipeService,
cardGroupService: cardGroupService,
cardService: cardService,
}
}

// HandleSwipe Main function to execute state machine
func (s *SwipeManagerUsecase) HandleSwipe(ctx context.Context, userID int64, cardGroupID int64, order string, limit int) ([]repository.Card, error) {
func (s *swipeManagerUsecase) HandleSwipe(ctx context.Context, userID int64, cardGroupID int64, order string, limit int) ([]repository.Card, error) {
swipeRecords, err := s.swipeService.GetSwipeRecordsByUserAndOrder(ctx, userID, order, limit)
if err != nil {
return nil, goerr.Wrap(err)
Expand All @@ -49,7 +53,7 @@ func (s *SwipeManagerUsecase) HandleSwipe(ctx context.Context, userID int64, car
return s.handleExist(ctx, swipeRecords)
}

func (s *SwipeManagerUsecase) handleNotExist(ctx context.Context, cardGroupID int64, order string, limit int) ([]repository.Card, error) {
func (s *swipeManagerUsecase) handleNotExist(ctx context.Context, cardGroupID int64, order string, limit int) ([]repository.Card, error) {
// Retrieve cards by user and card group with the specified order and limit
cards, err := s.cardService.GetCardsByUserAndCardGroup(ctx, cardGroupID, order, limit)
if err != nil {
Expand All @@ -59,67 +63,26 @@ func (s *SwipeManagerUsecase) handleNotExist(ctx context.Context, cardGroupID in
return cards, nil
}

func (s *SwipeManagerUsecase) handleExist(ctx context.Context, swipeRecords []repository.SwipeRecord) ([]repository.Card, error) {
var strategy StateStrategy

switch {
case s.isDifficult(swipeRecords):
strategy = DifficultStateStrategy{}
case s.isGood(swipeRecords):
strategy = GoodStateStrategy{}
case s.isEasy(swipeRecords):
strategy = EasyStateStrategy{}
case s.isDef1(swipeRecords):
strategy = Def1StateStrategy{}
default:
strategy = Def2StateStrategy{}
}

context := &StateContext{}
context.SetStrategy(strategy)
return context.ExecuteStrategy(ctx, s, swipeRecords)
}

func (s *SwipeManagerUsecase) isDifficult(swipeRecords []repository.SwipeRecord) bool {
// If the last 5 records indicate other than "known", configure difficult
unknownCount := 0
for i := 0; i < 5 && i < len(swipeRecords); i++ {
if swipeRecords[i].Direction != services.KNOWN {
unknownCount++
}
func (s *swipeManagerUsecase) handleExist(ctx context.Context, swipeRecords []repository.SwipeRecord) ([]repository.Card, error) {
strategies := []SwipeStrategy{
NewDifficultStateStrategy(s),
NewGoodStateStrategy(s),
NewEasyStateStrategy(s),
NewDef1StateStrategy(s),
NewDef2StateStrategy(s), // Default strategy, placed last
}
return unknownCount == 5
}

func (s *SwipeManagerUsecase) isGood(swipeRecords []repository.SwipeRecord) bool {
// Check if 5 out of the last 10 records are "known"
knownCount := 0
for i := 0; i < 10 && i < len(swipeRecords); i++ {
if swipeRecords[i].Direction == services.KNOWN {
knownCount++
for _, strategy := range strategies {
if strategy.IsApplicable(swipeRecords) {
strategyExecutor := NewStrategyExecutor(strategy)
return strategyExecutor.ExecuteStrategy(ctx, swipeRecords)
}
}
return knownCount >= 5
}

func (s *SwipeManagerUsecase) isEasy(swipeRecords []repository.SwipeRecord) bool {
// Check if the last 5 records indicate "known"
knownCount := 0
for i := 0; i < 5 && i < len(swipeRecords); i++ {
if swipeRecords[i].Direction == services.KNOWN {
knownCount++
}
}
return knownCount == 5
}

func (s *SwipeManagerUsecase) isDef1(swipeRecords []repository.SwipeRecord) bool {
// Check if none of the conditions match and time since last swipe is significant
// Implement the specific logic for Def1
return time.Since(swipeRecords[0].Updated) > 24*time.Hour
return nil, nil // This should theoretically never be reached
}

func (s *SwipeManagerUsecase) changeState(ctx context.Context, userID int64, newState string) error {
func (s *swipeManagerUsecase) ChangeState(ctx context.Context, userID int64, newState string) error {
fmt.Printf("Changing user state to: %s\n", newState)
// Implement logic to change user state and display cards based on state
// ...
Expand Down
34 changes: 34 additions & 0 deletions backend/pkg/usecases/swipe_manager/swipe_strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package swipe_manager

import (
repository "backend/graph/db"
"github.com/m-mizutani/goerr"
"golang.org/x/net/context"
)

type SwipeStrategy interface {
ChangeState(ctx context.Context, swipeRecords []repository.SwipeRecord) error
IsApplicable(records []repository.SwipeRecord) bool
}

type strategyExecutor struct {
strategy SwipeStrategy
}

type StrategyExecutor interface {
ExecuteStrategy(ctx context.Context, swipeRecords []repository.SwipeRecord) ([]repository.Card, error)
}

func NewStrategyExecutor(strategy SwipeStrategy) StrategyExecutor {
return &strategyExecutor{
strategy: strategy,
}
}

func (sc *strategyExecutor) ExecuteStrategy(ctx context.Context, swipeRecords []repository.SwipeRecord) ([]repository.Card, error) {
err := sc.strategy.ChangeState(ctx, swipeRecords)
if err != nil {
return nil, goerr.Wrap(err)
}
return nil, nil
}

0 comments on commit 3005ba9

Please sign in to comment.