From 90c57c340a15cf8b3f0096bee76d4a6771cc3789 Mon Sep 17 00:00:00 2001 From: Ravi Prasad Date: Wed, 6 Aug 2025 03:15:41 -0500 Subject: [PATCH] player adv controller update --- controllers/player_advanced_controller.go | 166 ++++++++++++------- docs/docs.go | 189 +++++++++++----------- docs/swagger.json | 189 +++++++++++----------- docs/swagger.yaml | 126 +++++++-------- 4 files changed, 360 insertions(+), 310 deletions(-) diff --git a/controllers/player_advanced_controller.go b/controllers/player_advanced_controller.go index 86c14d9..35f2434 100644 --- a/controllers/player_advanced_controller.go +++ b/controllers/player_advanced_controller.go @@ -8,6 +8,56 @@ import ( "github.com/nprasad2077/NBA_Go/services" ) +// --- MODIFICATION START --- +// A DTO is created to control the JSON output for advanced stats. +// It omits fields like ID, ExternalID, CreatedAt, UpdatedAt, and DeletedAt. +type PlayerAdvancedStatDTO struct { + PlayerID string `json:"playerId"` + PlayerName string `json:"playerName"` + Position string `json:"position"` + Age int `json:"age"` + Team string `json:"team"` + Games int `json:"games"` + MinutesPlayed int `json:"minutesPlayed"` + PER float64 `json:"per"` + TSPercent float64 `json:"tsPercent"` + ThreePAR float64 `json:"threePAR"` + FTr float64 `json:"ftr"` + OffensiveRBPercent float64 `json:"offensiveRBPercent"` + DefensiveRBPercent float64 `json:"defensiveRBPercent"` + TotalRBPercent float64 `json:"totalRBPercent"` + AssistPercent float64 `json:"assistPercent"` + StealPercent float64 `json:"stealPercent"` + BlockPercent float64 `json:"blockPercent"` + TurnoverPercent float64 `json:"turnoverPercent"` + UsagePercent float64 `json:"usagePercent"` + OffensiveWS float64 `json:"offensiveWS"` + DefensiveWS float64 `json:"defensiveWS"` + WinShares float64 `json:"winShares"` + WinSharesPer float64 `json:"winSharesPer"` + OffensiveBox float64 `json:"offensiveBox"` + DefensiveBox float64 `json:"defensiveBox"` + Box float64 `json:"box"` + VORP float64 `json:"vorp"` + Season int `json:"season"` + IsPlayoff bool `json:"isPlayoff"` +} + +// AdvancedStatsResponse is the swagger response model for GetAllAdvancedPlayerStats +// It wraps the returned player advanced stats and pagination metadata. +// The Data field is updated to use the DTO. +type AdvancedStatsResponse struct { + Data []PlayerAdvancedStatDTO `json:"data"` + Pagination struct { + Total int64 `json:"total"` + Page int `json:"page"` + PageSize int `json:"pageSize"` + Pages int64 `json:"pages"` + } `json:"pagination"` +} +// --- MODIFICATION END --- + + var advancedSortMap = map[string]string{ "playerId": "player_id", "playerName": "player_name", @@ -39,32 +89,6 @@ var advancedSortMap = map[string]string{ "season": "season", } -// AdvancedStatsResponse is the swagger response model for GetAllAdvancedPlayerStats -// It wraps the returned player advanced stats and pagination metadata. -type AdvancedStatsResponse struct { - Data []models.PlayerAdvancedStat `json:"data"` - Pagination struct { - Total int64 `json:"total"` - Page int `json:"page"` - PageSize int `json:"pageSize"` - Pages int64 `json:"pages"` - } `json:"pagination"` -} - -// FetchPlayerAdvancedStats returns a handler that imports advanced stats for a season -// Note: not exposed in Swagger docs, only internal use. -// func FetchPlayerAdvancedStats(db *gorm.DB) fiber.Handler { -// return func(c *fiber.Ctx) error { -// season := c.QueryInt("season", 2025) -// isPlayoff := c.QueryBool("isPlayoff", false) - -// if err := services.FetchAndStorePlayerAdvancedStats(db, season, isPlayoff); err != nil { -// return c.Status(500).JSON(fiber.Map{"error": err.Error()}) -// } - -// return c.JSON(fiber.Map{"message": "Player stats fetched and saved."}) -// } -// } // ScrapePlayerAdvancedStats godoc // @ignore @@ -114,40 +138,35 @@ func GetAllAdvancedPlayerStats(db *gorm.DB) fiber.Handler { return func(c *fiber.Ctx) error { var stats []models.PlayerAdvancedStat - // --- MODIFICATION FOR FILTERS --- - // Allow both "playerId" and "player_id" - playerId := c.Query("playerId") - if playerId == "" { - playerId = c.Query("player_id") - } + // --- FILTERS --- + playerId := c.Query("playerId") + if playerId == "" { + playerId = c.Query("player_id") + } - // Filters season := c.QueryInt("season", 0) team := c.Query("team") - // playerId := c.Query("playerId") - // Pagination + // --- PAGINATION --- page := c.QueryInt("page", 1) pageSize := c.QueryInt("pageSize", 20) offset := (page - 1) * pageSize - // --- MODIFICATION FOR SORTING --- - // Sorting - sortByParam := c.Query("sortBy", "winShares") // Default to a common field - ascending := c.QueryBool("ascending", false) + // --- SORTING --- + sortByParam := c.Query("sortBy", "winShares") + ascending := c.QueryBool("ascending", false) - // Translate sortBy param to a valid DB column, defaulting if not found. - sortBy, ok := advancedSortMap[sortByParam] - if !ok { - sortBy = "win_shares" // Safe default - } + sortBy, ok := advancedSortMap[sortByParam] + if !ok { + sortBy = "win_shares" // Safe default + } - order := sortBy + " DESC" - if ascending { - order = sortBy + " ASC" - } + order := sortBy + " DESC" + if ascending { + order = sortBy + " ASC" + } - // Build query + // --- QUERY BUILDING --- query := db.Model(&models.PlayerAdvancedStat{}) if season != 0 { @@ -160,29 +179,68 @@ func GetAllAdvancedPlayerStats(db *gorm.DB) fiber.Handler { query = query.Where("player_id = ?", playerId) } - // Only apply the isPlayoff filter if the parameter is actually present in the query string if c.Query("isPlayoff") != "" { isPlayoff := c.QueryBool("isPlayoff", false) query = query.Where("is_playoff = ?", isPlayoff) } - // Count total + // Count total records for pagination var total int64 query.Count(&total) - // Fetch page + // Fetch the data page err := query.Order(order).Limit(pageSize).Offset(offset).Find(&stats).Error if err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } - // Build response - resp := AdvancedStatsResponse{Data: stats} + // --- MODIFICATION START --- + // Transform the database models into DTOs. + advancedStatDTOs := make([]PlayerAdvancedStatDTO, len(stats)) + for i, s := range stats { + advancedStatDTOs[i] = PlayerAdvancedStatDTO{ + PlayerID: s.PlayerID, + PlayerName: s.PlayerName, + Position: s.Position, + Age: s.Age, + Team: s.Team, + Games: s.Games, + MinutesPlayed: s.MinutesPlayed, + PER: s.PER, + TSPercent: s.TSPercent, + ThreePAR: s.ThreePAR, + FTr: s.FTR, // FIX: Changed s.FTr to s.FTR to match the model + OffensiveRBPercent: s.OffensiveRBPercent, + DefensiveRBPercent: s.DefensiveRBPercent, + TotalRBPercent: s.TotalRBPercent, + AssistPercent: s.AssistPercent, + StealPercent: s.StealPercent, + BlockPercent: s.BlockPercent, + TurnoverPercent: s.TurnoverPercent, + UsagePercent: s.UsagePercent, + OffensiveWS: s.OffensiveWS, + DefensiveWS: s.DefensiveWS, + WinShares: s.WinShares, + WinSharesPer: s.WinSharesPer, + OffensiveBox: s.OffensiveBox, + DefensiveBox: s.DefensiveBox, + Box: s.Box, + VORP: s.VORP, + Season: s.Season, + IsPlayoff: s.IsPlayoff, + } + } + + // Build the final response using the new DTOs and response struct. + resp := AdvancedStatsResponse{ + Data: advancedStatDTOs, + } resp.Pagination.Total = total resp.Pagination.Page = page resp.Pagination.PageSize = pageSize resp.Pagination.Pages = (total + int64(pageSize) - 1) / int64(pageSize) return c.JSON(resp) + // --- MODIFICATION END --- } } diff --git a/docs/docs.go b/docs/docs.go index de7f573..1647b95 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -379,7 +379,7 @@ const docTemplate = `{ "data": { "type": "array", "items": { - "$ref": "#/definitions/models.PlayerAdvancedStat" + "$ref": "#/definitions/controllers.PlayerAdvancedStatDTO" } }, "pagination": { @@ -529,6 +529,98 @@ const docTemplate = `{ } } }, + "controllers.PlayerAdvancedStatDTO": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "assistPercent": { + "type": "number" + }, + "blockPercent": { + "type": "number" + }, + "box": { + "type": "number" + }, + "defensiveBox": { + "type": "number" + }, + "defensiveRBPercent": { + "type": "number" + }, + "defensiveWS": { + "type": "number" + }, + "ftr": { + "type": "number" + }, + "games": { + "type": "integer" + }, + "isPlayoff": { + "type": "boolean" + }, + "minutesPlayed": { + "type": "integer" + }, + "offensiveBox": { + "type": "number" + }, + "offensiveRBPercent": { + "type": "number" + }, + "offensiveWS": { + "type": "number" + }, + "per": { + "type": "number" + }, + "playerId": { + "type": "string" + }, + "playerName": { + "type": "string" + }, + "position": { + "type": "string" + }, + "season": { + "type": "integer" + }, + "stealPercent": { + "type": "number" + }, + "team": { + "type": "string" + }, + "threePAR": { + "type": "number" + }, + "totalRBPercent": { + "type": "number" + }, + "tsPercent": { + "type": "number" + }, + "turnoverPercent": { + "type": "number" + }, + "usagePercent": { + "type": "number" + }, + "vorp": { + "type": "number" + }, + "winShares": { + "type": "number" + }, + "winSharesPer": { + "type": "number" + } + } + }, "controllers.PlayerGameAdvStatDTO": { "type": "object", "properties": { @@ -913,101 +1005,6 @@ const docTemplate = `{ } } }, - "models.PlayerAdvancedStat": { - "type": "object", - "properties": { - "age": { - "type": "integer" - }, - "assistPercent": { - "type": "number" - }, - "blockPercent": { - "type": "number" - }, - "box": { - "type": "number" - }, - "defensiveBox": { - "type": "number" - }, - "defensiveRBPercent": { - "type": "number" - }, - "defensiveWS": { - "type": "number" - }, - "ftr": { - "type": "number" - }, - "games": { - "type": "integer" - }, - "id": { - "type": "integer" - }, - "isPlayoff": { - "type": "boolean" - }, - "minutesPlayed": { - "type": "integer" - }, - "offensiveBox": { - "type": "number" - }, - "offensiveRBPercent": { - "type": "number" - }, - "offensiveWS": { - "type": "number" - }, - "per": { - "type": "number" - }, - "playerId": { - "type": "string" - }, - "playerName": { - "type": "string" - }, - "position": { - "type": "string" - }, - "season": { - "type": "integer" - }, - "stealPercent": { - "type": "number" - }, - "team": { - "type": "string" - }, - "threePAR": { - "type": "number" - }, - "totalRBPercent": { - "type": "number" - }, - "tsPercent": { - "type": "number" - }, - "turnoverPercent": { - "type": "number" - }, - "usagePercent": { - "type": "number" - }, - "vorp": { - "type": "number" - }, - "winShares": { - "type": "number" - }, - "winSharesPer": { - "type": "number" - } - } - }, "models.PlayerShotChart": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 9b69ba2..8898d3a 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -376,7 +376,7 @@ "data": { "type": "array", "items": { - "$ref": "#/definitions/models.PlayerAdvancedStat" + "$ref": "#/definitions/controllers.PlayerAdvancedStatDTO" } }, "pagination": { @@ -526,6 +526,98 @@ } } }, + "controllers.PlayerAdvancedStatDTO": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "assistPercent": { + "type": "number" + }, + "blockPercent": { + "type": "number" + }, + "box": { + "type": "number" + }, + "defensiveBox": { + "type": "number" + }, + "defensiveRBPercent": { + "type": "number" + }, + "defensiveWS": { + "type": "number" + }, + "ftr": { + "type": "number" + }, + "games": { + "type": "integer" + }, + "isPlayoff": { + "type": "boolean" + }, + "minutesPlayed": { + "type": "integer" + }, + "offensiveBox": { + "type": "number" + }, + "offensiveRBPercent": { + "type": "number" + }, + "offensiveWS": { + "type": "number" + }, + "per": { + "type": "number" + }, + "playerId": { + "type": "string" + }, + "playerName": { + "type": "string" + }, + "position": { + "type": "string" + }, + "season": { + "type": "integer" + }, + "stealPercent": { + "type": "number" + }, + "team": { + "type": "string" + }, + "threePAR": { + "type": "number" + }, + "totalRBPercent": { + "type": "number" + }, + "tsPercent": { + "type": "number" + }, + "turnoverPercent": { + "type": "number" + }, + "usagePercent": { + "type": "number" + }, + "vorp": { + "type": "number" + }, + "winShares": { + "type": "number" + }, + "winSharesPer": { + "type": "number" + } + } + }, "controllers.PlayerGameAdvStatDTO": { "type": "object", "properties": { @@ -910,101 +1002,6 @@ } } }, - "models.PlayerAdvancedStat": { - "type": "object", - "properties": { - "age": { - "type": "integer" - }, - "assistPercent": { - "type": "number" - }, - "blockPercent": { - "type": "number" - }, - "box": { - "type": "number" - }, - "defensiveBox": { - "type": "number" - }, - "defensiveRBPercent": { - "type": "number" - }, - "defensiveWS": { - "type": "number" - }, - "ftr": { - "type": "number" - }, - "games": { - "type": "integer" - }, - "id": { - "type": "integer" - }, - "isPlayoff": { - "type": "boolean" - }, - "minutesPlayed": { - "type": "integer" - }, - "offensiveBox": { - "type": "number" - }, - "offensiveRBPercent": { - "type": "number" - }, - "offensiveWS": { - "type": "number" - }, - "per": { - "type": "number" - }, - "playerId": { - "type": "string" - }, - "playerName": { - "type": "string" - }, - "position": { - "type": "string" - }, - "season": { - "type": "integer" - }, - "stealPercent": { - "type": "number" - }, - "team": { - "type": "string" - }, - "threePAR": { - "type": "number" - }, - "totalRBPercent": { - "type": "number" - }, - "tsPercent": { - "type": "number" - }, - "turnoverPercent": { - "type": "number" - }, - "usagePercent": { - "type": "number" - }, - "vorp": { - "type": "number" - }, - "winShares": { - "type": "number" - }, - "winSharesPer": { - "type": "number" - } - } - }, "models.PlayerShotChart": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 230cfc3..c06127d 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -4,7 +4,7 @@ definitions: properties: data: items: - $ref: '#/definitions/models.PlayerAdvancedStat' + $ref: '#/definitions/controllers.PlayerAdvancedStatDTO' type: array pagination: properties: @@ -103,6 +103,67 @@ definitions: total: type: integer type: object + controllers.PlayerAdvancedStatDTO: + properties: + age: + type: integer + assistPercent: + type: number + blockPercent: + type: number + box: + type: number + defensiveBox: + type: number + defensiveRBPercent: + type: number + defensiveWS: + type: number + ftr: + type: number + games: + type: integer + isPlayoff: + type: boolean + minutesPlayed: + type: integer + offensiveBox: + type: number + offensiveRBPercent: + type: number + offensiveWS: + type: number + per: + type: number + playerId: + type: string + playerName: + type: string + position: + type: string + season: + type: integer + stealPercent: + type: number + team: + type: string + threePAR: + type: number + totalRBPercent: + type: number + tsPercent: + type: number + turnoverPercent: + type: number + usagePercent: + type: number + vorp: + type: number + winShares: + type: number + winSharesPer: + type: number + type: object controllers.PlayerGameAdvStatDTO: properties: astPercent: @@ -358,69 +419,6 @@ definitions: trb: type: integer type: object - models.PlayerAdvancedStat: - properties: - age: - type: integer - assistPercent: - type: number - blockPercent: - type: number - box: - type: number - defensiveBox: - type: number - defensiveRBPercent: - type: number - defensiveWS: - type: number - ftr: - type: number - games: - type: integer - id: - type: integer - isPlayoff: - type: boolean - minutesPlayed: - type: integer - offensiveBox: - type: number - offensiveRBPercent: - type: number - offensiveWS: - type: number - per: - type: number - playerId: - type: string - playerName: - type: string - position: - type: string - season: - type: integer - stealPercent: - type: number - team: - type: string - threePAR: - type: number - totalRBPercent: - type: number - tsPercent: - type: number - turnoverPercent: - type: number - usagePercent: - type: number - vorp: - type: number - winShares: - type: number - winSharesPer: - type: number - type: object models.PlayerShotChart: properties: date: