From 3cfdcdbc4d9cea7d80af04784f25c162d9142456 Mon Sep 17 00:00:00 2001 From: Ravi Prasad Date: Wed, 6 Aug 2025 02:39:14 -0500 Subject: [PATCH 1/3] totals controller update in dev --- Dockerfile | 4 +- controllers/player_total_controller.go | 188 +++++++++++++++++-------- docs/docs.go | 140 ++++++++++++++++-- docs/swagger.json | 140 ++++++++++++++++-- docs/swagger.yaml | 94 ++++++++++++- 5 files changed, 481 insertions(+), 85 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9f0af2e..0700431 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,8 @@ FROM golang:1.24-bullseye AS builder ENV CGO_ENABLED=1 ENV GOOS=linux # Changed from Apple Silicon arm64 config. -ENV GOARCH=amd64 -# ENV GOARCH=arm64 +# ENV GOARCH=amd64 +ENV GOARCH=arm64 WORKDIR /app diff --git a/controllers/player_total_controller.go b/controllers/player_total_controller.go index 53f3f95..3568296 100644 --- a/controllers/player_total_controller.go +++ b/controllers/player_total_controller.go @@ -1,15 +1,3 @@ -// FetchPlayerTotalStats godoc -// @Summary Fetch player total stats from external API -// @Description Imports totals data and stores or updates in DB -// @Tags PlayerTotals -// @Accept json -// @Produce json -// @Param season query int false "Season (e.g. 2000)" -// @Param isPlayoff query bool false "Whether the stats are for playoffs" -// @Success 200 {object} map[string]string -// @Failure 500 {object} map[string]string -// @Router /api/playertotals/fetch [get] - package controllers import ( @@ -19,6 +7,57 @@ import ( "gorm.io/gorm" ) +// --- MODIFICATION START --- +// A DTO is created to control the JSON output. +// It omits fields like ID, ExternalID, CreatedAt, UpdatedAt, and DeletedAt. +type PlayerTotalStatDTO struct { + PlayerID string `json:"playerId"` + PlayerName string `json:"playerName"` + Position string `json:"position"` + Age int `json:"age"` + Games int `json:"games"` + GamesStarted int `json:"gamesStarted"` + MinutesPG float64 `json:"minutesPg"` + FieldGoals int `json:"fieldGoals"` + FieldAttempts int `json:"fieldAttempts"` + FieldPercent float64 `json:"fieldPercent"` + ThreeFG int `json:"threeFg"` + ThreeAttempts int `json:"threeAttempts"` + ThreePercent float64 `json:"threePercent"` + TwoFG int `json:"twoFg"` + TwoAttempts int `json:"twoAttempts"` + TwoPercent float64 `json:"twoPercent"` + EffectFGPercent float64 `json:"effectFgPercent"` + FT int `json:"ft"` + FTAttempts int `json:"ftAttempts"` + FTPercent float64 `json:"ftPercent"` + OffensiveRB int `json:"offensiveRb"` + DefensiveRB int `json:"defensiveRb"` + TotalRB int `json:"totalRb"` + Assists int `json:"assists"` + Steals int `json:"steals"` + Blocks int `json:"blocks"` + Turnovers int `json:"turnovers"` + PersonalFouls int `json:"personalFouls"` + Points int `json:"points"` + Team string `json:"team"` + Season int `json:"season"` + IsPlayoff bool `json:"isPlayoff"` +} + +// A dedicated response struct is created for cleaner, type-safe code. +type PlayerTotalsResponse struct { + Data []PlayerTotalStatDTO `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 totalSortMap = map[string]string{ "playerId": "player_id", "playerName": "player_name", @@ -53,29 +92,15 @@ var totalSortMap = map[string]string{ "season": "season", } -// func FetchPlayerTotalStats(db *gorm.DB) fiber.Handler { -// return func(c *fiber.Ctx) error { -// season := c.QueryInt("season", 2025) -// isPlayoff := c.QueryBool("isPlayoff", false) - -// err := services.FetchAndStorePlayerTotalStats(db, season, isPlayoff) -// if err != nil { -// return c.Status(500).JSON(fiber.Map{"error": err.Error()}) -// } - -// return c.JSON(fiber.Map{"message": "Player total stats fetched and saved."}) -// } -// } - // ScrapePlayerTotalStats godoc // @ignore -// @Summary Scrape player total stats from BR website -// @Tags PlayerTotals -// @Param season query int true "Season (e.g. 2025)" -// @Param isPlayoff query bool false "Whether playoffs?" -// @Success 200 {object} map[string]string -// @Failure 400,500 {object} map[string]string -// //@Router /api/playertotals/scrape [get] +// @Summary Scrape player total stats from BR website +// @Tags PlayerTotals +// @Param season query int true "Season (e.g. 2025)" +// @Param isPlayoff query bool false "Whether playoffs?" +// @Success 200 {object} map[string]string +// @Failure 400,500 {object} map[string]string +// //@Router /api/playertotals/scrape [get] func ScrapePlayerTotalStats(db *gorm.DB) fiber.Handler { return func(c *fiber.Ctx) error { season := c.QueryInt("season", 0) @@ -98,7 +123,7 @@ func ScrapePlayerTotalStats(db *gorm.DB) fiber.Handler { // @Tags PlayerTotals // @x-order 1 // @Group Player Stats -// @Accept json +// @Accept json // @Produce json // @Param season query int false "Season (e.g. 2000)" // @Param team query string false "Team abbreviation (e.g. LAL)" @@ -108,7 +133,7 @@ func ScrapePlayerTotalStats(db *gorm.DB) fiber.Handler { // @Param sortBy query string false "Field to sort by (e.g. points, assists)" // @Param ascending query bool false "Sort ascending (default false)" // @Param isPlayoff query bool false "Whether the stats are for playoffs" -// @Success 200 {object} map[string]interface{} +// @Success 200 {object} controllers.PlayerTotalsResponse // @Failure 500 {object} map[string]string // @Router /api/playertotals [get] func GetPlayerTotalStats(db *gorm.DB) fiber.Handler { @@ -116,32 +141,31 @@ func GetPlayerTotalStats(db *gorm.DB) fiber.Handler { var stats []models.PlayerTotalStat // --- FILTERS --- - playerId := c.Query("playerId") - if playerId == "" { - playerId = c.Query("player_id") - } + playerId := c.Query("playerId") + if playerId == "" { + playerId = c.Query("player_id") + } season := c.QueryInt("season", 0) team := c.Query("team") - // playerId := c.Query("playerId") page := c.QueryInt("page", 1) pageSize := c.QueryInt("pageSize", 20) - // --- MODIFICATION FOR SORTING --- - sortByParam := c.Query("sortBy", "points") - ascending := c.QueryBool("ascending", false) + // --- SORTING --- + sortByParam := c.Query("sortBy", "points") + ascending := c.QueryBool("ascending", false) // Translate sortBy param to a valid DB column, defaulting if not found. - sortBy, ok := totalSortMap[sortByParam] - if !ok { - sortBy = "points" // Safe default - } + sortBy, ok := totalSortMap[sortByParam] + if !ok { + sortBy = "points" // Safe default + } - offset := (page - 1) * pageSize - order := sortBy + " DESC" - if ascending { - order = sortBy + " ASC" - } + offset := (page - 1) * pageSize + order := sortBy + " DESC" + if ascending { + order = sortBy + " ASC" + } query := db.Model(&models.PlayerTotalStat{}) @@ -169,14 +193,56 @@ func GetPlayerTotalStats(db *gorm.DB) fiber.Handler { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } - return c.JSON(fiber.Map{ - "data": stats, - "pagination": fiber.Map{ - "total": total, - "page": page, - "pageSize": pageSize, - "pages": (total + int64(pageSize) - 1) / int64(pageSize), - }, - }) + // --- MODIFICATION START --- + // Transform the database models into DTOs. + playerTotalDTOs := make([]PlayerTotalStatDTO, len(stats)) + for i, s := range stats { + playerTotalDTOs[i] = PlayerTotalStatDTO{ + PlayerID: s.PlayerID, + PlayerName: s.PlayerName, + Position: s.Position, + Age: s.Age, + Games: s.Games, + GamesStarted: s.GamesStarted, + MinutesPG: s.MinutesPG, + FieldGoals: s.FieldGoals, + FieldAttempts: s.FieldAttempts, + FieldPercent: s.FieldPercent, + ThreeFG: s.ThreeFG, + ThreeAttempts: s.ThreeAttempts, + ThreePercent: s.ThreePercent, + TwoFG: s.TwoFG, + TwoAttempts: s.TwoAttempts, + TwoPercent: s.TwoPercent, + EffectFGPercent: s.EffectFGPercent, + FT: s.FT, + FTAttempts: s.FTAttempts, + FTPercent: s.FTPercent, + OffensiveRB: s.OffensiveRB, + DefensiveRB: s.DefensiveRB, + TotalRB: s.TotalRB, + Assists: s.Assists, + Steals: s.Steals, + Blocks: s.Blocks, + Turnovers: s.Turnovers, + PersonalFouls: s.PersonalFouls, + Points: s.Points, + Team: s.Team, + Season: s.Season, + IsPlayoff: s.IsPlayoff, + } + } + + // Build the final response using the new DTOs and response struct. + resp := PlayerTotalsResponse{ + Data: playerTotalDTOs, + } + 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 762962d..de7f573 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -355,8 +355,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/definitions/controllers.PlayerTotalsResponse" } }, "500": { @@ -408,9 +407,6 @@ const docTemplate = `{ "arena": { "type": "string" }, - "boxScoreUrl": { - "type": "string" - }, "date": { "type": "string" }, @@ -418,6 +414,7 @@ const docTemplate = `{ "type": "string" }, "gameId": { + "description": "ID uint ` + "`" + `json:\"id\"` + "`" + `", "type": "string" }, "homePts": { @@ -426,13 +423,11 @@ const docTemplate = `{ "homeTeam": { "type": "string" }, - "id": { - "type": "integer" - }, "isPlayoff": { "type": "boolean" }, "lineScores": { + "description": "BoxScoreURL string ` + "`" + `json:\"boxScoreUrl\"` + "`" + `", "type": "array", "items": { "$ref": "#/definitions/controllers.LineScoreDTO" @@ -678,6 +673,135 @@ const docTemplate = `{ } } }, + "controllers.PlayerTotalStatDTO": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "assists": { + "type": "integer" + }, + "blocks": { + "type": "integer" + }, + "defensiveRb": { + "type": "integer" + }, + "effectFgPercent": { + "type": "number" + }, + "fieldAttempts": { + "type": "integer" + }, + "fieldGoals": { + "type": "integer" + }, + "fieldPercent": { + "type": "number" + }, + "ft": { + "type": "integer" + }, + "ftAttempts": { + "type": "integer" + }, + "ftPercent": { + "type": "number" + }, + "games": { + "type": "integer" + }, + "gamesStarted": { + "type": "integer" + }, + "isPlayoff": { + "type": "boolean" + }, + "minutesPg": { + "type": "number" + }, + "offensiveRb": { + "type": "integer" + }, + "personalFouls": { + "type": "integer" + }, + "playerId": { + "type": "string" + }, + "playerName": { + "type": "string" + }, + "points": { + "type": "integer" + }, + "position": { + "type": "string" + }, + "season": { + "type": "integer" + }, + "steals": { + "type": "integer" + }, + "team": { + "type": "string" + }, + "threeAttempts": { + "type": "integer" + }, + "threeFg": { + "type": "integer" + }, + "threePercent": { + "type": "number" + }, + "totalRb": { + "type": "integer" + }, + "turnovers": { + "type": "integer" + }, + "twoAttempts": { + "type": "integer" + }, + "twoFg": { + "type": "integer" + }, + "twoPercent": { + "type": "number" + } + } + }, + "controllers.PlayerTotalsResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/controllers.PlayerTotalStatDTO" + } + }, + "pagination": { + "type": "object", + "properties": { + "page": { + "type": "integer" + }, + "pageSize": { + "type": "integer" + }, + "pages": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + } + } + }, "controllers.TeamGameAdvStatDTO": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index ff3229a..9b69ba2 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -352,8 +352,7 @@ "200": { "description": "OK", "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/definitions/controllers.PlayerTotalsResponse" } }, "500": { @@ -405,9 +404,6 @@ "arena": { "type": "string" }, - "boxScoreUrl": { - "type": "string" - }, "date": { "type": "string" }, @@ -415,6 +411,7 @@ "type": "string" }, "gameId": { + "description": "ID uint `json:\"id\"`", "type": "string" }, "homePts": { @@ -423,13 +420,11 @@ "homeTeam": { "type": "string" }, - "id": { - "type": "integer" - }, "isPlayoff": { "type": "boolean" }, "lineScores": { + "description": "BoxScoreURL string `json:\"boxScoreUrl\"`", "type": "array", "items": { "$ref": "#/definitions/controllers.LineScoreDTO" @@ -675,6 +670,135 @@ } } }, + "controllers.PlayerTotalStatDTO": { + "type": "object", + "properties": { + "age": { + "type": "integer" + }, + "assists": { + "type": "integer" + }, + "blocks": { + "type": "integer" + }, + "defensiveRb": { + "type": "integer" + }, + "effectFgPercent": { + "type": "number" + }, + "fieldAttempts": { + "type": "integer" + }, + "fieldGoals": { + "type": "integer" + }, + "fieldPercent": { + "type": "number" + }, + "ft": { + "type": "integer" + }, + "ftAttempts": { + "type": "integer" + }, + "ftPercent": { + "type": "number" + }, + "games": { + "type": "integer" + }, + "gamesStarted": { + "type": "integer" + }, + "isPlayoff": { + "type": "boolean" + }, + "minutesPg": { + "type": "number" + }, + "offensiveRb": { + "type": "integer" + }, + "personalFouls": { + "type": "integer" + }, + "playerId": { + "type": "string" + }, + "playerName": { + "type": "string" + }, + "points": { + "type": "integer" + }, + "position": { + "type": "string" + }, + "season": { + "type": "integer" + }, + "steals": { + "type": "integer" + }, + "team": { + "type": "string" + }, + "threeAttempts": { + "type": "integer" + }, + "threeFg": { + "type": "integer" + }, + "threePercent": { + "type": "number" + }, + "totalRb": { + "type": "integer" + }, + "turnovers": { + "type": "integer" + }, + "twoAttempts": { + "type": "integer" + }, + "twoFg": { + "type": "integer" + }, + "twoPercent": { + "type": "number" + } + } + }, + "controllers.PlayerTotalsResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/controllers.PlayerTotalStatDTO" + } + }, + "pagination": { + "type": "object", + "properties": { + "page": { + "type": "integer" + }, + "pageSize": { + "type": "integer" + }, + "pages": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + } + } + }, "controllers.TeamGameAdvStatDTO": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index cd0a7a3..230cfc3 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -22,23 +22,21 @@ definitions: properties: arena: type: string - boxScoreUrl: - type: string date: type: string gameDuration: type: string gameId: + description: ID uint `json:"id"` type: string homePts: type: integer homeTeam: type: string - id: - type: integer isPlayoff: type: boolean lineScores: + description: BoxScoreURL string `json:"boxScoreUrl"` items: $ref: '#/definitions/controllers.LineScoreDTO' type: array @@ -201,6 +199,91 @@ definitions: trb: type: integer type: object + controllers.PlayerTotalStatDTO: + properties: + age: + type: integer + assists: + type: integer + blocks: + type: integer + defensiveRb: + type: integer + effectFgPercent: + type: number + fieldAttempts: + type: integer + fieldGoals: + type: integer + fieldPercent: + type: number + ft: + type: integer + ftAttempts: + type: integer + ftPercent: + type: number + games: + type: integer + gamesStarted: + type: integer + isPlayoff: + type: boolean + minutesPg: + type: number + offensiveRb: + type: integer + personalFouls: + type: integer + playerId: + type: string + playerName: + type: string + points: + type: integer + position: + type: string + season: + type: integer + steals: + type: integer + team: + type: string + threeAttempts: + type: integer + threeFg: + type: integer + threePercent: + type: number + totalRb: + type: integer + turnovers: + type: integer + twoAttempts: + type: integer + twoFg: + type: integer + twoPercent: + type: number + type: object + controllers.PlayerTotalsResponse: + properties: + data: + items: + $ref: '#/definitions/controllers.PlayerTotalStatDTO' + type: array + pagination: + properties: + page: + type: integer + pageSize: + type: integer + pages: + type: integer + total: + type: integer + type: object + type: object controllers.TeamGameAdvStatDTO: properties: astPercent: @@ -614,8 +697,7 @@ paths: "200": description: OK schema: - additionalProperties: true - type: object + $ref: '#/definitions/controllers.PlayerTotalsResponse' "500": description: Internal Server Error schema: From 90c57c340a15cf8b3f0096bee76d4a6771cc3789 Mon Sep 17 00:00:00 2001 From: Ravi Prasad Date: Wed, 6 Aug 2025 03:15:41 -0500 Subject: [PATCH 2/3] 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: From 6d7e37bf7884db2f97d06ee2fcafff7821349551 Mon Sep 17 00:00:00 2001 From: Ravi Prasad Date: Wed, 6 Aug 2025 03:19:07 -0500 Subject: [PATCH 3/3] amd64 --- Dockerfile | 4 ++-- README.md | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0700431..9f0af2e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,8 +4,8 @@ FROM golang:1.24-bullseye AS builder ENV CGO_ENABLED=1 ENV GOOS=linux # Changed from Apple Silicon arm64 config. -# ENV GOARCH=amd64 -ENV GOARCH=arm64 +ENV GOARCH=amd64 +# ENV GOARCH=arm64 WORKDIR /app diff --git a/README.md b/README.md index 30899d3..2652ff9 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,8 @@ swag init -g main.go -o docs go run loadtest.go -n 100 -c 10 -url "http://127.0.0.1:8080/api/playeradvancedstats?page=1&pageSize=20" -log results.log -key "xxx" ``` - ### Local Environment ```bash docker-compose -f docker-compose.local.yml up --build -d -``` \ No newline at end of file +```