adding game player scores init sync

This commit is contained in:
Laurent Le Houerou 2024-03-23 19:46:17 +00:00
parent 12cf5dd6e1
commit 4ce9f62117
7 changed files with 702 additions and 47 deletions

View File

@ -12,6 +12,7 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/shopspring/decimal"
) )
var ( var (
@ -234,6 +235,298 @@ func (b *CreateOrUpdateFixturesBatchResults) Close() error {
return b.br.Close() 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 const createOrUpdateGamePlayers = `-- name: CreateOrUpdateGamePlayers :batchexec
INSERT INTO game_players( INSERT INTO game_players(
game_id, game_id,

View File

@ -0,0 +1,8 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.25.0
// source: game_player_score.sql
package model
import ()

View File

@ -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;

View File

@ -1 +1,52 @@
package sorare_utils 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
}

View File

@ -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
}

View File

@ -27,7 +27,7 @@ func NewUpdateService(s *sorare.Sorare, db *model.Queries) *UpdateService {
func (u *UpdateService) InitSyncDatabase(ctx context.Context) error { func (u *UpdateService) InitSyncDatabase(ctx context.Context) error {
sfixtures, err := u.s.Football.So5.So5Fixtures.Get(ctx, football.So5FixturesParams{ sfixtures, err := u.s.Football.So5.So5Fixtures.Get(ctx, football.So5FixturesParams{
AasmStates: []string{"started"}, // AasmStates: []string{"started"},
}) })
if err != nil { if err != nil {
return err return err
@ -76,6 +76,7 @@ func (u *UpdateService) InitSyncDatabase(ctx context.Context) error {
log.Debug().Msgf("getting players for each game...") log.Debug().Msgf("getting players for each game...")
var gamePlayers []model.CreateOrUpdateGamePlayersParams var gamePlayers []model.CreateOrUpdateGamePlayersParams
playerSlugsByGameMap := make(map[string][]string)
for _, game := range games { for _, game := range games {
gameWithFormation, err := u.s.Football.Game.Get(ctx, graphql.IdParams{Id: gql.ID(game.Id.Value)}) gameWithFormation, err := u.s.Football.Game.Get(ctx, graphql.IdParams{Id: gql.ID(game.Id.Value)})
if err != nil { if err != nil {
@ -83,6 +84,12 @@ func (u *UpdateService) InitSyncDatabase(ctx context.Context) error {
} }
newplayers := ExtractPlayersFromGameWithFormation(gameWithFormation) newplayers := ExtractPlayersFromGameWithFormation(gameWithFormation)
log.Debug().Msgf("\t%s -> %d players", game.String(), len(newplayers)) 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...) gamePlayers = append(gamePlayers, newplayers...)
} }
@ -170,6 +177,24 @@ func (u *UpdateService) InitSyncDatabase(ctx context.Context) error {
} }
log.Debug().Msgf("found %d countries", len(countries)) 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...") log.Debug().Msg("inserting countries into db...")
batchCountries := u.db.CreateOrUpdateCountries( batchCountries := u.db.CreateOrUpdateCountries(
ctx, ctx,
@ -342,54 +367,35 @@ func (u *UpdateService) InitSyncDatabase(ctx context.Context) error {
} }
log.Debug().Msgf("%d game players inserted", len(gamePlayers)) 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 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 { func ExtractTeamSlugsFromPlayersAndGames(players []football.Player, games []football.Game) []string {
return lo.Uniq(lo.Union( return lo.Uniq(lo.Union(
ExtractTeamSlugsFromPlayers(players), ExtractTeamSlugsFromPlayers(players),

View File

@ -20,6 +20,9 @@ sql:
go_type: go_type:
import: "github.com/shopspring/decimal" import: "github.com/shopspring/decimal"
type: "Decimal" type: "Decimal"
- db_type: "numeric"
go_type:
import: "github.com/shopspring/decimal"
type: "Decimal"