games field fixes

This commit is contained in:
Ravi Prasad
2025-06-03 20:28:46 -05:00
parent c32da76997
commit 5a027f6636
14 changed files with 38366 additions and 68 deletions
@@ -54,31 +54,59 @@ func FetchAndStorePlayerAdvancedScrapedStats(db *gorm.DB, season int, isPlayoff
return err
}
// pick the right table selector
var table *goquery.Selection
// 1) Determine parent wrapper and table selector
// - Regular season: <div id="all_advanced"><!-- <table id="advanced">…</table> --></div>
// - Playoffs: <div id="all_advanced_stats"><!-- <table id="advanced_stats">…</table> --></div>
var parentDivSelector, tableSelector string
if isPlayoff {
table = doc.Find("#div_advanced_stats table#advanced_stats")
parentDivSelector = "#all_advanced_stats"
tableSelector = "table#advanced_stats"
} else {
table = doc.Find("#div_advanced table#advanced")
}
if table.Length() == 0 {
return fmt.Errorf("could not find advanced stats table")
parentDivSelector = "#all_advanced"
tableSelector = "table#advanced"
}
// 1. collect the data-stat keys in header order
// Attempt to find the table node directly (it will be inside a comment for regular season)
table := doc.Find(parentDivSelector + " " + tableSelector)
if table.Length() == 0 {
// Look for the commentedout HTML inside parentDivSelector
commentSel := doc.
Find(parentDivSelector).
Contents().
FilterFunction(func(i int, s *goquery.Selection) bool {
return goquery.NodeName(s) == "#comment"
})
if commentSel.Length() == 0 {
return fmt.Errorf("could not find advanced stats table (even inside comment)")
}
// Extract the raw HTML string from the comment, then reparse
commentedHTML := commentSel.Nodes[0].FirstChild.Data
innerDoc, err := goquery.NewDocumentFromReader(strings.NewReader(commentedHTML))
if err != nil {
return fmt.Errorf("failed to parse commented advanced HTML: %w", err)
}
table = innerDoc.Find(tableSelector)
if table.Length() == 0 {
return fmt.Errorf("could not find advanced stats table after uncommenting")
}
}
// 2) Collect the data-stat keys in header order
var headers []string
table.Find("thead tr th").Each(func(i int, th *goquery.Selection) {
if stat, ok := th.Attr("data-stat"); ok && stat != "" {
headers = append(headers, stat)
}
})
// add our appended-player column
// Add our appendedplayer column
headers = append(headers, "player-additional")
// 2. iterate each row
// 3) Iterate each row
table.Find("tbody tr").Each(func(_ int, tr *goquery.Selection) {
if tr.HasClass("thead") {
return // skip repeated headers
return // skip repeated header rows
}
cells := tr.Find("th, td")
data := make(map[string]string, len(headers))
@@ -92,13 +120,14 @@ func FetchAndStorePlayerAdvancedScrapedStats(db *gorm.DB, season int, isPlayoff
}
})
if playerID == "" {
return // not a data row
// not a real data row
return
}
data["player-additional"] = playerID
// 4) pick the correct keys for ExternalID, PlayerName, Team, depending on season vs. playoff
// - playoffs tables still use "rk", "player", "team_id"
// - regularseason advanced uses "ranker", "name_display", "team_name_abbr"
// 4) Determine ExternalID, PlayerName, Team
// • Playoff pages use "rk", "player", "team_id"
// • Regularseason advanced uses "ranker", "name_display", "team_name_abbr"
extID := mustAtoi(data["rk"])
if extID == 0 {
extID = mustAtoi(data["ranker"])
@@ -114,15 +143,20 @@ func FetchAndStorePlayerAdvancedScrapedStats(db *gorm.DB, season int, isPlayoff
teamID = data["team_name_abbr"]
}
// 5) map into your GORM model
// 5) Games column is always "g" in advanced (playoffs or season)
g := mustAtoi(data["games"])
if g == 0 {
g = mustAtoi(data["g"])
}
// 6) Map into your GORM model
stat := models.PlayerAdvancedStat{
ExternalID: extID,
PlayerID: playerID,
PlayerName: playerName,
Position: data["pos"],
Age: mustAtoi(data["age"]),
Games: mustAtoi(data["g"]),
GamesStarted: mustAtoi(data["games_started"]),
Games: g,
MinutesPlayed: mustAtoi(data["mp"]),
PER: mustParseFloat(data["per"]),
TSPercent: mustParseFloat(data["ts_pct"]),
@@ -149,7 +183,7 @@ func FetchAndStorePlayerAdvancedScrapedStats(db *gorm.DB, season int, isPlayoff
IsPlayoff: isPlayoff,
}
// 6) upsert on (player_id, season, team, is_playoff)
// 7) Upsert on (player_id, season, team, is_playoff)
if err := db.Clauses(clause.OnConflict{
Columns: []clause.Column{
{Name: "player_id"},
@@ -158,9 +192,9 @@ func FetchAndStorePlayerAdvancedScrapedStats(db *gorm.DB, season int, isPlayoff
{Name: "is_playoff"},
},
DoUpdates: clause.AssignmentColumns([]string{
"external_id", "player_name", "position", "age", "games", "games_started",
"external_id", "player_name", "position", "age", "games",
"minutes_played",
"per", "ts_percent", "three_par", "ftr",
"per", "ts_percent", "three_par", "ftr", // ← use three_par & ftr
"offensive_rb_percent", "defensive_rb_percent", "total_rb_percent",
"assist_percent", "steal_percent", "block_percent", "turnover_percent",
"usage_percent", "offensive_ws", "defensive_ws", "win_shares",