diff --git a/model/batch.go b/model/batch.go index 0f99e6d..049b37f 100644 --- a/model/batch.go +++ b/model/batch.go @@ -12,6 +12,7 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" + "github.com/shopspring/decimal" ) var ( @@ -234,6 +235,298 @@ func (b *CreateOrUpdateFixturesBatchResults) Close() error { return b.br.Close() } +const createOrUpdateGamePlayerScores = `-- name: CreateOrUpdateGamePlayerScores :batchexec +INSERT INTO game_player_scores( + game_id, + player_slug, + score, + decisive_score, + all_around_score, + minutes_played, + game_started, + formation_place, + live, + on_game_sheet, + reviewed, + goal, + assist, + penalty_won, + clearance_off_line, + last_man_tackle, + penalty_save, + own_goal, + red_card, + error_lead_to_goal, + penalty_conceded, + yellow_card, + fouls, + fouled, + clean_sheet, + double_double, + triple_double, + triple_triple, + error_lead_to_shot, + saves, + saved_shot_from_inside_box, + good_high_claim, + punches, + diving_save, + diving_catch, + cross_not_claimed, + goalkeeper_smother, + six_second_violation, + keeper_sweeper, + goals_conceded, + effective_clearance, + won_tackle, + blocked_cross, + block, + possession_lost, + possession_won, + duel_lost, + duel_won, + interception, + accurate_pass, + accurate_final_third_pass, + accurate_long_ball, + long_pass_into_opposition, + missed_pass, + shot_on_target, + won_contest, + big_chance_created, + attempted_assist, + penalty_area_entries, + penalty_kick_missed, + big_chance_missed) +VALUES( + $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31, + $32,$33,$34,$35,$36,$37,$38,$39,$40,$41,$42,$43,$44,$45,$46,$47,$48,$49,$50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$60, + $61) +ON CONFLICT (game_id, player_slug) DO UPDATE +SET score = EXCLUDED.score, + decisive_score = EXCLUDED.decisive_score, + all_around_score = EXCLUDED.all_around_score, + minutes_played = EXCLUDED.minutes_played, + game_started = EXCLUDED.game_started, + formation_place = EXCLUDED.formation_place, + live = EXCLUDED.live, + on_game_sheet = EXCLUDED.on_game_sheet, + reviewed = EXCLUDED.reviewed, + goal = EXCLUDED.goal, + assist = EXCLUDED.assist, + penalty_won = EXCLUDED.penalty_won, + clearance_off_line = EXCLUDED.clearance_off_line, + last_man_tackle = EXCLUDED.last_man_tackle, + penalty_save = EXCLUDED.penalty_save, + own_goal = EXCLUDED.own_goal, + red_card = EXCLUDED.red_card, + error_lead_to_goal = EXCLUDED.error_lead_to_goal, + penalty_conceded = EXCLUDED.penalty_conceded, + yellow_card = EXCLUDED.yellow_card, + fouls = EXCLUDED.fouls, + fouled = EXCLUDED.fouled, + clean_sheet = EXCLUDED.clean_sheet, + double_double = EXCLUDED.double_double, + triple_double = EXCLUDED.triple_double, + triple_triple = EXCLUDED.triple_triple, + error_lead_to_shot = EXCLUDED.error_lead_to_shot, + saves = EXCLUDED.saves, + saved_shot_from_inside_box = EXCLUDED.saved_shot_from_inside_box, + good_high_claim = EXCLUDED.good_high_claim, + punches = EXCLUDED.punches, + diving_save = EXCLUDED.diving_save, + diving_catch = EXCLUDED.diving_catch, + cross_not_claimed = EXCLUDED.cross_not_claimed, + goalkeeper_smother = EXCLUDED.goalkeeper_smother, + six_second_violation = EXCLUDED.six_second_violation, + keeper_sweeper = EXCLUDED.keeper_sweeper, + goals_conceded = EXCLUDED.goals_conceded, + effective_clearance = EXCLUDED.effective_clearance, + won_tackle = EXCLUDED.won_tackle, + blocked_cross = EXCLUDED.blocked_cross, + block = EXCLUDED.block, + possession_lost = EXCLUDED.possession_lost, + possession_won = EXCLUDED.possession_won, + duel_lost = EXCLUDED.duel_lost, + duel_won = EXCLUDED.duel_won, + interception = EXCLUDED.interception, + accurate_pass = EXCLUDED.accurate_pass, + accurate_final_third_pass = EXCLUDED.accurate_final_third_pass, + accurate_long_ball = EXCLUDED.accurate_long_ball, + long_pass_into_opposition = EXCLUDED.long_pass_into_opposition, + missed_pass = EXCLUDED.missed_pass, + shot_on_target = EXCLUDED.shot_on_target, + won_contest = EXCLUDED.won_contest, + big_chance_created = EXCLUDED.big_chance_created, + attempted_assist = EXCLUDED.attempted_assist, + penalty_area_entries = EXCLUDED.penalty_area_entries, + penalty_kick_missed = EXCLUDED.penalty_kick_missed, + big_chance_missed = EXCLUDED.big_chance_missed +` + +type CreateOrUpdateGamePlayerScoresBatchResults struct { + br pgx.BatchResults + tot int + closed bool +} + +type CreateOrUpdateGamePlayerScoresParams struct { + GameID string + PlayerSlug string + Score decimal.Decimal + DecisiveScore decimal.Decimal + AllAroundScore decimal.Decimal + MinutesPlayed int32 + GameStarted bool + FormationPlace int32 + Live bool + OnGameSheet bool + Reviewed bool + Goal int32 + Assist int32 + PenaltyWon int32 + ClearanceOffLine int32 + LastManTackle int32 + PenaltySave int32 + OwnGoal int32 + RedCard bool + ErrorLeadToGoal int32 + PenaltyConceded int32 + YellowCard int32 + Fouls int32 + Fouled int32 + CleanSheet bool + DoubleDouble bool + TripleDouble bool + TripleTriple bool + ErrorLeadToShot int32 + Saves int32 + SavedShotFromInsideBox int32 + GoodHighClaim int32 + Punches int32 + DivingSave int32 + DivingCatch int32 + CrossNotClaimed int32 + GoalkeeperSmother int32 + SixSecondViolation int32 + KeeperSweeper int32 + GoalsConceded int32 + EffectiveClearance int32 + WonTackle int32 + BlockedCross int32 + Block int32 + PossessionLost int32 + PossessionWon int32 + DuelLost int32 + DuelWon int32 + Interception int32 + AccuratePass int32 + AccurateFinalThirdPass int32 + AccurateLongBall int32 + LongPassIntoOpposition int32 + MissedPass int32 + ShotOnTarget int32 + WonContest int32 + BigChanceCreated int32 + AttemptedAssist int32 + PenaltyAreaEntries int32 + PenaltyKickMissed int32 + BigChanceMissed int32 +} + +func (q *Queries) CreateOrUpdateGamePlayerScores(ctx context.Context, arg []CreateOrUpdateGamePlayerScoresParams) *CreateOrUpdateGamePlayerScoresBatchResults { + batch := &pgx.Batch{} + for _, a := range arg { + vals := []interface{}{ + a.GameID, + a.PlayerSlug, + a.Score, + a.DecisiveScore, + a.AllAroundScore, + a.MinutesPlayed, + a.GameStarted, + a.FormationPlace, + a.Live, + a.OnGameSheet, + a.Reviewed, + a.Goal, + a.Assist, + a.PenaltyWon, + a.ClearanceOffLine, + a.LastManTackle, + a.PenaltySave, + a.OwnGoal, + a.RedCard, + a.ErrorLeadToGoal, + a.PenaltyConceded, + a.YellowCard, + a.Fouls, + a.Fouled, + a.CleanSheet, + a.DoubleDouble, + a.TripleDouble, + a.TripleTriple, + a.ErrorLeadToShot, + a.Saves, + a.SavedShotFromInsideBox, + a.GoodHighClaim, + a.Punches, + a.DivingSave, + a.DivingCatch, + a.CrossNotClaimed, + a.GoalkeeperSmother, + a.SixSecondViolation, + a.KeeperSweeper, + a.GoalsConceded, + a.EffectiveClearance, + a.WonTackle, + a.BlockedCross, + a.Block, + a.PossessionLost, + a.PossessionWon, + a.DuelLost, + a.DuelWon, + a.Interception, + a.AccuratePass, + a.AccurateFinalThirdPass, + a.AccurateLongBall, + a.LongPassIntoOpposition, + a.MissedPass, + a.ShotOnTarget, + a.WonContest, + a.BigChanceCreated, + a.AttemptedAssist, + a.PenaltyAreaEntries, + a.PenaltyKickMissed, + a.BigChanceMissed, + } + batch.Queue(createOrUpdateGamePlayerScores, vals...) + } + br := q.db.SendBatch(ctx, batch) + return &CreateOrUpdateGamePlayerScoresBatchResults{br, len(arg), false} +} + +func (b *CreateOrUpdateGamePlayerScoresBatchResults) Exec(f func(int, error)) { + defer b.br.Close() + for t := 0; t < b.tot; t++ { + if b.closed { + if f != nil { + f(t, ErrBatchAlreadyClosed) + } + continue + } + _, err := b.br.Exec() + if f != nil { + f(t, err) + } + } +} + +func (b *CreateOrUpdateGamePlayerScoresBatchResults) Close() error { + b.closed = true + return b.br.Close() +} + const createOrUpdateGamePlayers = `-- name: CreateOrUpdateGamePlayers :batchexec INSERT INTO game_players( game_id, diff --git a/model/game_player_score.sql.go b/model/game_player_score.sql.go new file mode 100644 index 0000000..d777741 --- /dev/null +++ b/model/game_player_score.sql.go @@ -0,0 +1,8 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 +// source: game_player_score.sql + +package model + +import () diff --git a/model/sql/game_player_score.sql b/model/sql/game_player_score.sql new file mode 100644 index 0000000..887da06 --- /dev/null +++ b/model/sql/game_player_score.sql @@ -0,0 +1,128 @@ +-- name: CreateOrUpdateGamePlayerScores :batchexec +INSERT INTO game_player_scores( + game_id, + player_slug, + score, + decisive_score, + all_around_score, + minutes_played, + game_started, + formation_place, + live, + on_game_sheet, + reviewed, + goal, + assist, + penalty_won, + clearance_off_line, + last_man_tackle, + penalty_save, + own_goal, + red_card, + error_lead_to_goal, + penalty_conceded, + yellow_card, + fouls, + fouled, + clean_sheet, + double_double, + triple_double, + triple_triple, + error_lead_to_shot, + saves, + saved_shot_from_inside_box, + good_high_claim, + punches, + diving_save, + diving_catch, + cross_not_claimed, + goalkeeper_smother, + six_second_violation, + keeper_sweeper, + goals_conceded, + effective_clearance, + won_tackle, + blocked_cross, + block, + possession_lost, + possession_won, + duel_lost, + duel_won, + interception, + accurate_pass, + accurate_final_third_pass, + accurate_long_ball, + long_pass_into_opposition, + missed_pass, + shot_on_target, + won_contest, + big_chance_created, + attempted_assist, + penalty_area_entries, + penalty_kick_missed, + big_chance_missed) +VALUES( + $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31, + $32,$33,$34,$35,$36,$37,$38,$39,$40,$41,$42,$43,$44,$45,$46,$47,$48,$49,$50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$60, + $61) +ON CONFLICT (game_id, player_slug) DO UPDATE +SET score = EXCLUDED.score, + decisive_score = EXCLUDED.decisive_score, + all_around_score = EXCLUDED.all_around_score, + minutes_played = EXCLUDED.minutes_played, + game_started = EXCLUDED.game_started, + formation_place = EXCLUDED.formation_place, + live = EXCLUDED.live, + on_game_sheet = EXCLUDED.on_game_sheet, + reviewed = EXCLUDED.reviewed, + goal = EXCLUDED.goal, + assist = EXCLUDED.assist, + penalty_won = EXCLUDED.penalty_won, + clearance_off_line = EXCLUDED.clearance_off_line, + last_man_tackle = EXCLUDED.last_man_tackle, + penalty_save = EXCLUDED.penalty_save, + own_goal = EXCLUDED.own_goal, + red_card = EXCLUDED.red_card, + error_lead_to_goal = EXCLUDED.error_lead_to_goal, + penalty_conceded = EXCLUDED.penalty_conceded, + yellow_card = EXCLUDED.yellow_card, + fouls = EXCLUDED.fouls, + fouled = EXCLUDED.fouled, + clean_sheet = EXCLUDED.clean_sheet, + double_double = EXCLUDED.double_double, + triple_double = EXCLUDED.triple_double, + triple_triple = EXCLUDED.triple_triple, + error_lead_to_shot = EXCLUDED.error_lead_to_shot, + saves = EXCLUDED.saves, + saved_shot_from_inside_box = EXCLUDED.saved_shot_from_inside_box, + good_high_claim = EXCLUDED.good_high_claim, + punches = EXCLUDED.punches, + diving_save = EXCLUDED.diving_save, + diving_catch = EXCLUDED.diving_catch, + cross_not_claimed = EXCLUDED.cross_not_claimed, + goalkeeper_smother = EXCLUDED.goalkeeper_smother, + six_second_violation = EXCLUDED.six_second_violation, + keeper_sweeper = EXCLUDED.keeper_sweeper, + goals_conceded = EXCLUDED.goals_conceded, + effective_clearance = EXCLUDED.effective_clearance, + won_tackle = EXCLUDED.won_tackle, + blocked_cross = EXCLUDED.blocked_cross, + block = EXCLUDED.block, + possession_lost = EXCLUDED.possession_lost, + possession_won = EXCLUDED.possession_won, + duel_lost = EXCLUDED.duel_lost, + duel_won = EXCLUDED.duel_won, + interception = EXCLUDED.interception, + accurate_pass = EXCLUDED.accurate_pass, + accurate_final_third_pass = EXCLUDED.accurate_final_third_pass, + accurate_long_ball = EXCLUDED.accurate_long_ball, + long_pass_into_opposition = EXCLUDED.long_pass_into_opposition, + missed_pass = EXCLUDED.missed_pass, + shot_on_target = EXCLUDED.shot_on_target, + won_contest = EXCLUDED.won_contest, + big_chance_created = EXCLUDED.big_chance_created, + attempted_assist = EXCLUDED.attempted_assist, + penalty_area_entries = EXCLUDED.penalty_area_entries, + penalty_kick_missed = EXCLUDED.penalty_kick_missed, + big_chance_missed = EXCLUDED.big_chance_missed; + diff --git a/sorare_utils/game_player.go b/sorare_utils/game_player.go index e410f01..1a752d8 100644 --- a/sorare_utils/game_player.go +++ b/sorare_utils/game_player.go @@ -1 +1,52 @@ package sorare_utils + +import ( + "git.lehouerou.net/laurent/sorare/football" + + "git.lehouerou.net/laurent/sorarebuddy/model" +) + +func ExtractPlayersFromGameWithFormation( + gameWithFormation football.GameWithFormation, +) []model.CreateOrUpdateGamePlayersParams { + var res []model.CreateOrUpdateGamePlayersParams + for _, p := range gameWithFormation.HomeFormation.Bench { + res = append(res, model.CreateOrUpdateGamePlayersParams{ + GameID: gameWithFormation.Id.Value, + PlayerSlug: p.Slug, + TeamSlug: gameWithFormation.HomeTeam.Team.Slug, + Status: "bench", + }) + } + for _, p := range gameWithFormation.HomeFormation.StartingLineup { + for _, q := range p { + res = append(res, model.CreateOrUpdateGamePlayersParams{ + GameID: gameWithFormation.Id.Value, + PlayerSlug: q.Slug, + TeamSlug: gameWithFormation.HomeTeam.Team.Slug, + Status: "starting", + }) + } + } + for _, p := range gameWithFormation.AwayFormation.Bench { + res = append(res, model.CreateOrUpdateGamePlayersParams{ + GameID: gameWithFormation.Id.Value, + PlayerSlug: p.Slug, + TeamSlug: gameWithFormation.AwayTeam.Team.Slug, + Status: "bench", + }) + } + for _, p := range gameWithFormation.AwayFormation.StartingLineup { + for _, q := range p { + res = append(res, model.CreateOrUpdateGamePlayersParams{ + GameID: gameWithFormation.Id.Value, + PlayerSlug: q.Slug, + TeamSlug: gameWithFormation.AwayTeam.Team.Slug, + Status: "starting", + }) + } + } + + return res + +} diff --git a/sorare_utils/game_player_score.go b/sorare_utils/game_player_score.go new file mode 100644 index 0000000..74d5958 --- /dev/null +++ b/sorare_utils/game_player_score.go @@ -0,0 +1,166 @@ +package sorare_utils + +import ( + "git.lehouerou.net/laurent/sorare/football" + "github.com/shopspring/decimal" + + "git.lehouerou.net/laurent/sorarebuddy/model" +) + +func NewCreateOrUpdateGamePlayerScoresParamsFromSorare( + gameId string, + playerScore football.PlayerScore, +) model.CreateOrUpdateGamePlayerScoresParams { + s := playerScore.So5Score + + res := model.CreateOrUpdateGamePlayerScoresParams{ + GameID: gameId, + PlayerSlug: s.Player.Slug, + MinutesPlayed: int32(s.PlayerGameStats.MinsPlayed), + GameStarted: s.PlayerGameStats.GameStarted == 1, + FormationPlace: int32(s.PlayerGameStats.FormationPlace), + Live: s.PlayerGameStats.Live, + OnGameSheet: s.PlayerGameStats.OnGameSheet, + Reviewed: s.PlayerGameStats.Reviewed, + } + score := s.Score + decisiveScore := s.DecisiveScore.TotalScore + allAroundScore := decimal.Zero + tacklefound := false + for _, stat := range s.AllAroundStats { + switch stat.Stat { + case "yellow_card": + res.YellowCard = int32(stat.StatValue.IntPart()) + case "fouls": + res.Fouls = int32(stat.StatValue.IntPart()) + case "was_fouled": + res.Fouled = int32(stat.StatValue.IntPart()) + case "clean_sheet_60": + res.CleanSheet = int(stat.StatValue.IntPart()) == 1 + case "double_double": + res.DoubleDouble = int(stat.StatValue.IntPart()) == 1 + case "triple_double": + res.TripleDouble = int(stat.StatValue.IntPart()) == 1 + case "triple_triple": + res.TripleTriple = int(stat.StatValue.IntPart()) == 1 + case "error_lead_to_shot": + res.ErrorLeadToShot = int32(stat.StatValue.IntPart()) + + case "saves": + res.Saves = int32(stat.StatValue.IntPart()) + case "saved_ibox": + res.SavedShotFromInsideBox = int32(stat.StatValue.IntPart()) + case "good_high_claim": + res.GoodHighClaim = int32(stat.StatValue.IntPart()) + case "punches": + res.Punches = int32(stat.StatValue.IntPart()) + case "dive_save": + res.DivingSave = int32(stat.StatValue.IntPart()) + case "dive_catch": + res.DivingCatch = int32(stat.StatValue.IntPart()) + case "cross_not_claimed": + res.CrossNotClaimed = int32(stat.StatValue.IntPart()) + case "six_second_violation": + res.SixSecondViolation = int32(stat.StatValue.IntPart()) + case "gk_smother": + res.GoalkeeperSmother = int32(stat.StatValue.IntPart()) + case "accurate_keeper_sweeper": + res.KeeperSweeper = int32(stat.StatValue.IntPart()) + + case "goals_conceded": + res.GoalsConceded = int32(stat.StatValue.IntPart()) + case "effective_clearance": + res.EffectiveClearance = int32(stat.StatValue.IntPart()) + case "won_tackle": + res.WonTackle = int32(stat.StatValue.IntPart()) + tacklefound = true + case "blocked_cross": + res.BlockedCross = int32(stat.StatValue.IntPart()) + case "outfielder_block": + res.Block = int32(stat.StatValue.IntPart()) + + case "poss_lost_ctrl": + res.PossessionLost = int32(stat.StatValue.IntPart()) + case "poss_won": + res.PossessionWon = int32(stat.StatValue.IntPart()) + case "duel_lost": + res.DuelLost = int32(stat.StatValue.IntPart()) + case "duel_won": + res.DuelWon = int32(stat.StatValue.IntPart()) + case "interception_won": + res.Interception = int32(stat.StatValue.IntPart()) + case "accurate_pass": + res.AccuratePass = int32(stat.StatValue.IntPart()) + case "successful_final_third_passes": + res.AccurateFinalThirdPass = int32(stat.StatValue.IntPart()) + case "accurate_long_balls": + res.AccurateLongBall = int32(stat.StatValue.IntPart()) + case "long_pass_own_to_opp_success": + res.LongPassIntoOpposition = int32(stat.StatValue.IntPart()) + + case "ontarget_scoring_att": + res.ShotOnTarget = int32(stat.StatValue.IntPart()) + case "won_contest": + res.WonContest = int32(stat.StatValue.IntPart()) + case "pen_area_entries": + res.PenaltyAreaEntries = int32(stat.StatValue.IntPart()) + case "big_chance_created": + res.BigChanceCreated = int32(stat.StatValue.IntPart()) + case "adjusted_total_att_assist": + res.AttemptedAssist = int32(stat.StatValue.IntPart()) + case "penalty_kick_missed": + res.PenaltyKickMissed = int32(stat.StatValue.IntPart()) + case "big_chance_missed": + res.BigChanceMissed = int32(stat.StatValue.IntPart()) + } + + allAroundScore = allAroundScore.Add(stat.TotalScore) + } + res.MissedPass = int32(s.PlayerGameStats.TotalPass - s.PlayerGameStats.AccuratePass) + if !tacklefound { + res.WonTackle = int32(s.PlayerGameStats.TotalTackle) + } + + for _, stat := range s.PositiveDecisiveStats { + switch stat.Stat { + case "goals": + res.Goal = int32(stat.StatValue.IntPart()) + case "goal_assist": + res.Assist = int32(stat.StatValue.IntPart()) + case "assist_penalty_won": + res.PenaltyWon = int32(stat.StatValue.IntPart()) + case "clearance_off_line": + res.ClearanceOffLine = int32(stat.StatValue.IntPart()) + case "last_man_tackle": + res.LastManTackle = int32(stat.StatValue.IntPart()) + case "clean_sheet_60": + res.CleanSheet = int32(stat.StatValue.IntPart()) == 1 + case "penalty_save": + res.PenaltySave = int32(stat.StatValue.IntPart()) + } + } + + for _, stat := range s.NegativeDecisiveStats { + switch stat.Stat { + case "red_card": + res.RedCard = int(stat.StatValue.IntPart()) == 1 + case "own_goals": + res.OwnGoal = int32(stat.StatValue.IntPart()) + case "error_lead_to_goal": + res.ErrorLeadToGoal = int32(stat.StatValue.IntPart()) + case "penalty_conceded": + res.PenaltyConceded = int32(stat.StatValue.IntPart()) + } + } + + if s.PlayerGameStats.MinsPlayed == 0 { + score = decimal.Zero + decisiveScore = decimal.Zero + allAroundScore = decimal.Zero + } + res.Score = score + res.DecisiveScore = decisiveScore + res.AllAroundScore = allAroundScore + + return res +} diff --git a/sorare_utils/update_service.go b/sorare_utils/update_service.go index 375517f..591e721 100644 --- a/sorare_utils/update_service.go +++ b/sorare_utils/update_service.go @@ -27,7 +27,7 @@ func NewUpdateService(s *sorare.Sorare, db *model.Queries) *UpdateService { func (u *UpdateService) InitSyncDatabase(ctx context.Context) error { sfixtures, err := u.s.Football.So5.So5Fixtures.Get(ctx, football.So5FixturesParams{ - AasmStates: []string{"started"}, + // AasmStates: []string{"started"}, }) if err != nil { return err @@ -76,6 +76,7 @@ func (u *UpdateService) InitSyncDatabase(ctx context.Context) error { log.Debug().Msgf("getting players for each game...") var gamePlayers []model.CreateOrUpdateGamePlayersParams + playerSlugsByGameMap := make(map[string][]string) for _, game := range games { gameWithFormation, err := u.s.Football.Game.Get(ctx, graphql.IdParams{Id: gql.ID(game.Id.Value)}) if err != nil { @@ -83,6 +84,12 @@ func (u *UpdateService) InitSyncDatabase(ctx context.Context) error { } newplayers := ExtractPlayersFromGameWithFormation(gameWithFormation) log.Debug().Msgf("\t%s -> %d players", game.String(), len(newplayers)) + playerSlugsByGameMap[game.Id.Value] = lo.Map( + newplayers, + func(player model.CreateOrUpdateGamePlayersParams, index int) string { + return player.PlayerSlug + }, + ) gamePlayers = append(gamePlayers, newplayers...) } @@ -170,6 +177,24 @@ func (u *UpdateService) InitSyncDatabase(ctx context.Context) error { } log.Debug().Msgf("found %d countries", len(countries)) + scores := make(map[string][]football.PlayerScore) + for gameId, playerSlugs := range playerSlugsByGameMap { + log.Debug().Msgf("getting scores for game %s...", gameId) + var gameScores []football.PlayerScore + for i, chunk := range lo.Chunk(playerSlugs, 80) { + log.Debug().Msgf("\tbatch %d/%d", i+1, (len(playerSlugs)/80)+1) + s, err := u.s.Football.PlayersGameScores(gql.ID(gameId)).Get(ctx, graphql.SlugsParams{Slugs: chunk}) + if err != nil { + return errors.Wrapf(err, "getting scores for game %s", gameId) + } + gameScores = append(gameScores, s...) + } + for _, score := range gameScores { + log.Debug().Msgf("\t%s -> %s", score.Slug, score.So5Score.Score) + } + scores[gameId] = gameScores + } + log.Debug().Msg("inserting countries into db...") batchCountries := u.db.CreateOrUpdateCountries( ctx, @@ -342,54 +367,35 @@ func (u *UpdateService) InitSyncDatabase(ctx context.Context) error { } log.Debug().Msgf("%d game players inserted", len(gamePlayers)) + log.Debug().Msg("inserting game player scores into db...") + batchGamePlayerScores := u.db.CreateOrUpdateGamePlayerScores( + ctx, + lo.Union( + lo.MapToSlice( + scores, + func(gameId string, scores []football.PlayerScore) []model.CreateOrUpdateGamePlayerScoresParams { + return lo.Map(scores, func(score football.PlayerScore, index int) model.CreateOrUpdateGamePlayerScoresParams { + return NewCreateOrUpdateGamePlayerScoresParamsFromSorare(gameId, score) + }) + + }, + )...), + ) + batcherr = nil + batchGamePlayerScores.Exec(func(i int, err error) { + if err != nil { + batcherr = err + batchGamePlayerScores.Close() + } + }) + if batcherr != nil { + return errors.Wrap(batcherr, "inserting game player scores") + } + log.Debug().Msgf("game player scores inserted") + return nil } -func ExtractPlayersFromGameWithFormation( - gameWithFormation football.GameWithFormation, -) []model.CreateOrUpdateGamePlayersParams { - var res []model.CreateOrUpdateGamePlayersParams - for _, p := range gameWithFormation.HomeFormation.Bench { - res = append(res, model.CreateOrUpdateGamePlayersParams{ - GameID: gameWithFormation.Id.Value, - PlayerSlug: p.Slug, - TeamSlug: gameWithFormation.HomeTeam.Team.Slug, - Status: "bench", - }) - } - for _, p := range gameWithFormation.HomeFormation.StartingLineup { - for _, q := range p { - res = append(res, model.CreateOrUpdateGamePlayersParams{ - GameID: gameWithFormation.Id.Value, - PlayerSlug: q.Slug, - TeamSlug: gameWithFormation.HomeTeam.Team.Slug, - Status: "starting", - }) - } - } - for _, p := range gameWithFormation.AwayFormation.Bench { - res = append(res, model.CreateOrUpdateGamePlayersParams{ - GameID: gameWithFormation.Id.Value, - PlayerSlug: p.Slug, - TeamSlug: gameWithFormation.AwayTeam.Team.Slug, - Status: "bench", - }) - } - for _, p := range gameWithFormation.AwayFormation.StartingLineup { - for _, q := range p { - res = append(res, model.CreateOrUpdateGamePlayersParams{ - GameID: gameWithFormation.Id.Value, - PlayerSlug: q.Slug, - TeamSlug: gameWithFormation.AwayTeam.Team.Slug, - Status: "starting", - }) - } - } - - return res - -} - func ExtractTeamSlugsFromPlayersAndGames(players []football.Player, games []football.Game) []string { return lo.Uniq(lo.Union( ExtractTeamSlugsFromPlayers(players), diff --git a/sqlc.yml b/sqlc.yml index dda9f27..9323e18 100644 --- a/sqlc.yml +++ b/sqlc.yml @@ -20,6 +20,9 @@ sql: go_type: import: "github.com/shopspring/decimal" type: "Decimal" - + - db_type: "numeric" + go_type: + import: "github.com/shopspring/decimal" + type: "Decimal"